import 'dart:convert'; import 'package:advanced_in_app_review/advanced_in_app_review.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_crashlytics/firebase_crashlytics.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:frosty/apis/bttv_api.dart'; import 'package:frosty/apis/ffz_api.dart'; import 'package:frosty/apis/seventv_api.dart'; import 'package:frosty/apis/twitch_api.dart'; import 'package:frosty/firebase_options.dart'; import 'package:frosty/screens/home/home.dart'; import 'package:frosty/screens/onboarding/onboarding_intro.dart'; import 'package:frosty/screens/settings/stores/auth_store.dart'; import 'package:frosty/screens/settings/stores/settings_store.dart'; import 'package:frosty/theme.dart'; import 'package:frosty/utils.dart'; import 'package:http/http.dart'; import 'package:mobx/mobx.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp( options: DefaultFirebaseOptions.currentPlatform, ); // Pass all uncaught "fatal" errors from the framework to Crashlytics FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterFatalError; // Pass all uncaught asynchronous errors that aren't handled by the Flutter framework to Crashlytics PlatformDispatcher.instance.onError = (error, stack) { FirebaseCrashlytics.instance.recordError(error, stack, fatal: true); return true; }; final prefs = await SharedPreferences.getInstance(); final firstRun = prefs.getBool('first_run') ?? true; // Workaround for clearing stored tokens on uninstall. // If first time running app, will clear all tokens in the secure storage. if (firstRun) { debugPrint('Clearing secure storage...'); const storage = FlutterSecureStorage(); await storage.deleteAll(); } await initUtils(); // With the shared preferences instance, obtain the existing user settings if it exists. // If default settings don't exist, use an empty JSON string to use the default values. final userSettings = prefs.getString('settings') ?? '{}'; // Initialize a settings store from the settings JSON string. final settingsStore = SettingsStore.fromJson(jsonDecode(userSettings)); // Create a MobX reaction that will save the settings on disk every time they are changed. autorun((_) => prefs.setString('settings', jsonEncode(settingsStore))); /// Initialize API services with a common client. /// This will prevent every request from creating a new client instance. final client = Client(); final twitchApiService = TwitchApi(client); final bttvApiService = BTTVApi(client); final ffzApiService = FFZApi(client); final sevenTVApiService = SevenTVApi(client); // Create and initialize the authentication store final authStore = AuthStore(twitchApi: twitchApiService); await authStore.init(); runApp( MultiProvider( providers: [ Provider(create: (_) => authStore), Provider(create: (_) => settingsStore), Provider(create: (_) => twitchApiService), Provider(create: (_) => bttvApiService), Provider(create: (_) => ffzApiService), Provider(create: (_) => sevenTVApiService), ], child: MyApp(firstRun: firstRun), ), ); } // Navigator key for sleep timer. Allows navigation popping without context. final navigatorKey = GlobalKey(); class MyApp extends StatefulWidget { final bool firstRun; const MyApp({ super.key, this.firstRun = false, }); @override State createState() => _MyAppState(); } class _MyAppState extends State { @override void initState() { super.initState(); AdvancedInAppReview() .setMinDaysBeforeRemind(7) .setMinDaysAfterInstall(1) .setMinLaunchTimes(5) .setMinSecondsBeforeShowDialog(3) .monitor(); } @override Widget build(BuildContext context) { return Observer( builder: (context) { final settingsStore = context.read(); final themes = FrostyThemes(colorSchemeSeed: Color(settingsStore.accentColor)); return Provider( create: (_) => themes, child: MaterialApp( title: 'Frosty', theme: themes.light, darkTheme: themes.dark, themeMode: settingsStore.themeType == ThemeType.system ? ThemeMode.system : settingsStore.themeType == ThemeType.light ? ThemeMode.light : ThemeMode.dark, home: widget.firstRun ? const OnboardingIntro() : const Home(), navigatorKey: navigatorKey, ), ); }, ); } }