diff --git a/.github/workflows/flutter_ci.yaml b/.github/workflows/flutter_ci.yaml index 0eeecab1..fc30dace 100644 --- a/.github/workflows/flutter_ci.yaml +++ b/.github/workflows/flutter_ci.yaml @@ -56,12 +56,18 @@ jobs: envkey_TRIPAPP_API_PORT: ${{ secrets.TRIPAPP_API_PORT }} file_name: .prod.env + - name: Install custom_lint + run: dart pub global activate custom_lint + - name: Install Flutter dependencies run: flutter pub get - name: Run Flutter Analyze run: flutter analyze + - name: Run riverpod_lint + run: custom_lint + # テスト結果を GitHub Actions 上に表示するため log に出力 # カバレッジ結果を Codecov に送信するためカバレッジありで実行 - name: Run Flutter Test diff --git a/analysis_options.yaml b/analysis_options.yaml index 2f4628eb..9141358c 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,6 +1,8 @@ include: package:very_good_analysis/analysis_options.yaml analyzer: + plugins: + - custom_lint exclude: - 'lib/env/env.dart' - 'lib/firebase_options.dart' @@ -8,6 +10,12 @@ analyzer: public_member_api_docs: ignore one_member_abstracts: ignore +custom_lint: + rules: + # Widget Test を書きやすくするため、[TripApp] ウィジェットの build() メソッドで、 + # [ProviderScope] を返している。 + - missing_provider_scope: false + linter: # The lint rules applied to this project can be customized in the # section below to disable rules from the `package:flutter_lints/flutter.yaml` diff --git a/lib/core/debug/logger.dart b/lib/core/debug/logger.dart index 5ba94443..d5ace931 100644 --- a/lib/core/debug/logger.dart +++ b/lib/core/debug/logger.dart @@ -1,11 +1,31 @@ +import 'package:flutter/foundation.dart'; import 'package:roggle/roggle.dart'; -final logger = Roggle( - printer: SinglePrettyPrinter( - loggerName: ' [APP]', - // warning 以上のときはスタックトレースを出力する - stackTraceLevel: Level.warning, - // ログが長くなるので非表示 - printCaller: false, - ), -); +final logger = DebugLogger(); + +class DebugLogger extends Roggle { + DebugLogger() + : super( + printer: SinglePrettyPrinter( + loggerName: ' [APP]', + // warning 以上のときはスタックトレースを出力する + stackTraceLevel: Level.warning, + // ログが長くなるので非表示 + printCaller: false, + ), + ); + + @override + void i(Object? message, [Object? error, StackTrace? stackTrace]) { + if (kDebugMode) { + super.i(message, error, stackTrace); + } + } + + @override + void e(Object? message, [Object? error, StackTrace? stackTrace]) { + if (kDebugMode) { + super.e(message, error, stackTrace); + } + } +} diff --git a/lib/core/http/network_connectivity.dart b/lib/core/http/network_connectivity.dart index 6c3f6d9b..7203e337 100644 --- a/lib/core/http/network_connectivity.dart +++ b/lib/core/http/network_connectivity.dart @@ -1,6 +1,4 @@ import 'package:connectivity_plus/connectivity_plus.dart'; -import 'package:flutter/foundation.dart'; -import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:trip_app_nativeapp/core/debug/logger.dart'; @@ -14,9 +12,10 @@ Future isNetworkConnected(IsNetworkConnectedRef ref) async { } /// ネットワーク接続状態を ref.watch したい場合に使う -final networkConnectivityProvider = StreamProvider((_) { - if (kDebugMode) { - logger.i('NetworkConnectivity: ${Connectivity().onConnectivityChanged}'); - } +@Riverpod(keepAlive: true) +Stream networkConnectivity( + NetworkConnectivityRef ref, +) { + logger.i('NetworkConnectivity Changed'); return Connectivity().onConnectivityChanged; -}); +} diff --git a/lib/core/http/network_connectivity.g.dart b/lib/core/http/network_connectivity.g.dart index f3913909..87a53aa6 100644 --- a/lib/core/http/network_connectivity.g.dart +++ b/lib/core/http/network_connectivity.g.dart @@ -26,4 +26,22 @@ final isNetworkConnectedProvider = AutoDisposeFutureProvider.internal( ); typedef IsNetworkConnectedRef = AutoDisposeFutureProviderRef; +String _$networkConnectivityHash() => + r'ae1c71ff048e6e204d1d13f48b2d4cf8f7909890'; + +/// ネットワーク接続状態を ref.watch したい場合に使う +/// +/// Copied from [networkConnectivity]. +@ProviderFor(networkConnectivity) +final networkConnectivityProvider = StreamProvider.internal( + networkConnectivity, + name: r'networkConnectivityProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$networkConnectivityHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef NetworkConnectivityRef = StreamProviderRef; // ignore_for_file: unnecessary_raw_strings, subtype_of_sealed_class, invalid_use_of_internal_member, do_not_use_environment, prefer_const_constructors, public_member_api_docs, avoid_private_typedef_functions diff --git a/lib/features/auth/controller/auth_controller.dart b/lib/features/auth/controller/auth_controller.dart index 2ed14d8b..111e1b22 100644 --- a/lib/features/auth/controller/auth_controller.dart +++ b/lib/features/auth/controller/auth_controller.dart @@ -10,9 +10,9 @@ import 'package:trip_app_nativeapp/view/widgets/helpers/scaffold_messenger.dart' part 'auth_controller.g.dart'; -final firebaseAuthUserProvider = StreamProvider( - (ref) => ref.watch(firebaseAuthProvider).userChanges(), -); +@Riverpod(keepAlive: true) +Stream firebaseAuthUser(FirebaseAuthUserRef ref) => + ref.watch(firebaseAuthProvider).userChanges(); @riverpod AuthController authController(AuthControllerRef ref) => AuthController(ref); diff --git a/lib/features/auth/controller/auth_controller.g.dart b/lib/features/auth/controller/auth_controller.g.dart index 6477574d..de3b9b16 100644 --- a/lib/features/auth/controller/auth_controller.g.dart +++ b/lib/features/auth/controller/auth_controller.g.dart @@ -8,6 +8,21 @@ part of 'auth_controller.dart'; // RiverpodGenerator // ************************************************************************** +String _$firebaseAuthUserHash() => r'ffb1a892262d80f60d87e9c52c4ad030773b68c0'; + +/// See also [firebaseAuthUser]. +@ProviderFor(firebaseAuthUser) +final firebaseAuthUserProvider = StreamProvider.internal( + firebaseAuthUser, + name: r'firebaseAuthUserProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$firebaseAuthUserHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef FirebaseAuthUserRef = StreamProviderRef; String _$authControllerHash() => r'59574e6e1cdcabbe18cb9e242363875abd753473'; /// See also [authController]. diff --git a/lib/features/user/controller/app_user_controller.dart b/lib/features/user/controller/app_user_controller.dart index f1c64647..ff8bb27b 100644 --- a/lib/features/user/controller/app_user_controller.dart +++ b/lib/features/user/controller/app_user_controller.dart @@ -13,7 +13,7 @@ class AppUserController extends _$AppUserController { @override FutureOr build() async { // AppUser が null 且つネットワーク接続がない状態で、build がコールされると ErrorPage に遷移する - // その場合に、ネットワークに接続した時にアプリを再起動しなくても、ユーザー情報を再取得できるように + // その場合に、ネットワークに再接続した際、アプリを再起動しなくてもユーザー情報を再取得できるように、 // ネットワーク接続状態を watch する ref.watch(networkConnectivityProvider); if (ref.watch(firebaseAuthUserProvider).value == null) { diff --git a/lib/router.dart b/lib/router.dart index b48b4c96..4f5069d7 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -12,6 +12,10 @@ import 'package:trip_app_nativeapp/view/pages/trips/trips_list_page.dart'; part 'router.g.dart'; @riverpod +// なぜかこのルール違反が出ちゃう。パッケージのバグだと思うので、一旦 ignore しておく +// riverpod/packages/riverpod_lint/lib/src/lints/unsupported_provider_value.dart の +// ソースコードを読んだだけだと、この現象の原因は分からなかった。 +// ignore: unsupported_provider_value GoRouter router(RouterRef ref) { final appUserAsync = ref.watch(appUserControllerProvider); return GoRouter( diff --git a/lib/router.g.dart b/lib/router.g.dart index 4d0c954c..a0302376 100644 --- a/lib/router.g.dart +++ b/lib/router.g.dart @@ -8,7 +8,7 @@ part of 'router.dart'; // RiverpodGenerator // ************************************************************************** -String _$routerHash() => r'f2ed8408dd5fb9ee574d55575b9ceef174cecd0f'; +String _$routerHash() => r'00f688253ebb306dd18446a4f538a8bde8bbec91'; /// See also [router]. @ProviderFor(router) diff --git a/pubspec.lock b/pubspec.lock index 1e7650e6..e1d40319 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: "0c80aeab9bc807ab10022cd3b2f4cf2ecdf231949dc1ddd9442406a003f19201" + sha256: "98d1d33ed129b372846e862de23a0fc365745f4d7b5e786ce667fcbbb7ac5c07" url: "https://pub.dev" source: hosted - version: "52.0.0" + version: "55.0.0" _flutterfire_internals: dependency: transitive description: @@ -29,10 +29,10 @@ packages: dependency: transitive description: name: analyzer - sha256: cd8ee83568a77f3ae6b913a36093a1c9b1264e7cb7f834d9ddd2311dade9c1f4 + sha256: "881348aed9b0b425882c97732629a6a31093c8ff20fc4b3b03fb9d3d50a3a126" url: "https://pub.dev" source: hosted - version: "5.4.0" + version: "5.7.1" analyzer_plugin: dependency: transitive description: @@ -242,21 +242,29 @@ packages: source: hosted version: "1.0.5" custom_lint: - dependency: transitive + dependency: "direct dev" description: name: custom_lint - sha256: "324e7026902f34e180d8d27e08a89dbbe3dd8a54e0c3852a0df0db0f4046120b" + sha256: "32648ef4f1dda618d98a22bc958fa79d479895891061e63338bec510b67e821a" + url: "https://pub.dev" + source: hosted + version: "0.3.2" + custom_lint_builder: + dependency: transitive + description: + name: custom_lint_builder + sha256: "5d472a901d07ab5ba0239262c340d29930aa2cfd0c73068aa4d1142a353ffee4" url: "https://pub.dev" source: hosted - version: "0.2.12" + version: "0.3.2" custom_lint_core: dependency: transitive description: name: custom_lint_core - sha256: "34a9447102de5741d6dc464c4824a4173b20b91d5685589ea652b051dda1f461" + sha256: cc20ce83432675abcc109b766aad2e420946eaeecc32ffd34bada389cdaa9a74 url: "https://pub.dev" source: hosted - version: "0.2.12" + version: "0.3.2" dart_jsonwebtoken: dependency: transitive description: @@ -487,10 +495,10 @@ packages: dependency: transitive description: name: flutter_riverpod - sha256: "46a27b7a11dc13738054093076f2dc65692ddcd463979b15092accf5681aea20" + sha256: b3c3a8a9714b7f88dd2a41e1efbc47f76d620b06ab427c62ae7bc82298cd7dbb url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.3.2" flutter_spinkit: dependency: "direct main" description: @@ -633,10 +641,18 @@ packages: dependency: "direct main" description: name: hooks_riverpod - sha256: a596bcb1eaf48eae6da1ce8b9e60ec9538ef7d15725e941c3626f29dfcc01d96 + sha256: "7e673817a7db4d6403a079882485affd3156d79c06209389353d0977d513f905" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.3.2" + hotreloader: + dependency: transitive + description: + name: hotreloader + sha256: "66b78edf340352fcdd2043ab6116e58ec018c5968cab6485cde0f1769d28e0b8" + url: "https://pub.dev" + source: hosted + version: "3.0.5" http: dependency: transitive description: @@ -973,46 +989,46 @@ packages: url: "https://pub.dev" source: hosted version: "3.2.1" - recase: - dependency: transitive - description: - name: recase - sha256: e4eb4ec2dcdee52dcf99cb4ceabaffc631d7424ee55e56f280bc039737f89213 - url: "https://pub.dev" - source: hosted - version: "4.1.0" riverpod: dependency: transitive description: name: riverpod - sha256: "59a48de9c757aa61aa28e9fd625ffb360d43b6b54606f12536622c55be9e8c4b" + sha256: b0fbf7927333c5c318f7e2c22c8b4fd2542ba294de0373e80ecdb34e0dcd8dc4 url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.3.2" riverpod_analyzer_utils: dependency: transitive description: name: riverpod_analyzer_utils - sha256: "4f0d8f5887186f5fad5377a003beabc36f21f48098828e61be11c03578a8f03a" + sha256: "7c2d4de69ba06107c3d7f1b3f43dc3fbdcb2f666b480560af654b4eb89af0d6d" url: "https://pub.dev" source: hosted - version: "0.0.2" + version: "0.2.0" riverpod_annotation: dependency: "direct main" description: name: riverpod_annotation - sha256: "4726dfa853880e64a8a9f7232eb10a69d6d1df5a45d5847fd0b9ecf6807e57b7" + sha256: c0f51b3fc5a0cefcbcddb35a10ad542d6c38919c081a25279045158ac7955cfb url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "2.0.2" riverpod_generator: dependency: "direct dev" description: name: riverpod_generator - sha256: de002980e1b47d8a8275a465e7c955cbe4722ea97cab76d93af90dad920eac05 + sha256: "2c08a6fbbe80d489f1c5208e5358bfdd4d612f1777c47fdb9ff91bb7a2670529" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "2.1.4" + riverpod_lint: + dependency: "direct dev" + description: + name: riverpod_lint + sha256: "11c4e23dfc0e778d66512c92933698bb74419114e62c62c29fb3638b405cc4c4" + url: "https://pub.dev" + source: hosted + version: "1.1.6" roggle: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index eabce05c..8e8e2729 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -30,18 +30,19 @@ dependencies: go_router: ^5.0.5 google_fonts: ^4.0.3 google_sign_in: ^5.4.2 - hooks_riverpod: ^2.1.1 + hooks_riverpod: ^2.3.2 intl: ^0.17.0 lottie: ^2.3.1 mockito: ^5.3.2 mocktail: ^0.3.0 nil: ^1.1.1 - riverpod_annotation: ^1.0.6 + riverpod_annotation: ^2.0.2 roggle: ^0.3.0 shared_preferences: ^2.0.20 dev_dependencies: build_runner: ^2.3.0 + custom_lint: ^0.3.2 flutter_lints: ^2.0.0 flutter_test: sdk: flutter @@ -49,7 +50,8 @@ dev_dependencies: google_sign_in_mocks: ^0.2.1 http_mock_adapter: ^0.3.3 json_serializable: ^6.5.4 - riverpod_generator: ^1.0.6 + riverpod_generator: ^2.1.4 + riverpod_lint: ^1.1.6 very_good_analysis: ^4.0.0+1 flutter: