From d21e28f7bbd7984595b19e5e7f7ea2a3187f5a7a Mon Sep 17 00:00:00 2001 From: Ryunosuke Muramatsu Date: Fri, 27 Dec 2024 20:00:41 +0900 Subject: [PATCH] feat: use initialization page and providers (#532) * feat: use initialization page and providers * chore: build diff * chore: build diff --- .../lib/initialization_provider.dart | 3 ++ .../lib/initialization_provider.g.dart | 2 +- packages/flutter_app/lib/main_app.dart | 4 +++ .../configurator_provider.dart | 15 ++++++++ .../configurator_provider.g.dart | 34 +++++++++++++++++++ .../package_info_provider.dart | 5 +-- .../package_info_provider.g.dart | 2 +- .../lib/pages/initialization_page.dart | 7 ++-- .../flutter_app/lib/router/app_routes.dart | 17 ---------- .../flutter_app/lib/router/app_routes.g.dart | 25 -------------- .../lib/router/router_provider.dart | 5 --- .../lib/router/router_provider.g.dart | 2 +- ...ync_notifier_provider_page_test.mocks.dart | 3 +- .../connectivity_provider_test.mocks.dart | 3 +- 14 files changed, 71 insertions(+), 56 deletions(-) create mode 100644 packages/flutter_app/lib/package_adaptor/configurator_provider.dart create mode 100644 packages/flutter_app/lib/package_adaptor/configurator_provider.g.dart diff --git a/packages/flutter_app/lib/initialization_provider.dart b/packages/flutter_app/lib/initialization_provider.dart index 1df1f46b..df937b1f 100644 --- a/packages/flutter_app/lib/initialization_provider.dart +++ b/packages/flutter_app/lib/initialization_provider.dart @@ -2,6 +2,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'features/user_device/user_device_provider.dart'; +import 'package_adaptor/configurator_provider.dart'; import 'package_adaptor/package_info_provider.dart'; import 'util/providers/providers.dart'; @@ -21,4 +22,6 @@ Future initialization(Ref ref) async { ref.watch(packageInfoInitializingProvider.future), ref.watch(userDeviceInitializingProvider.future), ]); + final configurator = ref.watch(configuratorProvider); + await configurator.fetchAndActivate(); } diff --git a/packages/flutter_app/lib/initialization_provider.g.dart b/packages/flutter_app/lib/initialization_provider.g.dart index 049715eb..1efb09ed 100644 --- a/packages/flutter_app/lib/initialization_provider.g.dart +++ b/packages/flutter_app/lib/initialization_provider.g.dart @@ -8,7 +8,7 @@ part of 'initialization_provider.dart'; // RiverpodGenerator // ************************************************************************** -String _$initializationHash() => r'0093b1f1b4f96b36d941a6139447f1b966a4ae8c'; +String _$initializationHash() => r'4c856a1b01533aad40e6acc6e3992ac78dd9c054'; /// See also [initialization]. @ProviderFor(initialization) diff --git a/packages/flutter_app/lib/main_app.dart b/packages/flutter_app/lib/main_app.dart index 068fa4b4..406a40e9 100644 --- a/packages/flutter_app/lib/main_app.dart +++ b/packages/flutter_app/lib/main_app.dart @@ -5,6 +5,7 @@ import 'package:themes/themes.dart'; import 'features/theme_selector/theme_selector.dart'; import 'gen/strings.g.dart'; +import 'pages/initialization_page.dart'; import 'router/router.dart'; import 'util/providers/scaffold_messenger_key_provider.dart'; @@ -29,6 +30,9 @@ class MainApp extends ConsumerWidget { ], supportedLocales: AppLocaleUtils.supportedLocales, scaffoldMessengerKey: ref.watch(scaffoldMessengerKeyProvider), + builder: (context, child) { + return InitializationPage(onInitialized: (_) => child!); + }, ); } } diff --git a/packages/flutter_app/lib/package_adaptor/configurator_provider.dart b/packages/flutter_app/lib/package_adaptor/configurator_provider.dart new file mode 100644 index 00000000..6b283d76 --- /dev/null +++ b/packages/flutter_app/lib/package_adaptor/configurator_provider.dart @@ -0,0 +1,15 @@ +import 'package:altfire_configurator/altfire_configurator.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +part 'configurator_provider.g.dart'; + +/// Provides a [Configurator] to retrieve parameters using "RemoteConfig". +/// +/// It is recommended to call [Configurator.fetchAndActivate] before using +/// the parameters (e.g., at app startup) in order to fetch and activate them +/// for use in the app. +@Riverpod(keepAlive: true) +Configurator configurator(Ref ref) { + return Configurator(); +} diff --git a/packages/flutter_app/lib/package_adaptor/configurator_provider.g.dart b/packages/flutter_app/lib/package_adaptor/configurator_provider.g.dart new file mode 100644 index 00000000..364ae212 --- /dev/null +++ b/packages/flutter_app/lib/package_adaptor/configurator_provider.g.dart @@ -0,0 +1,34 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +// ignore_for_file: duplicate_ignore, type=lint, implicit_dynamic_parameter, implicit_dynamic_type, implicit_dynamic_method, strict_raw_type + +part of 'configurator_provider.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$configuratorHash() => r'9e492ffb951b15cbe24c846fb5bdcbb0fe71a690'; + +/// Provides a [Configurator] to retrieve parameters using "RemoteConfig". +/// +/// It is recommended to call [Configurator.fetchAndActivate] before using +/// the parameters (e.g., at app startup) in order to fetch and activate them +/// for use in the app. +/// +/// Copied from [configurator]. +@ProviderFor(configurator) +final configuratorProvider = Provider.internal( + configurator, + name: r'configuratorProvider', + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') ? null : _$configuratorHash, + dependencies: null, + allTransitiveDependencies: null, +); + +@Deprecated('Will be removed in 3.0. Use Ref instead') +// ignore: unused_element +typedef ConfiguratorRef = ProviderRef; +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/packages/flutter_app/lib/package_adaptor/package_info_provider.dart b/packages/flutter_app/lib/package_adaptor/package_info_provider.dart index 08214855..a311eda8 100644 --- a/packages/flutter_app/lib/package_adaptor/package_info_provider.dart +++ b/packages/flutter_app/lib/package_adaptor/package_info_provider.dart @@ -6,8 +6,9 @@ part 'package_info_provider.g.dart'; /// Providers that need to initialize asynchronously only once at startup. @Riverpod(keepAlive: true) -Future packageInfoInitializing(Ref ref) async => - PackageInfo.fromPlatform(); +Future packageInfoInitializing(Ref ref) async { + return PackageInfo.fromPlatform(); +} /// Provide metadata for the application. /// diff --git a/packages/flutter_app/lib/package_adaptor/package_info_provider.g.dart b/packages/flutter_app/lib/package_adaptor/package_info_provider.g.dart index d1426451..c2ed7d69 100644 --- a/packages/flutter_app/lib/package_adaptor/package_info_provider.g.dart +++ b/packages/flutter_app/lib/package_adaptor/package_info_provider.g.dart @@ -9,7 +9,7 @@ part of 'package_info_provider.dart'; // ************************************************************************** String _$packageInfoInitializingHash() => - r'92258b1cb5e798dac5f0ee4dadb2a05b2bc9c6a9'; + r'9e9859b77c365e47475d313f542ee8bf55158c16'; /// Providers that need to initialize asynchronously only once at startup. /// diff --git a/packages/flutter_app/lib/pages/initialization_page.dart b/packages/flutter_app/lib/pages/initialization_page.dart index a40273e4..b2b48cd9 100644 --- a/packages/flutter_app/lib/pages/initialization_page.dart +++ b/packages/flutter_app/lib/pages/initialization_page.dart @@ -4,13 +4,16 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import '../initialization_provider.dart'; +/// The first page. class InitializationPage extends ConsumerWidget { - const InitializationPage({super.key}); + const InitializationPage({super.key, required this.onInitialized}); + + final WidgetBuilder onInitialized; @override Widget build(BuildContext context, WidgetRef ref) { return switch (ref.watch(initializationProvider)) { - AsyncData(isLoading: false) => throw AssertionError(), + AsyncData(isLoading: false) => onInitialized(context), AsyncError(:final error) => _ErrorPage( error, onRetry: () => ref.invalidate(initializationProvider), diff --git a/packages/flutter_app/lib/router/app_routes.dart b/packages/flutter_app/lib/router/app_routes.dart index 4617b065..491bf2e2 100644 --- a/packages/flutter_app/lib/router/app_routes.dart +++ b/packages/flutter_app/lib/router/app_routes.dart @@ -1,28 +1,11 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; -import '../pages/initialization_page.dart'; import '../pages/main/main_page.dart'; import 'branches/branches.dart'; part 'app_routes.g.dart'; -@TypedGoRoute( - name: InitializationRoute.name, - path: InitializationRoute.path, -) -class InitializationRoute extends GoRouteData { - const InitializationRoute(); - - static const String name = '/init'; - static const String path = '/init'; - - @override - Page buildPage(BuildContext context, GoRouterState state) { - return const NoTransitionPage(child: InitializationPage()); - } -} - @TypedStatefulShellRoute( branches: [ TypedStatefulShellBranch( diff --git a/packages/flutter_app/lib/router/app_routes.g.dart b/packages/flutter_app/lib/router/app_routes.g.dart index 82901af0..890d43f4 100644 --- a/packages/flutter_app/lib/router/app_routes.g.dart +++ b/packages/flutter_app/lib/router/app_routes.g.dart @@ -9,34 +9,9 @@ part of 'app_routes.dart'; // ************************************************************************** List get $appRoutes => [ - $initializationRoute, $mainShellRouteData, ]; -RouteBase get $initializationRoute => GoRouteData.$route( - path: '/init', - name: '/init', - factory: $InitializationRouteExtension._fromState, - ); - -extension $InitializationRouteExtension on InitializationRoute { - static InitializationRoute _fromState(GoRouterState state) => - const InitializationRoute(); - - String get location => GoRouteData.$location( - '/init', - ); - - void go(BuildContext context) => context.go(location); - - Future push(BuildContext context) => context.push(location); - - void pushReplacement(BuildContext context) => - context.pushReplacement(location); - - void replace(BuildContext context) => context.replace(location); -} - RouteBase get $mainShellRouteData => StatefulShellRouteData.$route( factory: $MainShellRouteDataExtension._fromState, branches: [ diff --git a/packages/flutter_app/lib/router/router_provider.dart b/packages/flutter_app/lib/router/router_provider.dart index 505d5a65..a41411c2 100644 --- a/packages/flutter_app/lib/router/router_provider.dart +++ b/packages/flutter_app/lib/router/router_provider.dart @@ -5,7 +5,6 @@ import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; -import '../initialization_provider.dart'; import '../package_adaptor/tracker_provider.dart'; import '../pages/not_found_page/error_page.dart'; import '../util/logger.dart'; @@ -16,16 +15,12 @@ part 'router_provider.g.dart'; @Riverpod(keepAlive: true) Raw router(Ref ref) { - final initialization = ref.watch(initializationProvider); final tracker = ref.watch(trackerProvider); late final GoRouter router; router = GoRouter( routes: $appRoutes, redirect: (context, state) { - if (initialization.isLoading || initialization.hasError) { - return InitializationRoute.path; - } if (state.matchedLocation == '/') { return HomeRouteData.path; } diff --git a/packages/flutter_app/lib/router/router_provider.g.dart b/packages/flutter_app/lib/router/router_provider.g.dart index 0b7ddcb1..1b62e33d 100644 --- a/packages/flutter_app/lib/router/router_provider.g.dart +++ b/packages/flutter_app/lib/router/router_provider.g.dart @@ -8,7 +8,7 @@ part of 'router_provider.dart'; // RiverpodGenerator // ************************************************************************** -String _$routerHash() => r'05e67d1c964e44d7ff84c89541d7cee8406a9605'; +String _$routerHash() => r'49290ce66bc128bc0a2d2bd864b390e877df9735'; /// See also [router]. @ProviderFor(router) diff --git a/packages/flutter_app/test/pages/riverpod_example_page/async_notifier_provider_page/async_notifier_provider_page_test.mocks.dart b/packages/flutter_app/test/pages/riverpod_example_page/async_notifier_provider_page/async_notifier_provider_page_test.mocks.dart index 92314c0d..96553b12 100644 --- a/packages/flutter_app/test/pages/riverpod_example_page/async_notifier_provider_page/async_notifier_provider_page_test.mocks.dart +++ b/packages/flutter_app/test/pages/riverpod_example_page/async_notifier_provider_page/async_notifier_provider_page_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.4 from annotations +// Mocks generated by Mockito 5.4.5 from annotations // in flutter_app/test/pages/riverpod_example_page/async_notifier_provider_page/async_notifier_provider_page_test.dart. // Do not manually edit this file. @@ -17,6 +17,7 @@ import 'package:mockito/mockito.dart' as _i1; // ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: must_be_immutable // ignore_for_file: prefer_const_constructors // ignore_for_file: unnecessary_parenthesis // ignore_for_file: camel_case_types diff --git a/packages/flutter_app/test/util/network_connectivity/connectivity_provider_test.mocks.dart b/packages/flutter_app/test/util/network_connectivity/connectivity_provider_test.mocks.dart index bcaf3a47..7ba5e27a 100644 --- a/packages/flutter_app/test/util/network_connectivity/connectivity_provider_test.mocks.dart +++ b/packages/flutter_app/test/util/network_connectivity/connectivity_provider_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.4 from annotations +// Mocks generated by Mockito 5.4.5 from annotations // in flutter_app/test/util/network_connectivity/connectivity_provider_test.dart. // Do not manually edit this file. @@ -18,6 +18,7 @@ import 'package:mockito/mockito.dart' as _i1; // ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: must_be_immutable // ignore_for_file: prefer_const_constructors // ignore_for_file: unnecessary_parenthesis // ignore_for_file: camel_case_types