mirror of
https://github.com/chenasraf/pantry-flutter.git
synced 2026-05-17 17:28:03 +00:00
242 lines
7.7 KiB
Dart
242 lines
7.7 KiB
Dart
import 'dart:async';
|
|
import 'dart:io' show Platform;
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
|
import 'package:provider/provider.dart';
|
|
import 'package:wakelock_plus/wakelock_plus.dart';
|
|
|
|
import 'i18n.dart';
|
|
import 'services/auth_service.dart';
|
|
import 'services/background_notification_task.dart';
|
|
import 'services/locale_service.dart';
|
|
import 'services/category_service.dart';
|
|
import 'services/checklist_service.dart';
|
|
import 'services/house_service.dart';
|
|
import 'services/local_notifications_service.dart';
|
|
import 'services/note_service.dart';
|
|
import 'services/photo_service.dart';
|
|
import 'services/prefs_service.dart';
|
|
import 'services/share_intent_service.dart';
|
|
import 'services/theming_service.dart';
|
|
import 'views/home/home_view.dart';
|
|
import 'views/login/login_view.dart';
|
|
import 'views/notifications_intro/notifications_intro_view.dart';
|
|
|
|
final rootNavigatorKey = GlobalKey<NavigatorState>();
|
|
|
|
void main() async {
|
|
WidgetsFlutterBinding.ensureInitialized();
|
|
if (kDebugMode) {
|
|
WakelockPlus.enable();
|
|
}
|
|
await AuthService.instance.loadCredentials();
|
|
await PrefsService.instance.load();
|
|
await LocalNotificationsService.instance.init();
|
|
if (AuthService.instance.isLoggedIn) {
|
|
await Future.wait([
|
|
ThemingService.instance.fetchTheme(),
|
|
HouseService.instance.cache.load(),
|
|
CategoryService.instance.cache.load(),
|
|
ChecklistService.instance.cache.load(),
|
|
PhotoService.instance.cache.load(),
|
|
NoteService.instance.cache.load(),
|
|
]);
|
|
// Kick off the periodic background poll if notifications are enabled.
|
|
if (PrefsService.instance.notificationsEnabled) {
|
|
unawaited(registerBackgroundNotificationPoll());
|
|
}
|
|
}
|
|
LocaleService.instance.apply();
|
|
unawaited(ShareIntentService.instance.init());
|
|
runApp(const PantryApp());
|
|
}
|
|
|
|
class PantryApp extends StatefulWidget {
|
|
const PantryApp({super.key});
|
|
|
|
@override
|
|
State<PantryApp> createState() => PantryAppState();
|
|
}
|
|
|
|
class _EscapePopWrapper extends StatelessWidget {
|
|
final Widget child;
|
|
const _EscapePopWrapper({required this.child});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Shortcuts(
|
|
shortcuts: const <ShortcutActivator, Intent>{
|
|
SingleActivator(LogicalKeyboardKey.escape): _PopRouteIntent(),
|
|
},
|
|
child: Actions(
|
|
actions: <Type, Action<Intent>>{_PopRouteIntent: _PopRouteAction()},
|
|
child: child,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _PopRouteIntent extends Intent {
|
|
const _PopRouteIntent();
|
|
}
|
|
|
|
class _PopRouteAction extends Action<_PopRouteIntent> {
|
|
@override
|
|
Object? invoke(covariant _PopRouteIntent intent) {
|
|
final nav = rootNavigatorKey.currentState;
|
|
if (nav?.canPop() == true) {
|
|
nav!.maybePop();
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
class PantryAppState extends State<PantryApp> {
|
|
bool _isLoggedIn = AuthService.instance.isLoggedIn;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
LocaleService.instance.addListener(_rebuild);
|
|
ThemingService.instance.addListener(_rebuild);
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
LocaleService.instance.removeListener(_rebuild);
|
|
ThemingService.instance.removeListener(_rebuild);
|
|
super.dispose();
|
|
}
|
|
|
|
void _rebuild() {
|
|
if (mounted) setState(() {});
|
|
}
|
|
|
|
Future<void> _onLoginSuccess() async {
|
|
await ThemingService.instance.fetchTheme();
|
|
_isLoggedIn = true;
|
|
final nextRoute = PrefsService.instance.notificationsIntroSeen
|
|
? '/home'
|
|
: '/notifications-intro';
|
|
rootNavigatorKey.currentState?.pushReplacementNamed(nextRoute);
|
|
if (mounted) setState(() {});
|
|
}
|
|
|
|
void _onIntroDone() {
|
|
rootNavigatorKey.currentState?.pushReplacementNamed('/home');
|
|
}
|
|
|
|
Future<void> _onLogout() async {
|
|
await cancelBackgroundNotificationPoll();
|
|
await LocalNotificationsService.instance.cancelAll();
|
|
await AuthService.instance.logout();
|
|
ThemingService.instance.clear();
|
|
await Future.wait([
|
|
PrefsService.instance.clear(),
|
|
HouseService.instance.cache.clear(),
|
|
CategoryService.instance.cache.clear(),
|
|
ChecklistService.instance.cache.clear(),
|
|
PhotoService.instance.cache.clear(),
|
|
NoteService.instance.cache.clear(),
|
|
]);
|
|
_isLoggedIn = false;
|
|
rootNavigatorKey.currentState?.pushReplacementNamed('/login');
|
|
if (mounted) setState(() {});
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final color = ThemingService.instance.effectiveColor;
|
|
final locale = LocaleService.instance.effectiveLocale;
|
|
final isMacOS = !kIsWeb && Platform.isMacOS;
|
|
final isDesktopHost =
|
|
kIsWeb ||
|
|
(!kIsWeb &&
|
|
(Platform.isMacOS || Platform.isWindows || Platform.isLinux));
|
|
final appBarTheme = isMacOS ? const AppBarTheme(toolbarHeight: 66) : null;
|
|
return ChangeNotifierProvider<PrefsService>.value(
|
|
value: PrefsService.instance,
|
|
child: Directionality(
|
|
textDirection: LocaleService.instance.textDirection,
|
|
child: MaterialApp(
|
|
key: ValueKey(locale),
|
|
debugShowCheckedModeBanner: false,
|
|
navigatorKey: rootNavigatorKey,
|
|
locale: locale,
|
|
supportedLocales: supportedLocales,
|
|
localizationsDelegates: const [
|
|
GlobalMaterialLocalizations.delegate,
|
|
GlobalWidgetsLocalizations.delegate,
|
|
GlobalCupertinoLocalizations.delegate,
|
|
],
|
|
title: m.common.appTitle,
|
|
theme: ThemeData(
|
|
colorScheme: ColorScheme.fromSeed(
|
|
seedColor: color,
|
|
).copyWith(primary: color),
|
|
useMaterial3: true,
|
|
appBarTheme: appBarTheme,
|
|
popupMenuTheme: PopupMenuThemeData(
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(20),
|
|
),
|
|
elevation: 8,
|
|
position: PopupMenuPosition.under,
|
|
),
|
|
),
|
|
darkTheme: ThemeData(
|
|
colorScheme: ColorScheme.fromSeed(
|
|
seedColor: color,
|
|
brightness: Brightness.dark,
|
|
).copyWith(primary: color),
|
|
useMaterial3: true,
|
|
appBarTheme: appBarTheme,
|
|
popupMenuTheme: PopupMenuThemeData(
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(20),
|
|
),
|
|
elevation: 8,
|
|
position: PopupMenuPosition.under,
|
|
),
|
|
),
|
|
themeMode: ThemingService.instance.themeMode,
|
|
builder: (context, child) {
|
|
if (child == null) return const SizedBox.shrink();
|
|
if (!isDesktopHost) return child;
|
|
return _EscapePopWrapper(child: child);
|
|
},
|
|
onGenerateInitialRoutes: (initialRoute) => [
|
|
MaterialPageRoute(
|
|
builder: (_) => _isLoggedIn
|
|
? (PrefsService.instance.notificationsIntroSeen
|
|
? HomeView(onLogout: _onLogout)
|
|
: NotificationsIntroView(onDone: _onIntroDone))
|
|
: LoginView(onLoginSuccess: _onLoginSuccess),
|
|
),
|
|
],
|
|
onGenerateRoute: (settings) {
|
|
switch (settings.name) {
|
|
case '/home':
|
|
return MaterialPageRoute(
|
|
builder: (_) => HomeView(onLogout: _onLogout),
|
|
);
|
|
case '/notifications-intro':
|
|
return MaterialPageRoute(
|
|
builder: (_) => NotificationsIntroView(onDone: _onIntroDone),
|
|
);
|
|
case '/login':
|
|
default:
|
|
return MaterialPageRoute(
|
|
builder: (_) => LoginView(onLoginSuccess: _onLoginSuccess),
|
|
);
|
|
}
|
|
},
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|