Add base skeleton
This commit is contained in:
		
							
								
								
									
										3
									
								
								moneymgr_mobile/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								moneymgr_mobile/.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -45,4 +45,5 @@ app.*.map.json
 | 
				
			|||||||
/android/app/release
 | 
					/android/app/release
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
*.g.dart
 | 
					*.g.dart
 | 
				
			||||||
 | 
					*.freezed.dart
 | 
				
			||||||
@@ -3,7 +3,9 @@ import 'package:flutter_hooks/flutter_hooks.dart';
 | 
				
			|||||||
import 'package:flutter_native_splash/flutter_native_splash.dart';
 | 
					import 'package:flutter_native_splash/flutter_native_splash.dart';
 | 
				
			||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
					import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
				
			||||||
import 'package:moneymgr_mobile/providers/settings.dart';
 | 
					import 'package:moneymgr_mobile/providers/settings.dart';
 | 
				
			||||||
 | 
					import 'package:moneymgr_mobile/services/router/router.dart';
 | 
				
			||||||
import 'package:moneymgr_mobile/services/storage/prefs.dart';
 | 
					import 'package:moneymgr_mobile/services/storage/prefs.dart';
 | 
				
			||||||
 | 
					import 'package:moneymgr_mobile/services/storage/secure_storage.dart';
 | 
				
			||||||
import 'package:moneymgr_mobile/utils/provider_observer.dart';
 | 
					import 'package:moneymgr_mobile/utils/provider_observer.dart';
 | 
				
			||||||
