main.dart 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. import 'dart:developer' as dev;
  2. import 'dart:io';
  3. import 'package:device_info_plus/device_info_plus.dart';
  4. import 'package:firebase_core/firebase_core.dart';
  5. import 'package:firebase_crashlytics/firebase_crashlytics.dart';
  6. import 'package:flutter/foundation.dart';
  7. import 'package:flutter/material.dart';
  8. import 'package:flutter/services.dart';
  9. import 'package:logging/logging.dart';
  10. import 'package:path_provider/path_provider.dart';
  11. import 'package:provider/provider.dart';
  12. import 'package:puzzleweave/app_lifecycle/app_lifecycle.dart';
  13. import 'package:puzzleweave/audio/jc_audio_controller.dart';
  14. import 'package:puzzleweave/firebase/firebase_options.dart';
  15. import 'package:puzzleweave/homepage/home_screen.dart';
  16. import 'package:puzzleweave/l10n/app_localizations.dart';
  17. import 'package:puzzleweave/models/data.dart';
  18. import 'package:puzzleweave/models/items.dart';
  19. import 'package:puzzleweave/persistence/persistence.dart';
  20. import 'package:puzzleweave/play/board_play.dart';
  21. import 'package:puzzleweave/remote_config/remote_config.dart';
  22. import 'package:puzzleweave/settings/settings_controller.dart';
  23. import 'config/config.dart' as cfg;
  24. import 'config/device.dart';
  25. Logger _log = Logger('main.dart');
  26. final RouteObserver<PageRoute> routeObserver = RouteObserver<PageRoute>();
  27. void main() async {
  28. // Subscribe to log messages.
  29. Logger.root.onRecord.listen((record) {
  30. dev.log(
  31. record.message,
  32. time: record.time,
  33. level: record.level.value,
  34. name: record.loggerName,
  35. zone: record.zone,
  36. error: record.error,
  37. stackTrace: record.stackTrace,
  38. );
  39. });
  40. WidgetsFlutterBinding.ensureInitialized();
  41. // 强制竖屏
  42. SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
  43. // 进入全屏沉浸式, 隐藏底部导航以及状态栏
  44. if (Platform.isAndroid) {
  45. SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky);
  46. }
  47. SystemChrome.setSystemUIOverlayStyle(
  48. const SystemUiOverlayStyle(
  49. statusBarColor: Colors.transparent, // <-- SEE HERE
  50. statusBarIconBrightness: Brightness.dark, //<-- For Android SEE HERE (dark icons)
  51. statusBarBrightness: Brightness.light, //<-- For iOS SEE HERE (dark icons)
  52. ),
  53. );
  54. ////////////////////// firebase relate ///////////////////////////////
  55. if (!kIsWeb && (Platform.isAndroid)) {
  56. try {
  57. await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
  58. FlutterError.onError = (errorDetails) {
  59. FirebaseCrashlytics.instance.recordFlutterFatalError(errorDetails);
  60. };
  61. // Pass all uncaught asynchronous errors
  62. // that aren't handled by the Flutter framework to Crashlytics.
  63. PlatformDispatcher.instance.onError = (error, stack) {
  64. if (error.runtimeType == MissingPluginException) {
  65. _log.warning('error=[$error],stack=\n$stack');
  66. } else if (error.toString().contains('LoadAdError') ||
  67. error.toString().contains('Failed host lookup') ||
  68. error.toString().contains('Unable to connect to the server')) {
  69. _log.warning('network error: $error[${error.runtimeType}]');
  70. // FirebaseCrashlytics.instance.log(error.toString());
  71. } else {
  72. FirebaseCrashlytics.instance.recordError(error, stack, fatal: true);
  73. }
  74. return true;
  75. };
  76. } catch (e) {
  77. debugPrint("Firebase couldn't be initialized: $e");
  78. }
  79. }
  80. //本地参数存储初始化
  81. await Persistence().initialize();
  82. // 远程参数初始化
  83. await RemoteConfig().initialize();
  84. // 程序首次运行时间
  85. DateTime firstRunTime = Persistence().firstRunTime;
  86. _log.info("first_run_time: $firstRunTime, now: ${DateTime.now()}");
  87. // 记录程序运行时间
  88. Persistence().lastRunTime = DateTime.now();
  89. Directory baseDir = await getApplicationDocumentsDirectory();
  90. runApp(MyApp(baseDir: baseDir));
  91. }
  92. class MyApp extends StatelessWidget {
  93. final Directory baseDir;
  94. const MyApp({super.key, required this.baseDir});
  95. // This widget is the root of your application.
  96. @override
  97. Widget build(BuildContext context) {
  98. cfg.Config config = cfg.Config(context, baseDir);
  99. return AppLifecycleObserver(
  100. child: MultiProvider(
  101. providers: [
  102. Provider<Data>(lazy: false, create: (context) => Data(persistence: Persistence())..loadDataFromPersistence()),
  103. Provider<SettingsController>(lazy: false, create: (context) => SettingsController(persistence: Persistence())..loadStateFromPersistence()),
  104. // ProxyProvider2<SettingsController, ValueNotifier<AppLifecycleState>, AudioController>(
  105. // lazy: false,
  106. // create: (context) => AudioController()..initialize(),
  107. // update: (context, settings, lifecycleNotifier, audio) {
  108. // if (audio == null) throw ArgumentError.notNull();
  109. // audio.attachSettings(settings);
  110. // audio.attachLifecycleNotifier(lifecycleNotifier);
  111. // return audio;
  112. // },
  113. // dispose: (context, audio) => audio.dispose(),
  114. // ),
  115. ProxyProvider2<SettingsController, ValueNotifier<AppLifecycleState>, JcAudioController>(
  116. lazy: false,
  117. create: (context) => JcAudioController()..initialize(),
  118. update: (context, settings, lifecycleNotifier, audio) {
  119. if (audio == null) throw ArgumentError.notNull();
  120. audio.attachSettings(settings);
  121. audio.attachLifecycleNotifier(lifecycleNotifier);
  122. return audio;
  123. },
  124. dispose: (context, audio) => audio.dispose(),
  125. ),
  126. // Provider<ApplovinAdsController>(create: (context) => ApplovinAdsController(context)),
  127. Provider<cfg.Config>(lazy: false, create: (context) => config),
  128. Provider<Device>(lazy: false, create: (context) => config.device),
  129. ],
  130. child: Prepare(
  131. child: MaterialApp(
  132. key: GlobalKey(),
  133. title: 'PuzzleWeave',
  134. initialRoute: '/',
  135. navigatorObservers: [routeObserver],
  136. routes: {
  137. '/': (context) => const HomeScreen(),
  138. '/play': (context) => BoardPlay(
  139. item: AssetItem(
  140. '0',
  141. 'title',
  142. 2000,
  143. 3000,
  144. 5,
  145. true,
  146. 'assets/images/691585964b99f02d1db82b44.jpeg',
  147. 'assets/images/691585964b99f02d1db82b44.jpeg',
  148. ),
  149. ),
  150. },
  151. theme: ThemeData(
  152. // textTheme: GoogleFonts.nunitoSansTextTheme(Theme.of(context).textTheme),
  153. brightness: Brightness.light,
  154. primaryColor: Colors.green,
  155. primarySwatch: Colors.blue,
  156. ),
  157. ///多语言设置
  158. localizationsDelegates: AppLocalizations.localizationsDelegates,
  159. supportedLocales: AppLocalizations.supportedLocales,
  160. /// 设置默认语言为英语
  161. localeResolutionCallback: (Locale? locale, Iterable<Locale> supportedLocales) {
  162. var result = supportedLocales.where((element) => element.languageCode == locale?.languageCode);
  163. if (result.isNotEmpty) {
  164. Device.locale = locale!;
  165. _log.info('当前语言(支持):${Device.locale!.countryCode}-${Device.locale!.languageCode}');
  166. return locale;
  167. }
  168. Device.locale = const Locale('en');
  169. _log.info('当前语言(默认):${Device.locale!.countryCode}-${Device.locale!.languageCode}');
  170. return Device.locale;
  171. },
  172. ),
  173. ),
  174. ),
  175. );
  176. }
  177. }
  178. class Prepare extends StatefulWidget {
  179. final Widget child;
  180. const Prepare({super.key, required this.child});
  181. @override
  182. State<Prepare> createState() => _PrepareState();
  183. }
  184. class _PrepareState extends State<Prepare> {
  185. @override
  186. void initState() {
  187. super.initState();
  188. loadDeviceInfo();
  189. }
  190. /// 获取android平台信息,用户判断是否低端机
  191. loadDeviceInfo() async {
  192. DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin();
  193. if (Platform.isAndroid) {
  194. try {
  195. context.read<Device>().androidDeviceInfo = await deviceInfoPlugin.androidInfo;
  196. } catch (e) {}
  197. }
  198. }
  199. @override
  200. Widget build(BuildContext context) {
  201. // applovin max 没有类似的api可以获取banner高度,以下代码注释掉
  202. //Update ad banner size
  203. // AdSize.getCurrentOrientationAnchoredAdaptiveBannerAdSize(
  204. // MediaQuery.of(context).size.width.truncate(),
  205. // ).then((value) {
  206. // if (value != null) {
  207. // context.read<Device>().bannerHeight = value.height.toDouble();
  208. // }
  209. // }).catchError((err) {
  210. // //todo
  211. // });
  212. return widget.child;
  213. }
  214. }