main.dart 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. import 'package:pb_authenticator/pages/how_it_works.dart';
  2. import 'package:pb_authenticator/pages/transfer_codes.dart';
  3. import 'state/app_state.dart';
  4. import 'package:flutter/material.dart';
  5. import 'package:flutter/services.dart';
  6. import 'package:flutter_localizations/flutter_localizations.dart'
  7. show GlobalMaterialLocalizations, GlobalWidgetsLocalizations;
  8. import 'l10n/app_localizations.dart';
  9. import 'state/file_storage.dart';
  10. import 'package:pb_authenticator_state/state.dart' show Repository;
  11. import 'pages/pages.dart';
  12. import 'package:provider/provider.dart';
  13. import 'config/routes.dart';
  14. import 'pages/lock_screen.dart';
  15. void main() {
  16. var repository = Repository(FileStorage());
  17. runApp(MultiProvider(
  18. providers: [ChangeNotifierProvider(create: (_) => AppState(repository))],
  19. child: MainApp(),
  20. ));
  21. }
  22. class MainApp extends StatefulWidget {
  23. // Locale information
  24. final Iterable<Locale> supportedLocales = [const Locale('en')];
  25. final Iterable<LocalizationsDelegate> localizationsDelegates = [
  26. AppLocalizations.delegate,
  27. GlobalMaterialLocalizations.delegate,
  28. GlobalWidgetsLocalizations.delegate,
  29. ];
  30. static const MethodChannel _platform = MethodChannel('screen_capture');
  31. void _updateScreenCapture(bool prevent) async {
  32. try {
  33. await _platform.invokeMethod(prevent ? 'prevent' : 'allow');
  34. } catch (e) {
  35. // ignore
  36. }
  37. }
  38. MainApp({super.key});
  39. @override
  40. State<MainApp> createState() => _MainAppState();
  41. }
  42. class _MainAppState extends State<MainApp> {
  43. bool _locked = false;
  44. bool _initialCheckDone = false;
  45. @override
  46. void didChangeDependencies() {
  47. super.didChangeDependencies();
  48. _checkInitialAuth();
  49. }
  50. Future<void> _checkInitialAuth() async {
  51. if (_initialCheckDone) return;
  52. final appState = Provider.of<AppState>(context, listen: false);
  53. if (appState.shouldRequireAuth()) {
  54. setState(() { _locked = true; });
  55. final unlocked = await Navigator.of(context).push(
  56. MaterialPageRoute(builder: (_) => const LockScreenPage(), fullscreenDialog: true),
  57. );
  58. if (unlocked == true) {
  59. setState(() { _locked = false; });
  60. }
  61. }
  62. _initialCheckDone = true;
  63. }
  64. @override
  65. Widget build(BuildContext context) {
  66. SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,
  67. overlays: [SystemUiOverlay.top]);
  68. return Consumer<AppState>(
  69. builder: (context, appState, _) {
  70. widget._updateScreenCapture(appState.screenCapturePrevented);
  71. final isPersian = appState.locale.languageCode == 'fa';
  72. return MaterialApp(
  73. onGenerateTitle: (context) => AppLocalizations.of(context)!.appName,
  74. theme: ThemeData(
  75. brightness: Brightness.light,
  76. fontFamily: isPersian ? 'Vazirmatn' : null,
  77. ),
  78. darkTheme: ThemeData(
  79. brightness: Brightness.dark,
  80. fontFamily: isPersian ? 'Vazirmatn' : null,
  81. ),
  82. themeMode: appState.themeMode,
  83. initialRoute: AppRoutes.home,
  84. routes: {
  85. AppRoutes.home: (context) => _AuthGate(child: const AndroidHomePage()),
  86. AppRoutes.edit: (context) => _AuthGate(child: const AndroidEditPage()),
  87. AppRoutes.add: (context) => _AuthGate(child: const AddPage()),
  88. AppRoutes.addScan: (context) => _AuthGate(child: const ScanQRPage()),
  89. AppRoutes.settings: (context) => _AuthGate(child: const SettingsPage()),
  90. AppRoutes.howItWorks: (context) => _AuthGate(child: const HowItWorksPage()),
  91. AppRoutes.transferCodes: (context) => _AuthGate(child: const TransferCodesPage()),
  92. AppRoutes.help: (context) => _AuthGate(child: const HelpPage()),
  93. },
  94. localizationsDelegates: AppLocalizations.localizationsDelegates,
  95. supportedLocales: const [Locale('en'), Locale('fa')],
  96. locale: appState.locale,
  97. builder: (context, child) {
  98. if (_locked) return const SizedBox.shrink();
  99. return Directionality(
  100. textDirection: isPersian ? TextDirection.rtl : TextDirection.ltr,
  101. child: child!,
  102. );
  103. },
  104. );
  105. },
  106. );
  107. }
  108. }
  109. class _AuthGate extends StatefulWidget {
  110. final Widget child;
  111. const _AuthGate({required this.child});
  112. @override
  113. State<_AuthGate> createState() => _AuthGateState();
  114. }
  115. class _AuthGateState extends State<_AuthGate> with WidgetsBindingObserver {
  116. bool _locked = false;
  117. @override
  118. void initState() {
  119. super.initState();
  120. WidgetsBinding.instance.addObserver(this);
  121. _checkAuth();
  122. }
  123. @override
  124. void dispose() {
  125. WidgetsBinding.instance.removeObserver(this);
  126. super.dispose();
  127. }
  128. @override
  129. void didChangeAppLifecycleState(AppLifecycleState state) {
  130. if (state == AppLifecycleState.resumed) {
  131. _checkAuth();
  132. }
  133. }
  134. Future<void> _checkAuth() async {
  135. final appState = Provider.of<AppState>(context, listen: false);
  136. if (appState.shouldRequireAuth()) {
  137. setState(() { _locked = true; });
  138. final unlocked = await Navigator.of(context).push(
  139. MaterialPageRoute(builder: (_) => const LockScreenPage(), fullscreenDialog: true),
  140. );
  141. if (unlocked == true) {
  142. setState(() { _locked = false; });
  143. }
  144. }
  145. }
  146. @override
  147. Widget build(BuildContext context) {
  148. if (_locked) {
  149. return const SizedBox.shrink();
  150. }
  151. return widget.child;
  152. }
  153. }