import 'package:moneymgr_mobile/utils/theme_utils.dart';
 | 
					import 'package:moneymgr_mobile/utils/theme_utils.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -16,10 +18,12 @@ Future<void> main() async {
 | 
				
			|||||||
  // app is inserted to the widget tree.
 | 
					  // app is inserted to the widget tree.
 | 
				
			||||||
  FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
 | 
					  FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  runApp(ProviderScope(
 | 
					  runApp(
 | 
				
			||||||
    observers: [AppProviderObserver()],
 | 
					    ProviderScope(
 | 
				
			||||||
    child: const MoneyMgrApp(),
 | 
					      observers: [AppProviderObserver()],
 | 
				
			||||||
  ));
 | 
					      child: const MoneyMgrApp(),
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class MoneyMgrApp extends StatelessWidget {
 | 
					class MoneyMgrApp extends StatelessWidget {
 | 
				
			||||||
@@ -27,21 +31,18 @@ class MoneyMgrApp extends StatelessWidget {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  Widget build(BuildContext context) {
 | 
					  Widget build(BuildContext context) {
 | 
				
			||||||
    return const _EagerInitialization(
 | 
					    return const _EagerInitialization(child: _MainApp());
 | 
				
			||||||
      child: _MainApp(),
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class _EagerInitialization extends ConsumerWidget {
 | 
					class _EagerInitialization extends ConsumerWidget {
 | 
				
			||||||
  const _EagerInitialization({required this.child});
 | 
					  const _EagerInitialization({required this.child});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  final Widget child;
 | 
					  final Widget child;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  Widget build(BuildContext context, WidgetRef ref) {
 | 
					  Widget build(BuildContext context, WidgetRef ref) {
 | 
				
			||||||
    final values = [
 | 
					    final values = [ref.watch(prefsProvider), ref.watch(secureStorageProvider)];
 | 
				
			||||||
      ref.watch(prefsProvider),
 | 
					 | 
				
			||||||
    ];
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (values.every((value) => value.hasValue)) {
 | 
					    if (values.every((value) => value.hasValue)) {
 | 
				
			||||||
      return child;
 | 
					      return child;
 | 
				
			||||||
@@ -58,7 +59,6 @@ class _MainApp extends StatefulHookConsumerWidget {
 | 
				
			|||||||
  ConsumerState<_MainApp> createState() => _MainAppState();
 | 
					  ConsumerState<_MainApp> createState() => _MainAppState();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
class _MainAppState extends ConsumerState<_MainApp> {
 | 
					class _MainAppState extends ConsumerState<_MainApp> {
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  void initState() {
 | 
					  void initState() {
 | 
				
			||||||
@@ -71,15 +71,17 @@ class _MainAppState extends ConsumerState<_MainApp> {
 | 
				
			|||||||
    final router = ref.watch(routerProvider);
 | 
					    final router = ref.watch(routerProvider);
 | 
				
			||||||
    final themeMode = ref.watch(currentThemeModeProvider);
 | 
					    final themeMode = ref.watch(currentThemeModeProvider);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    final (lightTheme, darkTheme) = useMemoized(() => createDualThemeData(
 | 
					    final (lightTheme, darkTheme) = useMemoized(
 | 
				
			||||||
      seedColor: Colors.blue,
 | 
					      () => createDualThemeData(
 | 
				
			||||||
      useMaterial3: true,
 | 
					        seedColor: Colors.blue,
 | 
				
			||||||
      transformer: (data) => data.copyWith(
 | 
					        useMaterial3: true,
 | 
				
			||||||
        inputDecorationTheme: const InputDecorationTheme(
 | 
					        transformer: (data) => data.copyWith(
 | 
				
			||||||
          border: OutlineInputBorder(),
 | 
					          inputDecorationTheme: const InputDecorationTheme(
 | 
				
			||||||
 | 
					            border: OutlineInputBorder(),
 | 
				
			||||||
 | 
					          ),
 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
      ),
 | 
					      ),
 | 
				
			||||||
    ));
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return MaterialApp.router(
 | 
					    return MaterialApp.router(
 | 
				
			||||||
      title: 'MoneyMgr',
 | 
					      title: 'MoneyMgr',
 | 
				
			||||||
@@ -90,4 +92,4 @@ class _MainAppState extends ConsumerState<_MainApp> {
 | 
				
			|||||||
      debugShowCheckedModeBanner: false,
 | 
					      debugShowCheckedModeBanner: false,
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										16
									
								
								moneymgr_mobile/lib/routes/login/login_model.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								moneymgr_mobile/lib/routes/login/login_model.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					import 'package:freezed_annotation/freezed_annotation.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					part 'login_model.freezed.dart';
 | 
				
			||||||
 | 
					part 'login_model.g.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@freezed
 | 
				
			||||||
 | 
					abstract class LoginModel with _$LoginModel {
 | 
				
			||||||
 | 
					  const factory LoginModel({
 | 
				
			||||||
 | 
					    // TODO : replace
 | 
				
			||||||
 | 
					    required String username,
 | 
				
			||||||
 | 
					    required String password,
 | 
				
			||||||
 | 
					  }) = _LoginModel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  factory LoginModel.fromJson(Map<String, dynamic> json) =>
 | 
				
			||||||
 | 
					      _$LoginModelFromJson(json);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										85
									
								
								moneymgr_mobile/lib/routes/login/login_screens.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								moneymgr_mobile/lib/routes/login/login_screens.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,85 @@
 | 
				
			|||||||
 | 
					import 'package:flextras/flextras.dart';
 | 
				
			||||||
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
 | 
					import 'package:flutter_gutter/flutter_gutter.dart';
 | 
				
			||||||
 | 
					import 'package:flutter_hooks/flutter_hooks.dart';
 | 
				
			||||||
 | 
					import 'package:go_router/go_router.dart';
 | 
				
			||||||
 | 
					import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
				
			||||||
 | 
					import 'package:moneymgr_mobile/routes/login/login_model.dart';
 | 
				
			||||||
 | 
					import 'package:moneymgr_mobile/widgets/app_button.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import '../../../services/auth_state.dart';
 | 
				
			||||||
 | 
					import '../../../utils/extensions.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class LoginScreen extends HookConsumerWidget {
 | 
				
			||||||
 | 
					  const LoginScreen({super.key});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  Widget build(BuildContext context, WidgetRef ref) {
 | 
				
			||||||
 | 
					    final isPasswordVisible = useState(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    final usernameController = useTextEditingController();
 | 
				
			||||||
 | 
					    final passwordController = useTextEditingController();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void onSettingsPressed() => context.push('/settings');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Future<void> onLoginPressed() async {
 | 
				
			||||||
 | 
					      try {
 | 
				
			||||||
 | 
					        await ref.read(currentAuthStateProvider.notifier).login(LoginModel(
 | 
				
			||||||
 | 
					          username: usernameController.text,
 | 
				
			||||||
 | 
					          password: passwordController.text,
 | 
				
			||||||
 | 
					        ));
 | 
				
			||||||
 | 
					      } on Exception catch (e) {
 | 
				
			||||||
 | 
					        if (!context.mounted) return;
 | 
				
			||||||
 | 
					        context.showTextSnackBar(e.toString());
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return Scaffold(
 | 
				
			||||||
 | 
					      appBar: AppBar(
 | 
				
			||||||
 | 
					        title: const Text('Login'),
 | 
				
			||||||
 | 
					        actions: [
 | 
				
			||||||
 | 
					          IconButton(
 | 
				
			||||||
 | 
					            onPressed: onSettingsPressed,
 | 
				
			||||||
 | 
					            icon: const Icon(Icons.settings),
 | 
				
			||||||
 | 
					          ),
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					      body: SeparatedColumn(
 | 
				
			||||||
 | 
					        padding: EdgeInsets.all(context.gutter),
 | 
				
			||||||
 | 
					        separatorBuilder: () => const Gutter(),
 | 
				
			||||||
 | 
					        mainAxisAlignment: MainAxisAlignment.center,
 | 
				
			||||||
 | 
					        crossAxisAlignment: CrossAxisAlignment.stretch,
 | 
				
			||||||
 | 
					        children: [
 | 
				
			||||||
 | 
					          TextField(
 | 
				
			||||||
 | 
					            controller: usernameController,
 | 
				
			||||||
 | 
					            decoration: const InputDecoration(labelText: 'Username'),
 | 
				
			||||||
 | 
					            textInputAction: TextInputAction.next,
 | 
				
			||||||
 | 
					          ),
 | 
				
			||||||
 | 
					          TextField(
 | 
				
			||||||
 | 
					            controller: passwordController,
 | 
				
			||||||
 | 
					            decoration: InputDecoration(
 | 
				
			||||||
 | 
					              labelText: 'Password',
 | 
				
			||||||
 | 
					              suffixIcon: IconButton(
 | 
				
			||||||
 | 
					                icon: Icon(
 | 
				
			||||||
 | 
					                  isPasswordVisible.value
 | 
				
			||||||
 | 
					                      ? Icons.visibility_off
 | 
				
			||||||
 | 
					                      : Icons.visibility,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                onPressed: () =>
 | 
				
			||||||
 | 
					                isPasswordVisible.value = !isPasswordVisible.value,
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            obscureText: !isPasswordVisible.value,
 | 
				
			||||||
 | 
					            keyboardType: TextInputType.visiblePassword,
 | 
				
			||||||
 | 
					            textInputAction: TextInputAction.done,
 | 
				
			||||||
 | 
					          ),
 | 
				
			||||||
 | 
					          const SizedBox.shrink(),
 | 
				
			||||||
 | 
					          AppButton(
 | 
				
			||||||
 | 
					            onPressed: onLoginPressed,
 | 
				
			||||||
 | 
					            label: 'Login',
 | 
				
			||||||
 | 
					          ),
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										75
									
								
								moneymgr_mobile/lib/services/auth_state.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								moneymgr_mobile/lib/services/auth_state.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,75 @@
 | 
				
			|||||||
 | 
					import 'package:moneymgr_mobile/routes/login/login_model.dart';
 | 
				
			||||||
 | 
					import 'package:moneymgr_mobile/services/router/routes_list.dart';
 | 
				
			||||||
 | 
					import 'package:moneymgr_mobile/services/storage/secure_storage.dart';
 | 
				
			||||||
 | 
					import 'package:riverpod_annotation/riverpod_annotation.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					part 'auth_state.g.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// The current authentication state of the app.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// This notifier is responsible for saving/removing the token and profile info
 | 
				
			||||||
 | 
					/// to the storage through the [login] and [logout] methods.
 | 
				
			||||||
 | 
					@riverpod
 | 
				
			||||||
 | 
					class CurrentAuthState extends _$CurrentAuthState {
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  AuthState build() {
 | 
				
			||||||
 | 
					    final secureStorage = ref.watch(secureStorageProvider).requireValue;
 | 
				
			||||||
 | 
					    final token = secureStorage.get('token');
 | 
				
			||||||
 | 
					    return token != null ? AuthState.authenticated : AuthState.unauthenticated;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Attempts to log in with [data] and saves the token and profile info to storage.
 | 
				
			||||||
 | 
					  /// Will invalidate the state if success.
 | 
				
			||||||
 | 
					  Future<void> login(LoginModel data) async {
 | 
				
			||||||
 | 
					    // TODO : perform login
 | 
				
			||||||
 | 
					    /*final secureStorage = ref.read(secureStorageProvider).requireValue;
 | 
				
			||||||
 | 
					    final token = await ref.read(apiServiceProvider).login(data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Save the new [token] and [profile] to secure storage.
 | 
				
			||||||
 | 
					    secureStorage.set('token', token);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ref
 | 
				
			||||||
 | 
					    // Invalidate the state so the auth state will be updated to authenticated.
 | 
				
			||||||
 | 
					      ..invalidateSelf()
 | 
				
			||||||
 | 
					    // Invalidate the token provider so the API service will use the new token.
 | 
				
			||||||
 | 
					      ..invalidate(tokenProvider);*/
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Logs out, deletes the saved token and profile info from storage, and invalidates
 | 
				
			||||||
 | 
					  /// the state.
 | 
				
			||||||
 | 
					  void logout() {
 | 
				
			||||||
 | 
					    // TODO : implement logic
 | 
				
			||||||
 | 
					    /*final secureStorage = ref.read(secureStorageProvider).requireValue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Delete the current [token] and [profile] from secure storage.
 | 
				
			||||||
 | 
					    secureStorage.remove('token');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ref
 | 
				
			||||||
 | 
					    // Invalidate the state so the auth state will be updated to unauthenticated.
 | 
				
			||||||
 | 
					      ..invalidateSelf()
 | 
				
			||||||
 | 
					    // Invalidate the token provider so the API service will no longer use the
 | 
				
			||||||
 | 
					    // previous token.
 | 
				
			||||||
 | 
					      ..invalidate(tokenProvider);*/
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// The possible authentication states of the app.
 | 
				
			||||||
 | 
					enum AuthState {
 | 
				
			||||||
 | 
					  unknown(redirectPath: homePath, allowedPaths: [homePath]),
 | 
				
			||||||
 | 
					  unauthenticated(
 | 
				
			||||||
 | 
					    redirectPath: authPath,
 | 
				
			||||||
 | 
					    allowedPaths: [authPath, settingsPath],
 | 
				
			||||||
 | 
					  ),
 | 
				
			||||||
 | 
					  authenticated(redirectPath: homePath, allowedPaths: null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const AuthState({required this.redirectPath, required this.allowedPaths});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// The target path to redirect when the current route is not allowed in this
 | 
				
			||||||
 | 
					  /// auth state.
 | 
				
			||||||
 | 
					  final String redirectPath;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// List of paths allowed when the app is in this auth state. May be set to null if there is no
 | 
				
			||||||
 | 
					  /// restriction applicable
 | 
				
			||||||
 | 
					  final List<String>? allowedPaths;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										107
									
								
								moneymgr_mobile/lib/services/router/router.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								moneymgr_mobile/lib/services/router/router.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,107 @@
 | 
				
			|||||||
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
 | 
					import 'package:go_router/go_router.dart';
 | 
				
			||||||
 | 
					import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
				
			||||||
 | 
					import 'package:moneymgr_mobile/routes/login/login_screens.dart';
 | 
				
			||||||
 | 
					import 'package:moneymgr_mobile/services/auth_state.dart';
 | 
				
			||||||
 | 
					import 'package:moneymgr_mobile/services/router/routes_list.dart';
 | 
				
			||||||
 | 
					import 'package:moneymgr_mobile/widgets/scaffold_with_navigation.dart';
 | 
				
			||||||
 | 
					import 'package:riverpod_annotation/riverpod_annotation.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					part 'router.g.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// The router config for the app.
 | 
				
			||||||
 | 
					@riverpod
 | 
				
			||||||
 | 
					GoRouter router(Ref ref) {
 | 
				
			||||||
 | 
					  // Local notifier for the current auth state. The purpose of this notifier is
 | 
				
			||||||
 | 
					  // to provide a [Listenable] to the [GoRouter] exposed by this provider.
 | 
				
			||||||
 | 
					  final authStateNotifier = ValueNotifier(AuthState.unknown);
 | 
				
			||||||
 | 
					  ref
 | 
				
			||||||
 | 
					    ..onDispose(authStateNotifier.dispose)
 | 
				
			||||||
 | 
					    ..listen(currentAuthStateProvider, (_, value) {
 | 
				
			||||||
 | 
					      authStateNotifier.value = value;
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // This is the only place you need to define your navigation items. The items
 | 
				
			||||||
 | 
					  // will be propagated automatically to the router and the navigation bar/rail
 | 
				
			||||||
 | 
					  // of the scaffold.
 | 
				
			||||||
 | 
					  //
 | 
				
			||||||
 | 
					  // To configure the authentication state needed to access a particular item,
 | 
				
			||||||
 | 
					  // see [AuthState] enum.
 | 
				
			||||||
 | 
					  final navigationItems = [
 | 
				
			||||||
 | 
					    NavigationItem(
 | 
				
			||||||
 | 
					      path: '/products',
 | 
				
			||||||
 | 
					      body: (_) => const Text("product screen"),
 | 
				
			||||||
 | 
					      icon: Icons.widgets_outlined,
 | 
				
			||||||
 | 
					      selectedIcon: Icons.widgets,
 | 
				
			||||||
 | 
					      label: 'Products',
 | 
				
			||||||
 | 
					      routes: [
 | 
				
			||||||
 | 
					        GoRoute(
 | 
				
			||||||
 | 
					          path: ':id',
 | 
				
			||||||
 | 
					          builder: (_, state) {
 | 
				
			||||||
 | 
					            final id = int.parse(state.pathParameters['id']!);
 | 
				
			||||||
 | 
					            return Text("product screen $id");
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					    NavigationItem(
 | 
				
			||||||
 | 
					      path: '/profile',
 | 
				
			||||||
 | 
					      body: (_) => const Text("Profile"),
 | 
				
			||||||
 | 
					      icon: Icons.person_outline,
 | 
				
			||||||
 | 
					      selectedIcon: Icons.person,
 | 
				
			||||||
 | 
					      label: 'Profile',
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					  ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  final router = GoRouter(
 | 
				
			||||||
 | 
					    debugLogDiagnostics: true,
 | 
				
			||||||
 | 
					    initialLocation: navigationItems.first.path,
 | 
				
			||||||
 | 
					    routes: [
 | 
				
			||||||
 | 
					      GoRoute(path: homePath, builder: (_, __) => const Scaffold()),
 | 
				
			||||||
 | 
					      GoRoute(path: authPath, builder: (_, __) => const LoginScreen()),
 | 
				
			||||||
 | 
					      GoRoute(
 | 
				
			||||||
 | 
					        path: settingsPath,
 | 
				
			||||||
 | 
					        builder: (_, __) => const Text("settings screen"),
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // Configuration for the bottom navigation bar routes. The routes themselves
 | 
				
			||||||
 | 
					      // should be defined in [navigationItems]. Modification to this [ShellRoute]
 | 
				
			||||||
 | 
					      // config is rarely needed.
 | 
				
			||||||
 | 
					      ShellRoute(
 | 
				
			||||||
 | 
					        builder: (_, __, child) => child,
 | 
				
			||||||
 | 
					        routes: [
 | 
				
			||||||
 | 
					          for (final (index, item) in navigationItems.indexed)
 | 
				
			||||||
 | 
					            GoRoute(
 | 
				
			||||||
 | 
					              path: item.path,
 | 
				
			||||||
 | 
					              pageBuilder: (context, _) => NoTransitionPage(
 | 
				
			||||||
 | 
					                child: ScaffoldWithNavigation(
 | 
				
			||||||
 | 
					                  selectedIndex: index,
 | 
				
			||||||
 | 
					                  navigationItems: navigationItems,
 | 
				
			||||||
 | 
					                  child: item.body(context),
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					              routes: item.routes,
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    refreshListenable: authStateNotifier,
 | 
				
			||||||
 | 
					    redirect: (_, state) {
 | 
				
			||||||
 | 
					      // Get the current auth state.
 | 
				
			||||||
 | 
					      final authState = ref.read(currentAuthStateProvider);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // Check if the current path is allowed for the current auth state. If not,
 | 
				
			||||||
 | 
					      // redirect to the redirect target of the current auth state.
 | 
				
			||||||
 | 
					      if (authState.allowedPaths?.contains(state.fullPath) == false) {
 | 
				
			||||||
 | 
					        return authState.redirectPath;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // If the current path is allowed for the current auth state, don't redirect.
 | 
				
			||||||
 | 
					      return null;
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					  ref.onDispose(router.dispose);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return router;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										7
									
								
								moneymgr_mobile/lib/services/router/routes_list.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								moneymgr_mobile/lib/services/router/routes_list.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					const homePath = "/";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Authentication path
 | 
				
			||||||
 | 
					const authPath = "/login";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Settings path
 | 
				
			||||||
 | 
					const settingsPath = "/settings";
 | 
				
			||||||
							
								
								
									
										42
									
								
								moneymgr_mobile/lib/services/storage/secure_storage.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								moneymgr_mobile/lib/services/storage/secure_storage.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
				
			|||||||
 | 
					import 'package:flutter_secure_storage/flutter_secure_storage.dart';
 | 
				
			||||||
 | 
					import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
				
			||||||
 | 
					import 'package:riverpod_annotation/riverpod_annotation.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					part 'secure_storage.g.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@riverpod
 | 
				
			||||||
 | 
					Future<SecureStorage> secureStorage(Ref ref) =>
 | 
				
			||||||
 | 
					    SecureStorage.getInstance(keys: {'token'});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SecureStorage {
 | 
				
			||||||
 | 
					  SecureStorage._(this._flutterSecureStorage, this._cache);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  late final FlutterSecureStorage _flutterSecureStorage;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  late final Map<String, String> _cache;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static Future<SecureStorage> getInstance({required Set<String> keys}) async {
 | 
				
			||||||
 | 
					    const flutterSecureStorage = FlutterSecureStorage();
 | 
				
			||||||
 | 
					    final cache = <String, String>{};
 | 
				
			||||||
 | 
					    await keys
 | 
				
			||||||
 | 
					        .map((key) => flutterSecureStorage.read(key: key).then((value) {
 | 
				
			||||||
 | 
					      if (value != null) {
 | 
				
			||||||
 | 
					        cache[key] = value;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }))
 | 
				
			||||||
 | 
					        .wait;
 | 
				
			||||||
 | 
					    return SecureStorage._(flutterSecureStorage, cache);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  String? get(String key) => _cache[key];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Future<void> set(String key, String value) {
 | 
				
			||||||
 | 
					    _cache[key] = value;
 | 
				
			||||||
 | 
					    return _flutterSecureStorage.write(key: key, value: value);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Future<void> remove(String key) {
 | 
				
			||||||
 | 
					    _cache.remove(key);
 | 
				
			||||||
 | 
					    return _flutterSecureStorage.delete(key: key);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										38
									
								
								moneymgr_mobile/lib/utils/extensions.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								moneymgr_mobile/lib/utils/extensions.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,38 @@
 | 
				
			|||||||
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extension BuildContextX on BuildContext {
 | 
				
			||||||
 | 
					  /// A convenient way to access [ThemeData.colorScheme] of the current context.
 | 
				
			||||||
 | 
					  ///
 | 
				
			||||||
 | 
					  /// This also prevents confusion with a bunch of other properties of [ThemeData]
 | 
				
			||||||
 | 
					  /// that are less commonly used.
 | 
				
			||||||
 | 
					  ColorScheme get colorScheme => Theme.of(this).colorScheme;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// A convenient way to access [ThemeData.textTheme] of the current context.
 | 
				
			||||||
 | 
					  ///
 | 
				
			||||||
 | 
					  /// This also prevents confusion with a bunch of other properties of [ThemeData]
 | 
				
			||||||
 | 
					  /// that are less commonly used.
 | 
				
			||||||
 | 
					  TextTheme get textTheme => Theme.of(this).textTheme;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Shows a floating snack bar with text as its content.
 | 
				
			||||||
 | 
					  ScaffoldFeatureController<SnackBar, SnackBarClosedReason> showTextSnackBar(
 | 
				
			||||||
 | 
					      String text,
 | 
				
			||||||
 | 
					      ) =>
 | 
				
			||||||
 | 
					      ScaffoldMessenger.of(this).showSnackBar(SnackBar(
 | 
				
			||||||
 | 
					        behavior: SnackBarBehavior.floating,
 | 
				
			||||||
 | 
					        content: Text(text),
 | 
				
			||||||
 | 
					      ));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void showAppLicensePage() => showLicensePage(
 | 
				
			||||||
 | 
					    context: this,
 | 
				
			||||||
 | 
					    useRootNavigator: true,
 | 
				
			||||||
 | 
					    applicationName: 'DummyMart',
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extension ThemeModeX on ThemeMode {
 | 
				
			||||||
 | 
					  String get label => switch (this) {
 | 
				
			||||||
 | 
					    ThemeMode.system => 'System',
 | 
				
			||||||
 | 
					    ThemeMode.light => 'Light',
 | 
				
			||||||
 | 
					    ThemeMode.dark => 'Dark',
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										19
									
								
								moneymgr_mobile/lib/utils/hooks.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								moneymgr_mobile/lib/utils/hooks.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
 | 
					import 'package:flutter_hooks/flutter_hooks.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef AsyncTask = ({
 | 
				
			||||||
 | 
					ValueNotifier<Future<void>?> pending,
 | 
				
			||||||
 | 
					AsyncSnapshot<void> snapshot,
 | 
				
			||||||
 | 
					bool hasError,
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Creates a hook that provides a [snapshot] of the current asynchronous task passed
 | 
				
			||||||
 | 
					/// to [pending] and a [hasError] value.
 | 
				
			||||||
 | 
					AsyncTask useAsyncTask() {
 | 
				
			||||||
 | 
					  final pending = useState<Future<void>?>(null);
 | 
				
			||||||
 | 
					  final snapshot = useFuture(pending.value);
 | 
				
			||||||
 | 
					  final hasError =
 | 
				
			||||||
 | 
					      snapshot.hasError && snapshot.connectionState != ConnectionState.waiting;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (pending: pending, snapshot: snapshot, hasError: hasError);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										45
									
								
								moneymgr_mobile/lib/widgets/app_button.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								moneymgr_mobile/lib/widgets/app_button.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,45 @@
 | 
				
			|||||||
 | 
					import 'package:flextras/flextras.dart';
 | 
				
			||||||
 | 
					import 'package:flutter/foundation.dart';
 | 
				
			||||||
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
 | 
					import 'package:flutter_gutter/flutter_gutter.dart';
 | 
				
			||||||
 | 
					import 'package:flutter_hooks/flutter_hooks.dart';
 | 
				
			||||||
 | 
					import 'package:moneymgr_mobile/utils/extensions.dart';
 | 
				
			||||||
 | 
					import 'package:moneymgr_mobile/utils/hooks.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// A button that shows a circular progress indicator when the [onPressed] callback
 | 
				
			||||||
 | 
					/// is pending.
 | 
				
			||||||
 | 
					class AppButton extends HookWidget {
 | 
				
			||||||
 | 
					  const AppButton({
 | 
				
			||||||
 | 
					    super.key,
 | 
				
			||||||
 | 
					    required this.onPressed,
 | 
				
			||||||
 | 
					    required this.label,
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  final AsyncCallback? onPressed;
 | 
				
			||||||
 | 
					  final String label;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  Widget build(BuildContext context) {
 | 
				
			||||||
 | 
					    final (:pending, :snapshot, hasError: _) = useAsyncTask();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return FilledButton(
 | 
				
			||||||
 | 
					      onPressed: onPressed == null ? null : () => pending.value = onPressed!(),
 | 
				
			||||||
 | 
					      child: SeparatedRow(
 | 
				
			||||||
 | 
					        separatorBuilder: () => const GutterSmall(),
 | 
				
			||||||
 | 
					        mainAxisAlignment: MainAxisAlignment.center,
 | 
				
			||||||
 | 
					        children: [
 | 
				
			||||||
 | 
					          if (snapshot.connectionState == ConnectionState.waiting)
 | 
				
			||||||
 | 
					            SizedBox.square(
 | 
				
			||||||
 | 
					              dimension: 12,
 | 
				
			||||||
 | 
					              child: CircularProgressIndicator(
 | 
				
			||||||
 | 
					                strokeWidth: 2,
 | 
				
			||||||
 | 
					                color: context.colorScheme.onPrimary,
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					          Text(label),
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										100
									
								
								moneymgr_mobile/lib/widgets/scaffold_with_navigation.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								moneymgr_mobile/lib/widgets/scaffold_with_navigation.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,100 @@
 | 
				
			|||||||
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
 | 
					import 'package:go_router/go_router.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// A scaffold that shows navigation bar/rail when the current path is a navigation
 | 
				
			||||||
 | 
					/// item.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// When in a navigation item, a [NavigationBar] will be shown if the width of the
 | 
				
			||||||
 | 
					/// screen is less than 600dp. Otherwise, a [NavigationRail] will be shown.
 | 
				
			||||||
 | 
					class ScaffoldWithNavigation extends StatelessWidget {
 | 
				
			||||||
 | 
					  const ScaffoldWithNavigation({
 | 
				
			||||||
 | 
					    super.key,
 | 
				
			||||||
 | 
					    required this.child,
 | 
				
			||||||
 | 
					    required this.selectedIndex,
 | 
				
			||||||
 | 
					    required this.navigationItems,
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  final Widget child;
 | 
				
			||||||
 | 
					  final int selectedIndex;
 | 
				
			||||||
 | 
					  final List<NavigationItem> navigationItems;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  Widget build(BuildContext context) {
 | 
				
			||||||
 | 
					    void onDestinationSelected(int index) =>
 | 
				
			||||||
 | 
					        context.go(navigationItems[index].path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Use navigation rail instead of navigation bar when the screen width is
 | 
				
			||||||
 | 
					    // larger than 600dp.
 | 
				
			||||||
 | 
					    if (MediaQuery.sizeOf(context).width > 600) {
 | 
				
			||||||
 | 
					      return Scaffold(
 | 
				
			||||||
 | 
					        body: Row(
 | 
				
			||||||
 | 
					          children: [
 | 
				
			||||||
 | 
					            NavigationRail(
 | 
				
			||||||
 | 
					              selectedIndex: selectedIndex,
 | 
				
			||||||
 | 
					              onDestinationSelected: onDestinationSelected,
 | 
				
			||||||
 | 
					              destinations: [
 | 
				
			||||||
 | 
					                for (final item in navigationItems)
 | 
				
			||||||
 | 
					                  NavigationRailDestination(
 | 
				
			||||||
 | 
					                    icon: Icon(item.icon),
 | 
				
			||||||
 | 
					                    selectedIcon: item.selectedIcon != null
 | 
				
			||||||
 | 
					                        ? Icon(item.selectedIcon)
 | 
				
			||||||
 | 
					                        : null,
 | 
				
			||||||
 | 
					                    label: Text(item.label),
 | 
				
			||||||
 | 
					                  )
 | 
				
			||||||
 | 
					              ],
 | 
				
			||||||
 | 
					              extended: true,
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            Expanded(child: child),
 | 
				
			||||||
 | 
					          ],
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return Scaffold(
 | 
				
			||||||
 | 
					      body: child,
 | 
				
			||||||
 | 
					      bottomNavigationBar: NavigationBar(
 | 
				
			||||||
 | 
					        selectedIndex: selectedIndex,
 | 
				
			||||||
 | 
					        onDestinationSelected: onDestinationSelected,
 | 
				
			||||||
 | 
					        destinations: [
 | 
				
			||||||
 | 
					          for (final item in navigationItems)
 | 
				
			||||||
 | 
					            NavigationDestination(
 | 
				
			||||||
 | 
					              icon: Icon(item.icon),
 | 
				
			||||||
 | 
					              selectedIcon:
 | 
				
			||||||
 | 
					              item.selectedIcon != null ? Icon(item.selectedIcon) : null,
 | 
				
			||||||
 | 
					              label: item.label,
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// An item that represents a navigation destination in a navigation bar/rail.
 | 
				
			||||||
 | 
					class NavigationItem {
 | 
				
			||||||
 | 
					  /// Path in the router.
 | 
				
			||||||
 | 
					  final String path;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Widget to show when navigating to this [path].
 | 
				
			||||||
 | 
					  final WidgetBuilder body;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Icon in the navigation bar.
 | 
				
			||||||
 | 
					  final IconData icon;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Icon in the navigation bar when selected.
 | 
				
			||||||
 | 
					  final IconData? selectedIcon;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Label in the navigation bar.
 | 
				
			||||||
 | 
					  final String label;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// The subroutes of the route from this [path].
 | 
				
			||||||
 | 
					  final List<RouteBase> routes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  NavigationItem({
 | 
				
			||||||
 | 
					    required this.path,
 | 
				
			||||||
 | 
					    required this.body,
 | 
				
			||||||
 | 
					    required this.icon,
 | 
				
			||||||
 | 
					    this.selectedIcon,
 | 
				
			||||||
 | 
					    required this.label,
 | 
				
			||||||
 | 
					    this.routes = const [],
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -6,6 +6,10 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include "generated_plugin_registrant.h"
 | 
					#include "generated_plugin_registrant.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void fl_register_plugins(FlPluginRegistry* registry) {
 | 
					void fl_register_plugins(FlPluginRegistry* registry) {
 | 
				
			||||||
 | 
					  g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar =
 | 
				
			||||||
 | 
					      fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin");
 | 
				
			||||||
 | 
					  flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,6 +3,7 @@
 | 
				
			|||||||
#
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
list(APPEND FLUTTER_PLUGIN_LIST
 | 
					list(APPEND FLUTTER_PLUGIN_LIST
 | 
				
			||||||
 | 
					  flutter_secure_storage_linux
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
 | 
					list(APPEND FLUTTER_FFI_PLUGIN_LIST
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,8 +5,12 @@
 | 
				
			|||||||
import FlutterMacOS
 | 
					import FlutterMacOS
 | 
				
			||||||
import Foundation
 | 
					import Foundation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import flutter_secure_storage_macos
 | 
				
			||||||
 | 
					import path_provider_foundation
 | 
				
			||||||
import shared_preferences_foundation
 | 
					import shared_preferences_foundation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
 | 
					func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
 | 
				
			||||||
 | 
					  FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
 | 
				
			||||||
 | 
					  PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
 | 
				
			||||||
  SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
 | 
					  SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -257,11 +257,35 @@ packages:
 | 
				
			|||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "1.1.1"
 | 
					    version: "1.1.1"
 | 
				
			||||||
 | 
					  flextras:
 | 
				
			||||||
 | 
					    dependency: "direct main"
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					      name: flextras
 | 
				
			||||||
 | 
					      sha256: e73b5c86dd9419569d2a48db470059b41b496012513e4e1bdc56ba2c661048d9
 | 
				
			||||||
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
 | 
					    source: hosted
 | 
				
			||||||
 | 
					    version: "1.0.0"
 | 
				
			||||||
  flutter:
 | 
					  flutter:
 | 
				
			||||||
    dependency: "direct main"
 | 
					    dependency: "direct main"
 | 
				
			||||||
    description: flutter
 | 
					    description: flutter
 | 
				
			||||||
    source: sdk
 | 
					    source: sdk
 | 
				
			||||||
    version: "0.0.0"
 | 
					    version: "0.0.0"
 | 
				
			||||||
 | 
					  flutter_adaptive_scaffold:
 | 
				
			||||||
 | 
					    dependency: transitive
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					      name: flutter_adaptive_scaffold
 | 
				
			||||||
 | 
					      sha256: "5eb1d1d174304a4e67c4bb402ed38cb4a5ebdac95ce54099e91460accb33d295"
 | 
				
			||||||
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
 | 
					    source: hosted
 | 
				
			||||||
 | 
					    version: "0.3.3+1"
 | 
				
			||||||
 | 
					  flutter_gutter:
 | 
				
			||||||
 | 
					    dependency: "direct main"
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					      name: flutter_gutter
 | 
				
			||||||
 | 
					      sha256: "2aa99181796d6f7d2de66da962b71b0feb996ec69b7a1ad2ac1c2119f25b041b"
 | 
				
			||||||
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
 | 
					    source: hosted
 | 
				
			||||||
 | 
					    version: "2.2.0"
 | 
				
			||||||
  flutter_hooks:
 | 
					  flutter_hooks:
 | 
				
			||||||
    dependency: "direct main"
 | 
					    dependency: "direct main"
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -294,6 +318,54 @@ packages:
 | 
				
			|||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "2.6.1"
 | 
					    version: "2.6.1"
 | 
				
			||||||
 | 
					  flutter_secure_storage:
 | 
				
			||||||
 | 
					    dependency: "direct main"
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					      name: flutter_secure_storage
 | 
				
			||||||
 | 
					      sha256: "9cad52d75ebc511adfae3d447d5d13da15a55a92c9410e50f67335b6d21d16ea"
 | 
				
			||||||
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
 | 
					    source: hosted
 | 
				
			||||||
 | 
					    version: "9.2.4"
 | 
				
			||||||
 | 
					  flutter_secure_storage_linux:
 | 
				
			||||||
 | 
					    dependency: transitive
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					      name: flutter_secure_storage_linux
 | 
				
			||||||
 | 
					      sha256: be76c1d24a97d0b98f8b54bce6b481a380a6590df992d0098f868ad54dc8f688
 | 
				
			||||||
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
 | 
					    source: hosted
 | 
				
			||||||
 | 
					    version: "1.2.3"
 | 
				
			||||||
 | 
					  flutter_secure_storage_macos:
 | 
				
			||||||
 | 
					    dependency: transitive
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					      name: flutter_secure_storage_macos
 | 
				
			||||||
 | 
					      sha256: "6c0a2795a2d1de26ae202a0d78527d163f4acbb11cde4c75c670f3a0fc064247"
 | 
				
			||||||
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
 | 
					    source: hosted
 | 
				
			||||||
 | 
					    version: "3.1.3"
 | 
				
			||||||
 | 
					  flutter_secure_storage_platform_interface:
 | 
				
			||||||
 | 
					    dependency: transitive
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					      name: flutter_secure_storage_platform_interface
 | 
				
			||||||
 | 
					      sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8
 | 
				
			||||||
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
 | 
					    source: hosted
 | 
				
			||||||
 | 
					    version: "1.1.2"
 | 
				
			||||||
 | 
					  flutter_secure_storage_web:
 | 
				
			||||||
 | 
					    dependency: transitive
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					      name: flutter_secure_storage_web
 | 
				
			||||||
 | 
					      sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9
 | 
				
			||||||
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
 | 
					    source: hosted
 | 
				
			||||||
 | 
					    version: "1.2.1"
 | 
				
			||||||
 | 
					  flutter_secure_storage_windows:
 | 
				
			||||||
 | 
					    dependency: transitive
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					      name: flutter_secure_storage_windows
 | 
				
			||||||
 | 
					      sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709
 | 
				
			||||||
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
 | 
					    source: hosted
 | 
				
			||||||
 | 
					    version: "3.1.2"
 | 
				
			||||||
  flutter_test:
 | 
					  flutter_test:
 | 
				
			||||||
    dependency: "direct dev"
 | 
					    dependency: "direct dev"
 | 
				
			||||||
    description: flutter
 | 
					    description: flutter
 | 
				
			||||||
@@ -304,8 +376,16 @@ packages:
 | 
				
			|||||||
    description: flutter
 | 
					    description: flutter
 | 
				
			||||||
    source: sdk
 | 
					    source: sdk
 | 
				
			||||||
    version: "0.0.0"
 | 
					    version: "0.0.0"
 | 
				
			||||||
 | 
					  freezed:
 | 
				
			||||||
 | 
					    dependency: "direct dev"
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					      name: freezed
 | 
				
			||||||
 | 
					      sha256: "6022db4c7bfa626841b2a10f34dd1e1b68e8f8f9650db6112dcdeeca45ca793c"
 | 
				
			||||||
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
 | 
					    source: hosted
 | 
				
			||||||
 | 
					    version: "3.0.6"
 | 
				
			||||||
  freezed_annotation:
 | 
					  freezed_annotation:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: "direct main"
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: freezed_annotation
 | 
					      name: freezed_annotation
 | 
				
			||||||
      sha256: c87ff004c8aa6af2d531668b46a4ea379f7191dc6dfa066acd53d506da6e044b
 | 
					      sha256: c87ff004c8aa6af2d531668b46a4ea379f7191dc6dfa066acd53d506da6e044b
 | 
				
			||||||
@@ -404,18 +484,26 @@ packages:
 | 
				
			|||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: js
 | 
					      name: js
 | 
				
			||||||
      sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc"
 | 
					      sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "0.7.2"
 | 
					    version: "0.6.7"
 | 
				
			||||||
  json_annotation:
 | 
					  json_annotation:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: "direct main"
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: json_annotation
 | 
					      name: json_annotation
 | 
				
			||||||
      sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"
 | 
					      sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "4.9.0"
 | 
					    version: "4.9.0"
 | 
				
			||||||
 | 
					  json_serializable:
 | 
				
			||||||
 | 
					    dependency: "direct dev"
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					      name: json_serializable
 | 
				
			||||||
 | 
					      sha256: c50ef5fc083d5b5e12eef489503ba3bf5ccc899e487d691584699b4bdefeea8c
 | 
				
			||||||
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
 | 
					    source: hosted
 | 
				
			||||||
 | 
					    version: "6.9.5"
 | 
				
			||||||
  leak_tracker:
 | 
					  leak_tracker:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -504,6 +592,30 @@ packages:
 | 
				
			|||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "1.9.1"
 | 
					    version: "1.9.1"
 | 
				
			||||||
 | 
					  path_provider:
 | 
				
			||||||
 | 
					    dependency: transitive
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					      name: path_provider
 | 
				
			||||||
 | 
					      sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
 | 
				
			||||||
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
 | 
					    source: hosted
 | 
				
			||||||
 | 
					    version: "2.1.5"
 | 
				
			||||||
 | 
					  path_provider_android:
 | 
				
			||||||
 | 
					    dependency: transitive
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					      name: path_provider_android
 | 
				
			||||||
 | 
					      sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9
 | 
				
			||||||
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
 | 
					    source: hosted
 | 
				
			||||||
 | 
					    version: "2.2.17"
 | 
				
			||||||
 | 
					  path_provider_foundation:
 | 
				
			||||||
 | 
					    dependency: transitive
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					      name: path_provider_foundation
 | 
				
			||||||
 | 
					      sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942"
 | 
				
			||||||
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
 | 
					    source: hosted
 | 
				
			||||||
 | 
					    version: "2.4.1"
 | 
				
			||||||
  path_provider_linux:
 | 
					  path_provider_linux:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -701,6 +813,14 @@ packages:
 | 
				
			|||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "2.0.0"
 | 
					    version: "2.0.0"
 | 
				
			||||||
 | 
					  source_helper:
 | 
				
			||||||
 | 
					    dependency: transitive
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					      name: source_helper
 | 
				
			||||||
 | 
					      sha256: "86d247119aedce8e63f4751bd9626fc9613255935558447569ad42f9f5b48b3c"
 | 
				
			||||||
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
 | 
					    source: hosted
 | 
				
			||||||
 | 
					    version: "1.3.5"
 | 
				
			||||||
  source_span:
 | 
					  source_span:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -853,6 +973,14 @@ packages:
 | 
				
			|||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "3.0.3"
 | 
					    version: "3.0.3"
 | 
				
			||||||
 | 
					  win32:
 | 
				
			||||||
 | 
					    dependency: transitive
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					      name: win32
 | 
				
			||||||
 | 
					      sha256: "66814138c3562338d05613a6e368ed8cfb237ad6d64a9e9334be3f309acfca03"
 | 
				
			||||||
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
 | 
					    source: hosted
 | 
				
			||||||
 | 
					    version: "5.14.0"
 | 
				
			||||||
  xdg_directories:
 | 
					  xdg_directories:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -38,6 +38,9 @@ dependencies:
 | 
				
			|||||||
  # Preferences management
 | 
					  # Preferences management
 | 
				
			||||||
  shared_preferences: ^2.5.3
 | 
					  shared_preferences: ^2.5.3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  # Credentials storage
 | 
				
			||||||
 | 
					  flutter_secure_storage: ^9.2.4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  # Splash screen
 | 
					  # Splash screen
 | 
				
			||||||
  flutter_native_splash: ^2.4.6
 | 
					  flutter_native_splash: ^2.4.6
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -51,6 +54,15 @@ dependencies:
 | 
				
			|||||||
  # Router
 | 
					  # Router
 | 
				
			||||||
  go_router: ^15.2.4
 | 
					  go_router: ^15.2.4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  # Flutter extras widgets for columns and rows
 | 
				
			||||||
 | 
					  flextras: ^1.0.0
 | 
				
			||||||
 | 
					  flutter_gutter: ^2.2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  # Help in models building
 | 
				
			||||||
 | 
					  freezed_annotation: ^3.0.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  json_annotation: ^4.9.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
dev_dependencies:
 | 
					dev_dependencies:
 | 
				
			||||||
  flutter_test:
 | 
					  flutter_test:
 | 
				
			||||||
    sdk: flutter
 | 
					    sdk: flutter
 | 
				
			||||||
@@ -68,6 +80,14 @@ dev_dependencies:
 | 
				
			|||||||
  # Riverpod code generation
 | 
					  # Riverpod code generation
 | 
				
			||||||
  riverpod_generator: ^2.6.5
 | 
					  riverpod_generator: ^2.6.5
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  # Freezed code generation
 | 
				
			||||||
 | 
					  freezed: ^3.0.6
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  # JSON serialization
 | 
				
			||||||
 | 
					  json_serializable: ^6.9.5
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# For information on the generic Dart part of this file, see the
 | 
					# For information on the generic Dart part of this file, see the
 | 
				
			||||||
# following page: https://dart.dev/tools/pub/pubspec
 | 
					# following page: https://dart.dev/tools/pub/pubspec
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,9 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include "generated_plugin_registrant.h"
 | 
					#include "generated_plugin_registrant.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
 | 
					void RegisterPlugins(flutter::PluginRegistry* registry) {
 | 
				
			||||||
 | 
					  FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
 | 
				
			||||||
 | 
					      registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,6 +3,7 @@
 | 
				
			|||||||
#
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
list(APPEND FLUTTER_PLUGIN_LIST
 | 
					list(APPEND FLUTTER_PLUGIN_LIST
 | 
				
			||||||
 | 
					  flutter_secure_storage_windows
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
 | 
					list(APPEND FLUTTER_FFI_PLUGIN_LIST
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user