diff --git a/lib/application_wrapper.dart b/lib/application_wrapper.dart index 1694e1e..ed83604 100644 --- a/lib/application_wrapper.dart +++ b/lib/application_wrapper.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter_native_splash/flutter_native_splash.dart'; import 'package:lightmeter/data/analytics/analytics.dart'; import 'package:lightmeter/data/analytics/api/analytics_firebase.dart'; import 'package:lightmeter/data/caffeine_service.dart'; @@ -18,61 +19,88 @@ import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart'; import 'package:platform/platform.dart'; import 'package:shared_preferences/shared_preferences.dart'; -class ApplicationWrapper extends StatelessWidget { +class ApplicationWrapper extends StatefulWidget { final Environment env; final Widget child; const ApplicationWrapper(this.env, {required this.child, super.key}); + @override + State createState() => _ApplicationWrapperState(); +} + +class _ApplicationWrapperState extends State { + late final remoteConfigService = widget.env.buildType != BuildType.dev + ? const RemoteConfigService(LightmeterAnalytics(api: LightmeterAnalyticsFirebase())) + : const MockRemoteConfigService(); + + late final IAPStorageService iapStorageService; + late final UserPreferencesService userPreferencesService; + late final bool hasLightSensor; + + final filmsStorageService = FilmsStorageService(); + final filmsProviderKey = GlobalKey(); + + late final Future _initFuture; + + @override + void initState() { + super.initState(); + _initFuture = _initialize(); + } + @override Widget build(BuildContext context) { - final remoteConfigService = env.buildType != BuildType.dev - ? const RemoteConfigService(LightmeterAnalytics(api: LightmeterAnalyticsFirebase())) - : const MockRemoteConfigService(); - final filmsStorageService = FilmsStorageService(); - return FutureBuilder( - future: Future.wait([ - SharedPreferences.getInstance(), - const LightSensorService(LocalPlatform()).hasSensor(), - remoteConfigService.activeAndFetchFeatures(), - filmsStorageService.init(), - ]), - builder: (_, snapshot) { - if (snapshot.data != null) { - final iapService = IAPStorageService(snapshot.data![0] as SharedPreferences); - final userPreferencesService = UserPreferencesService(snapshot.data![0] as SharedPreferences); - final hasLightSensor = snapshot.data![1] as bool; - return ServicesProvider( - analytics: const LightmeterAnalytics(api: LightmeterAnalyticsFirebase()), - caffeineService: const CaffeineService(), - environment: env.copyWith(hasLightSensor: hasLightSensor), - hapticsService: const HapticsService(), - lightSensorService: const LightSensorService(LocalPlatform()), - permissionsService: const PermissionsService(), - userPreferencesService: userPreferencesService, - volumeEventsService: const VolumeEventsService(LocalPlatform()), - child: RemoteConfigProvider( - remoteConfigService: remoteConfigService, - child: EquipmentProfileProvider( - storageService: iapService, - child: FilmsProvider( - filmsStorageService: filmsStorageService, + return FilmsProvider( + key: filmsProviderKey, + filmsStorageService: filmsStorageService, + child: FutureBuilder( + future: _initFuture, + builder: (context, snapshot) { + if (snapshot.error != null) { + return Center(child: Text(snapshot.error!.toString())); + } else if (snapshot.connectionState == ConnectionState.done) { + return ServicesProvider( + analytics: const LightmeterAnalytics(api: LightmeterAnalyticsFirebase()), + caffeineService: const CaffeineService(), + environment: widget.env.copyWith(hasLightSensor: hasLightSensor), + hapticsService: const HapticsService(), + lightSensorService: const LightSensorService(LocalPlatform()), + permissionsService: const PermissionsService(), + userPreferencesService: userPreferencesService, + volumeEventsService: const VolumeEventsService(LocalPlatform()), + child: RemoteConfigProvider( + remoteConfigService: remoteConfigService, + child: EquipmentProfileProvider( + storageService: iapStorageService, child: UserPreferencesProvider( hasLightSensor: hasLightSensor, userPreferencesService: userPreferencesService, - child: child, + child: widget.child, ), ), ), - ), - ); - } else if (snapshot.error != null) { - return Center(child: Text(snapshot.error!.toString())); - } + ); + } - // TODO(@vodemn): maybe user splashscreen instead - return const SizedBox(); - }, + return const SizedBox(); + }, + ), ); } + + Future _initialize() async { + await Future.wait([ + SharedPreferences.getInstance(), + const LightSensorService(LocalPlatform()).hasSensor(), + remoteConfigService.activeAndFetchFeatures(), + filmsStorageService.init().then((_) => filmsProviderKey.currentState?.init()), + ]).then((value) { + final sharedPrefs = (value[0] as SharedPreferences?)!; + iapStorageService = IAPStorageService(sharedPrefs); + userPreferencesService = UserPreferencesService(sharedPrefs); + hasLightSensor = value[1] as bool? ?? false; + FlutterNativeSplash.remove(); + }); + } } diff --git a/lib/providers/films_provider.dart b/lib/providers/films_provider.dart index 38cf769..6fa8f33 100644 --- a/lib/providers/films_provider.dart +++ b/lib/providers/films_provider.dart @@ -24,16 +24,10 @@ class FilmsProvider extends StatefulWidget { class FilmsProviderState extends State { final Map> predefinedFilms = {}; final Map> customFilms = {}; - late String _selectedId; + String _selectedId = ''; Film get _selectedFilm => customFilms[_selectedId]?.film ?? predefinedFilms[_selectedId]?.film ?? const FilmStub(); - @override - void initState() { - super.initState(); - _init(); - } - @override Widget build(BuildContext context) { return Films( @@ -44,6 +38,14 @@ class FilmsProviderState extends State { ); } + Future init() async { + _selectedId = widget.filmsStorageService.selectedFilmId; + predefinedFilms.addAll(await widget.filmsStorageService.getPredefinedFilms()); + customFilms.addAll(await widget.filmsStorageService.getCustomFilms()); + _discardSelectedIfNotIncluded(); + if (mounted) setState(() {}); + } + /* Both type of films **/ Future toggleFilm(Film film, bool enabled) async { @@ -89,14 +91,6 @@ class FilmsProviderState extends State { setState(() {}); } - Future _init() async { - _selectedId = widget.filmsStorageService.selectedFilmId; - predefinedFilms.addAll(await widget.filmsStorageService.getPredefinedFilms()); - customFilms.addAll(await widget.filmsStorageService.getCustomFilms()); - _discardSelectedIfNotIncluded(); - if (mounted) setState(() {}); - } - void _discardSelectedIfNotIncluded() { if (_selectedId == const FilmStub().id) { return; diff --git a/lib/runner.dart b/lib/runner.dart index b43d7bf..9e695a0 100644 --- a/lib/runner.dart +++ b/lib/runner.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/widgets.dart'; +import 'package:flutter_native_splash/flutter_native_splash.dart'; import 'package:lightmeter/application.dart'; import 'package:lightmeter/application_wrapper.dart'; import 'package:lightmeter/constants.dart'; @@ -16,7 +17,8 @@ const _errorsLogger = LightmeterAnalytics(api: LightmeterAnalyticsFirebase()); Future runLightmeterApp(Environment env) async { runZonedGuarded( () async { - WidgetsFlutterBinding.ensureInitialized(); + final widgetsBinding = WidgetsFlutterBinding.ensureInitialized(); + FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding); if (env.buildType == BuildType.prod) { await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); } @@ -28,6 +30,7 @@ Future runLightmeterApp(Environment env) async { products: [ IAPProduct( storeId: IAPProductType.paidFeatures.storeId, + status: IAPProductStatus.purchased, price: '0.0\$', ), ], diff --git a/pubspec.yaml b/pubspec.yaml index b82a4c6..ba32852 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -24,6 +24,7 @@ dependencies: flutter_bloc: 8.1.3 flutter_localizations: sdk: flutter + flutter_native_splash: 2.3.5 intl: 0.18.1 intl_utils: 2.8.2 light_sensor: 3.0.0 @@ -48,7 +49,6 @@ dev_dependencies: args: 2.5.0 bloc_test: 9.1.3 build_runner: 2.4.6 - flutter_native_splash: 2.3.5 flutter_test: sdk: flutter golden_toolkit: 0.15.0