perf(startup): paint a splash immediately, run init off the first frame
main() awaited all of configureDependencies() (open the encrypted DB, derive the social key, seed/scan the species catalog) before the first runApp, so nothing painted until init finished — startup jank visible as skipped frames. Now main() runs runApp immediately with a Bootstrap widget that shows a native-splash-matching green splash while the heavy work runs, then swaps in TaneApp. On failure it shows a localized error with a retry (configureDependencies is idempotent, so retry is safe). Adds a bootstrap i18n group (en/es/pt/ast) and a widget test for the splash/error frame.
This commit is contained in:
parent
33d8b2a4d7
commit
71413f3801
12 changed files with 289 additions and 53 deletions
|
|
@ -1,60 +1,15 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'app.dart';
|
||||
import 'data/species_repository.dart';
|
||||
import 'data/variety_repository.dart';
|
||||
import 'di/injector.dart';
|
||||
import 'bootstrap.dart';
|
||||
import 'i18n/strings.g.dart';
|
||||
import 'services/auto_backup_service.dart';
|
||||
import 'services/coarse_location.dart';
|
||||
import 'services/inbox_service.dart';
|
||||
import 'services/locale_store.dart';
|
||||
import 'services/message_store.dart';
|
||||
import 'services/offer_outbox.dart';
|
||||
import 'services/onboarding_store.dart';
|
||||
import 'services/profile_cache.dart';
|
||||
import 'services/profile_store.dart';
|
||||
import 'services/social_service.dart';
|
||||
import 'services/social_settings.dart';
|
||||
|
||||
Future<void> main() async {
|
||||
void main() {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
// Start from the device language, then let an explicit in-app choice win —
|
||||
// it's the only reliable way to reach languages the OS picker doesn't offer
|
||||
// (e.g. Asturian). Restored after DI, since the store lives in the keystore.
|
||||
// (e.g. Asturian). The saved choice is restored inside [Bootstrap], after DI.
|
||||
LocaleSettings.useDeviceLocaleSync();
|
||||
await configureDependencies();
|
||||
final savedLocale = await getIt<LocaleStore>().saved();
|
||||
if (savedLocale != null) LocaleSettings.setLocaleSync(savedLocale);
|
||||
final onboarding = getIt<OnboardingStore>();
|
||||
final inbox =
|
||||
getIt.isRegistered<InboxService>() ? getIt<InboxService>() : null;
|
||||
// Start listening for incoming private messages app-wide (foreground), so
|
||||
// they arrive and populate the inbox list even before their chat is opened.
|
||||
if (inbox != null) unawaited(inbox.start());
|
||||
runApp(
|
||||
TranslationProvider(
|
||||
child: TaneApp(
|
||||
repository: getIt<VarietyRepository>(),
|
||||
species: getIt<SpeciesRepository>(),
|
||||
onboarding: onboarding,
|
||||
social: getIt.isRegistered<SocialService>()
|
||||
? getIt<SocialService>()
|
||||
: null,
|
||||
socialSettings: getIt<SocialSettings>(),
|
||||
location: const GeolocatorCoarseLocation(),
|
||||
outbox: getIt<OfferOutbox>(),
|
||||
messageStore: getIt<MessageStore>(),
|
||||
profileStore: getIt<ProfileStore>(),
|
||||
profileCache: getIt<ProfileCache>(),
|
||||
inbox: inbox,
|
||||
showIntro: !await onboarding.introSeen(),
|
||||
autoBackup: getIt.isRegistered<AutoBackupService>()
|
||||
? getIt<AutoBackupService>()
|
||||
: null,
|
||||
),
|
||||
),
|
||||
);
|
||||
// Paint immediately: [Bootstrap] shows a splash and runs the heavy init off
|
||||
// the first frame, so the app window appears at once instead of after DI.
|
||||
runApp(TranslationProvider(child: const Bootstrap()));
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue