feat: Implements app theme system
Replaces hardcoded colors and standard Theme.of(context) accesses with a custom AppTheme. Introduces distinct light and dark themes and persists the selected theme mode using shared_preferences. Integrates the theme into the main app structure and applies it to various components and pages. Adds google_fonts dependency for theme typography.
This commit is contained in:
parent
94b4fc1723
commit
e963abb0ce
3
devtools_options.yaml
Normal file
3
devtools_options.yaml
Normal file
@ -0,0 +1,3 @@
|
||||
description: This file stores settings for Dart & Flutter DevTools.
|
||||
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
|
||||
extensions:
|
31
lib/app.dart
31
lib/app.dart
@ -1,30 +1,44 @@
|
||||
import 'package:barcode_scanner/router/router.dart';
|
||||
import 'package:barcode_scanner/themes/app_theme.dart';
|
||||
import 'package:barcode_scanner/utils/utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:responsive_framework/responsive_framework.dart';
|
||||
|
||||
class App extends StatelessWidget {
|
||||
class App extends ConsumerStatefulWidget {
|
||||
const App({super.key});
|
||||
|
||||
@override
|
||||
ConsumerState<App> createState() => _AppState();
|
||||
}
|
||||
|
||||
class _AppState extends ConsumerState<App> {
|
||||
ThemeMode _themeMode = AppTheme.themeMode;
|
||||
|
||||
void setThemeMode(ThemeMode mode) => safeSetState(() {
|
||||
_themeMode = mode;
|
||||
AppTheme.saveThemeMode(mode);
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ProviderScope(overrides: [routerProvider], child: _InnerApp());
|
||||
return ProviderScope(
|
||||
overrides: [routerProvider],
|
||||
child: _InnerApp(themeMode: _themeMode),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _InnerApp extends ConsumerWidget {
|
||||
const _InnerApp();
|
||||
const _InnerApp({required this.themeMode});
|
||||
final ThemeMode themeMode;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return MaterialApp.router(
|
||||
debugShowCheckedModeBanner: false,
|
||||
title: "BarcodeScan",
|
||||
theme: ThemeData(
|
||||
fontFamily: 'Roboto',
|
||||
scaffoldBackgroundColor: Colors.white,
|
||||
),
|
||||
locale: Locale('fr'),
|
||||
supportedLocales: [Locale('fr', 'FR'), Locale('en', 'US')],
|
||||
localizationsDelegates: [
|
||||
@ -40,6 +54,9 @@ class _InnerApp extends ConsumerWidget {
|
||||
const Breakpoint(start: 451, end: 800, name: TABLET),
|
||||
],
|
||||
),
|
||||
theme: ThemeData(brightness: Brightness.light),
|
||||
darkTheme: ThemeData(brightness: Brightness.dark),
|
||||
themeMode: themeMode,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import 'package:barcode_scanner/themes/app_theme.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class LoadingProgressComponent extends StatelessWidget {
|
||||
@ -22,7 +23,7 @@ class LoadingProgressComponent extends StatelessWidget {
|
||||
value: value,
|
||||
strokeWidth: 2,
|
||||
valueColor: AlwaysStoppedAnimation<Color>(
|
||||
color ?? Theme.of(context).primaryColor,
|
||||
color ?? AppTheme.of(context).primary,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -1,4 +1,5 @@
|
||||
import 'package:barcode_scanner/components/loading_progress_component.dart';
|
||||
import 'package:barcode_scanner/themes/app_theme.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class PrimaryButtonComponent extends StatelessWidget {
|
||||
@ -16,7 +17,7 @@ class PrimaryButtonComponent extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Theme.of(context).primaryColor,
|
||||
backgroundColor: AppTheme.of(context).primary,
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
),
|
||||
@ -26,14 +27,16 @@ class PrimaryButtonComponent extends StatelessWidget {
|
||||
height: 20,
|
||||
width: 20,
|
||||
child: Center(
|
||||
child: const LoadingProgressComponent(color: Colors.white),
|
||||
child: LoadingProgressComponent(
|
||||
color: AppTheme.of(context).primary,
|
||||
),
|
||||
),
|
||||
)
|
||||
: Text(
|
||||
text,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
color: AppTheme.of(context).primaryText,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -1,3 +1,4 @@
|
||||
import 'package:barcode_scanner/themes/app_theme.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ProductScannedComponent extends StatefulWidget {
|
||||
@ -34,7 +35,7 @@ class _ProductScannedComponentState extends State<ProductScannedComponent> {
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
color: AppTheme.of(context).secondaryBackground,
|
||||
borderRadius: BorderRadius.circular(16.0),
|
||||
),
|
||||
child: Padding(
|
||||
@ -146,15 +147,26 @@ class _ProductScannedComponentState extends State<ProductScannedComponent> {
|
||||
onPressed: () async {
|
||||
await widget.onRescan?.call();
|
||||
},
|
||||
child: Text('Nouveau scan'),
|
||||
child: Text(
|
||||
'Nouveau scan',
|
||||
style: AppTheme.of(context).bodyMedium,
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: FilledButton(
|
||||
style: ButtonStyle(
|
||||
backgroundColor: WidgetStatePropertyAll(
|
||||
AppTheme.of(context).primary,
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
widget.onDetails?.call();
|
||||
},
|
||||
child: Text('Voir détails'),
|
||||
child: Text(
|
||||
'Voir détails',
|
||||
style: AppTheme.of(context).bodyMedium,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
@ -1,6 +1,7 @@
|
||||
import 'package:barcode_scanner/pages/login_page/login_page_model.dart';
|
||||
import 'package:barcode_scanner/router/go_router_builder.dart';
|
||||
import 'package:barcode_scanner/router/go_secure_router_builder.dart';
|
||||
import 'package:barcode_scanner/themes/app_theme.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
@ -17,8 +18,8 @@ class _HomePageState extends ConsumerState<HomePage> {
|
||||
final primaryColor = Theme.of(context).primaryColor;
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: AppTheme.of(context).primaryBackground,
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.white,
|
||||
automaticallyImplyLeading: false,
|
||||
actions: [
|
||||
IconButton(
|
||||
@ -32,7 +33,6 @@ class _HomePageState extends ConsumerState<HomePage> {
|
||||
),
|
||||
],
|
||||
),
|
||||
backgroundColor: Colors.white,
|
||||
body: Center(
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(24),
|
||||
@ -44,7 +44,7 @@ class _HomePageState extends ConsumerState<HomePage> {
|
||||
width: 100,
|
||||
height: 100,
|
||||
decoration: BoxDecoration(
|
||||
color: primaryColor,
|
||||
color: AppTheme.of(context).primary,
|
||||
shape: BoxShape.circle,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
@ -87,7 +87,7 @@ class _HomePageState extends ConsumerState<HomePage> {
|
||||
height: 200,
|
||||
padding: const EdgeInsets.all(40),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFF6F8FA),
|
||||
color: AppTheme.of(context).alternate,
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
boxShadow: const [
|
||||
BoxShadow(
|
||||
@ -97,10 +97,10 @@ class _HomePageState extends ConsumerState<HomePage> {
|
||||
),
|
||||
],
|
||||
),
|
||||
child: const Icon(
|
||||
child: Icon(
|
||||
Icons.qr_code_2_rounded,
|
||||
size: 60,
|
||||
color: Colors.black87,
|
||||
color: AppTheme.of(context).primaryText,
|
||||
),
|
||||
),
|
||||
|
||||
@ -122,7 +122,7 @@ class _HomePageState extends ConsumerState<HomePage> {
|
||||
ScannerRoute().push(context);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: primaryColor,
|
||||
backgroundColor: AppTheme.of(context).primary,
|
||||
padding: const EdgeInsets.symmetric(vertical: 18),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
|
@ -1,8 +1,10 @@
|
||||
import 'package:barcode_scanner/components/primary_button_component.dart';
|
||||
import 'package:barcode_scanner/pages/login_page/login_page_model.dart';
|
||||
import 'package:barcode_scanner/router/go_router_builder.dart';
|
||||
import 'package:barcode_scanner/themes/app_theme.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
|
||||
class LoginPage extends ConsumerStatefulWidget {
|
||||
const LoginPage({super.key});
|
||||
@ -32,15 +34,15 @@ class _LoginPageState extends ConsumerState<LoginPage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
backgroundColor: AppTheme.of(context).primaryBackground,
|
||||
appBar: AppBar(
|
||||
title: Text(
|
||||
'BarcodeScan',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(fontSize: 28, fontWeight: FontWeight.bold),
|
||||
style: AppTheme.of(context).titleLarge,
|
||||
),
|
||||
centerTitle: true,
|
||||
backgroundColor: Colors.white,
|
||||
backgroundColor: AppTheme.of(context).primaryBackground,
|
||||
elevation: 0,
|
||||
),
|
||||
body: Align(
|
||||
@ -58,15 +60,14 @@ class _LoginPageState extends ConsumerState<LoginPage> {
|
||||
const SizedBox(height: 32),
|
||||
Text(
|
||||
'Welcome Back',
|
||||
style: TextStyle(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
style: AppTheme.of(context).titleLarge,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'Fill out the information below in order to access your account.',
|
||||
style: TextStyle(color: Colors.grey[700]),
|
||||
style: AppTheme.of(context).bodyMedium.override(
|
||||
color: AppTheme.of(context).secondaryText,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
|
||||
@ -75,7 +76,7 @@ class _LoginPageState extends ConsumerState<LoginPage> {
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Email',
|
||||
filled: true,
|
||||
fillColor: Colors.grey[100],
|
||||
fillColor: AppTheme.of(context).secondaryBackground,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide.none,
|
||||
@ -101,7 +102,7 @@ class _LoginPageState extends ConsumerState<LoginPage> {
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Password',
|
||||
filled: true,
|
||||
fillColor: Colors.grey[100],
|
||||
fillColor: AppTheme.of(context).secondaryBackground,
|
||||
suffixIcon: IconButton(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
@ -197,7 +198,12 @@ class _LoginPageState extends ConsumerState<LoginPage> {
|
||||
/// Forgot Password
|
||||
TextButton(
|
||||
onPressed: () {},
|
||||
child: const Text('Forgot Password?'),
|
||||
child: Text(
|
||||
'Forgot Password?',
|
||||
style: TextStyle(
|
||||
color: AppTheme.of(context).primaryText,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
|
@ -1,4 +1,6 @@
|
||||
import 'package:barcode_scanner/components/primary_button_component.dart';
|
||||
import 'package:barcode_scanner/router/go_secure_router_builder.dart';
|
||||
import 'package:barcode_scanner/themes/app_theme.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ProductFormPage extends StatefulWidget {
|
||||
@ -29,7 +31,7 @@ class _ProductFormPageState extends State<ProductFormPage> {
|
||||
return InputDecoration(
|
||||
labelText: label,
|
||||
filled: true,
|
||||
fillColor: Colors.grey[100],
|
||||
fillColor: AppTheme.of(context).secondaryBackground,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide.none,
|
||||
@ -40,18 +42,24 @@ class _ProductFormPageState extends State<ProductFormPage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: AppTheme.of(context).primaryBackground,
|
||||
appBar: AppBar(
|
||||
leading: IconButton(
|
||||
icon: Icon(Icons.arrow_back_ios),
|
||||
icon: Icon(
|
||||
Icons.arrow_back_ios,
|
||||
color: AppTheme.of(context).primaryText,
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
title: const Text('Formulaire Produit'),
|
||||
title: Text(
|
||||
'Formulaire Produit',
|
||||
style: AppTheme.of(context).titleLarge,
|
||||
),
|
||||
centerTitle: true,
|
||||
backgroundColor: Colors.white,
|
||||
backgroundColor: AppTheme.of(context).primaryBackground,
|
||||
elevation: 0,
|
||||
foregroundColor: Colors.black,
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(24),
|
||||
@ -96,7 +104,7 @@ class _ProductFormPageState extends State<ProductFormPage> {
|
||||
const SizedBox(height: 24),
|
||||
|
||||
/// Bouton sauvegarder
|
||||
ElevatedButton(
|
||||
PrimaryButtonComponent(
|
||||
onPressed: () {
|
||||
if (_formKey.currentState!.validate()) {
|
||||
// Traitement ici
|
||||
@ -104,20 +112,7 @@ class _ProductFormPageState extends State<ProductFormPage> {
|
||||
HomeRoute().go(context);
|
||||
}
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Theme.of(context).primaryColor,
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
),
|
||||
child: const Text(
|
||||
'Sauvegarder',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
text: 'Sauvegarder',
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -3,6 +3,7 @@ import 'dart:async';
|
||||
import 'package:barcode_scanner/backend/api/api_calls.dart';
|
||||
import 'package:barcode_scanner/components/product_scanned_component.dart';
|
||||
import 'package:barcode_scanner/router/go_secure_router_builder.dart';
|
||||
import 'package:barcode_scanner/themes/app_theme.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
@ -226,15 +227,19 @@ class _ScannerPageState extends ConsumerState<ScannerPage>
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.black,
|
||||
backgroundColor: AppTheme.of(context).primaryBackground,
|
||||
automaticallyImplyLeading: false,
|
||||
leading: IconButton(
|
||||
icon: Icon(Icons.arrow_back_ios, color: Colors.white, size: 24.0),
|
||||
icon: Icon(
|
||||
Icons.arrow_back_ios,
|
||||
color: AppTheme.of(context).primaryText,
|
||||
size: 24.0,
|
||||
),
|
||||
onPressed: () async {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
title: Text('Scanner'),
|
||||
title: Text('Scanner', style: AppTheme.of(context).titleLarge),
|
||||
actions: [
|
||||
Padding(
|
||||
padding: EdgeInsetsDirectional.fromSTEB(8.0, 0.0, 8.0, 0.0),
|
||||
@ -275,9 +280,9 @@ class _ScannerPageState extends ConsumerState<ScannerPage>
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
Color(0xB7000000),
|
||||
AppTheme.of(context).primaryBackground,
|
||||
Colors.transparent,
|
||||
Color(0xB7000000),
|
||||
AppTheme.of(context).primaryBackground,
|
||||
],
|
||||
stops: [0.0, 0.5, 1.0],
|
||||
begin: AlignmentDirectional(0.0, -1.0),
|
||||
@ -304,7 +309,9 @@ class _ScannerPageState extends ConsumerState<ScannerPage>
|
||||
Text(
|
||||
'Scanner le code',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(color: Colors.white),
|
||||
style: TextStyle(
|
||||
color: AppTheme.of(context).primaryText,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsetsDirectional.fromSTEB(
|
||||
@ -316,7 +323,9 @@ class _ScannerPageState extends ConsumerState<ScannerPage>
|
||||
child: Text(
|
||||
'Positionnez le code-barres ou QR code dans le cadre',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(color: Colors.white),
|
||||
style: TextStyle(
|
||||
color: AppTheme.of(context).primaryText,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -356,7 +365,9 @@ class _ScannerPageState extends ConsumerState<ScannerPage>
|
||||
children: [
|
||||
Icon(
|
||||
Icons.qr_code_scanner,
|
||||
color: Colors.white,
|
||||
color: Colors.white.withValues(
|
||||
alpha: .5,
|
||||
),
|
||||
size: 64.0,
|
||||
),
|
||||
],
|
||||
@ -367,122 +378,24 @@ class _ScannerPageState extends ConsumerState<ScannerPage>
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsetsDirectional.fromSTEB(
|
||||
24.0,
|
||||
0.0,
|
||||
24.0,
|
||||
40.0,
|
||||
),
|
||||
child: Column(
|
||||
padding: const EdgeInsets.all(40),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
spacing: 12,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'Types de codes supportés:',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
Container(
|
||||
width: 80.0,
|
||||
height: 40.0,
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0x33FFFFFF),
|
||||
borderRadius: BorderRadius.circular(
|
||||
8.0,
|
||||
),
|
||||
),
|
||||
child: Align(
|
||||
alignment: AlignmentDirectional(
|
||||
0.0,
|
||||
0.0,
|
||||
),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
'EAN-13',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
width: 80.0,
|
||||
height: 40.0,
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0x33FFFFFF),
|
||||
borderRadius: BorderRadius.circular(
|
||||
8.0,
|
||||
),
|
||||
),
|
||||
child: Align(
|
||||
alignment: AlignmentDirectional(
|
||||
0.0,
|
||||
0.0,
|
||||
),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
'Code 128',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
width: 80.0,
|
||||
height: 40.0,
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0x33FFFFFF),
|
||||
borderRadius: BorderRadius.circular(
|
||||
8.0,
|
||||
),
|
||||
),
|
||||
child: Align(
|
||||
alignment: AlignmentDirectional(
|
||||
0.0,
|
||||
0.0,
|
||||
),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
'QR Code',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
IconButton(
|
||||
color: Color(0x33FFFFFF),
|
||||
icon: Icon(
|
||||
mobileScannerController.torchEnabled
|
||||
? Icons.flash_off
|
||||
: Icons.flash_on,
|
||||
color: Colors.white,
|
||||
size: 28.0,
|
||||
),
|
||||
onPressed: () {
|
||||
mobileScannerController.toggleTorch();
|
||||
},
|
||||
),
|
||||
],
|
||||
IconButton(
|
||||
color: AppTheme.of(context).primaryText,
|
||||
iconSize: 2,
|
||||
icon: Icon(
|
||||
mobileScannerController.torchEnabled
|
||||
? Icons.flash_off
|
||||
: Icons.flash_on,
|
||||
color: AppTheme.of(context).primaryText,
|
||||
size: 28.0,
|
||||
),
|
||||
onPressed: () {
|
||||
mobileScannerController.toggleTorch();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -2,6 +2,7 @@ import 'package:barcode_scanner/components/loading_progress_component.dart';
|
||||
import 'package:barcode_scanner/pages/login_page/login_page_model.dart';
|
||||
import 'package:barcode_scanner/router/go_router_builder.dart';
|
||||
import 'package:barcode_scanner/router/go_secure_router_builder.dart';
|
||||
import 'package:barcode_scanner/themes/app_theme.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
@ -22,6 +23,9 @@ class SplashPage extends ConsumerWidget {
|
||||
});
|
||||
}
|
||||
});
|
||||
return Scaffold(body: Center(child: LoadingProgressComponent()));
|
||||
return Scaffold(
|
||||
backgroundColor: AppTheme.of(context).primaryBackground,
|
||||
body: Center(child: LoadingProgressComponent()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,12 @@
|
||||
import 'package:barcode_scanner/pages/login_page/login_page_model.dart';
|
||||
import 'package:barcode_scanner/provider_container.dart';
|
||||
import 'package:barcode_scanner/themes/app_theme.dart';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
class InitializerService {
|
||||
static Future init() async {
|
||||
await AppTheme.initialize();
|
||||
await providerContainer
|
||||
.read(loginPageModelProvider.notifier)
|
||||
.checkHasUserConnected();
|
||||
|
381
lib/themes/app_theme.dart
Normal file
381
lib/themes/app_theme.dart
Normal file
@ -0,0 +1,381 @@
|
||||
// ignore_for_file: overridden_fields, annotate_overrides
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
const kThemeModeKey = '__theme_mode__';
|
||||
|
||||
SharedPreferences? _prefs;
|
||||
|
||||
abstract class AppTheme {
|
||||
static Future initialize() async =>
|
||||
_prefs = await SharedPreferences.getInstance();
|
||||
|
||||
static ThemeMode get themeMode {
|
||||
final darkMode = _prefs?.getBool(kThemeModeKey);
|
||||
return darkMode == null
|
||||
? ThemeMode.system
|
||||
: darkMode
|
||||
? ThemeMode.dark
|
||||
: ThemeMode.light;
|
||||
}
|
||||
|
||||
static void saveThemeMode(ThemeMode mode) => mode == ThemeMode.system
|
||||
? _prefs?.remove(kThemeModeKey)
|
||||
: _prefs?.setBool(kThemeModeKey, mode == ThemeMode.dark);
|
||||
|
||||
static AppTheme of(BuildContext context) {
|
||||
return Theme.of(context).brightness == Brightness.dark
|
||||
? DarkModeTheme()
|
||||
: LightModeTheme();
|
||||
}
|
||||
|
||||
late Color primary;
|
||||
late Color secondary;
|
||||
late Color tertiary;
|
||||
late Color alternate;
|
||||
late Color primaryText;
|
||||
late Color secondaryText;
|
||||
late Color primaryBackground;
|
||||
late Color secondaryBackground;
|
||||
late Color accent1;
|
||||
late Color accent2;
|
||||
late Color accent3;
|
||||
late Color accent4;
|
||||
late Color success;
|
||||
late Color warning;
|
||||
late Color error;
|
||||
late Color info;
|
||||
|
||||
late Color trueBlue;
|
||||
late Color orangePeel;
|
||||
late Color white;
|
||||
late Color columbiaBlue;
|
||||
late Color battleshipGray;
|
||||
late Color backgroundTransparent;
|
||||
|
||||
String get displayLargeFamily => typography.displayLargeFamily;
|
||||
bool get displayLargeIsCustom => typography.displayLargeIsCustom;
|
||||
TextStyle get displayLarge => typography.displayLarge;
|
||||
String get displayMediumFamily => typography.displayMediumFamily;
|
||||
bool get displayMediumIsCustom => typography.displayMediumIsCustom;
|
||||
TextStyle get displayMedium => typography.displayMedium;
|
||||
String get displaySmallFamily => typography.displaySmallFamily;
|
||||
bool get displaySmallIsCustom => typography.displaySmallIsCustom;
|
||||
TextStyle get displaySmall => typography.displaySmall;
|
||||
String get headlineLargeFamily => typography.headlineLargeFamily;
|
||||
bool get headlineLargeIsCustom => typography.headlineLargeIsCustom;
|
||||
TextStyle get headlineLarge => typography.headlineLarge;
|
||||
String get headlineMediumFamily => typography.headlineMediumFamily;
|
||||
bool get headlineMediumIsCustom => typography.headlineMediumIsCustom;
|
||||
TextStyle get headlineMedium => typography.headlineMedium;
|
||||
String get headlineSmallFamily => typography.headlineSmallFamily;
|
||||
bool get headlineSmallIsCustom => typography.headlineSmallIsCustom;
|
||||
TextStyle get headlineSmall => typography.headlineSmall;
|
||||
String get titleLargeFamily => typography.titleLargeFamily;
|
||||
bool get titleLargeIsCustom => typography.titleLargeIsCustom;
|
||||
TextStyle get titleLarge => typography.titleLarge;
|
||||
String get titleMediumFamily => typography.titleMediumFamily;
|
||||
bool get titleMediumIsCustom => typography.titleMediumIsCustom;
|
||||
TextStyle get titleMedium => typography.titleMedium;
|
||||
String get titleSmallFamily => typography.titleSmallFamily;
|
||||
bool get titleSmallIsCustom => typography.titleSmallIsCustom;
|
||||
TextStyle get titleSmall => typography.titleSmall;
|
||||
String get labelLargeFamily => typography.labelLargeFamily;
|
||||
bool get labelLargeIsCustom => typography.labelLargeIsCustom;
|
||||
TextStyle get labelLarge => typography.labelLarge;
|
||||
String get labelMediumFamily => typography.labelMediumFamily;
|
||||
bool get labelMediumIsCustom => typography.labelMediumIsCustom;
|
||||
TextStyle get labelMedium => typography.labelMedium;
|
||||
String get labelSmallFamily => typography.labelSmallFamily;
|
||||
bool get labelSmallIsCustom => typography.labelSmallIsCustom;
|
||||
TextStyle get labelSmall => typography.labelSmall;
|
||||
String get bodyLargeFamily => typography.bodyLargeFamily;
|
||||
bool get bodyLargeIsCustom => typography.bodyLargeIsCustom;
|
||||
TextStyle get bodyLarge => typography.bodyLarge;
|
||||
String get bodyMediumFamily => typography.bodyMediumFamily;
|
||||
bool get bodyMediumIsCustom => typography.bodyMediumIsCustom;
|
||||
TextStyle get bodyMedium => typography.bodyMedium;
|
||||
String get bodySmallFamily => typography.bodySmallFamily;
|
||||
bool get bodySmallIsCustom => typography.bodySmallIsCustom;
|
||||
TextStyle get bodySmall => typography.bodySmall;
|
||||
|
||||
Typography get typography => ThemeTypography(this);
|
||||
}
|
||||
|
||||
class LightModeTheme extends AppTheme {
|
||||
@Deprecated('Use primary instead')
|
||||
Color get primaryColor => primary;
|
||||
@Deprecated('Use secondary instead')
|
||||
Color get secondaryColor => secondary;
|
||||
@Deprecated('Use tertiary instead')
|
||||
Color get tertiaryColor => tertiary;
|
||||
|
||||
late Color primary = const Color(0xFF3D6BB2);
|
||||
late Color secondary = const Color(0xFFF9A434);
|
||||
late Color tertiary = const Color(0xFFEE8B60);
|
||||
late Color alternate = const Color(0xFFE0E3E7);
|
||||
late Color primaryText = const Color(0xFF14181B);
|
||||
late Color secondaryText = const Color(0xFF919191);
|
||||
late Color primaryBackground = const Color(0xFFFFFFFF);
|
||||
late Color secondaryBackground = const Color(0xFFF1F4F8);
|
||||
late Color accent1 = const Color(0xFF003EA2);
|
||||
late Color accent2 = const Color(0xFFB36600);
|
||||
late Color accent3 = const Color(0x4DEE8B60);
|
||||
late Color accent4 = const Color(0xCCFFFFFF);
|
||||
late Color success = const Color(0xFF249689);
|
||||
late Color warning = const Color(0xFFF9CF58);
|
||||
late Color error = const Color(0xFFFF5963);
|
||||
late Color info = const Color(0xFFFFFFFF);
|
||||
|
||||
late Color trueBlue = const Color(0xFF3D6BB2);
|
||||
late Color orangePeel = const Color(0xFFF9A434);
|
||||
late Color white = const Color(0xFFFFFFFF);
|
||||
late Color columbiaBlue = const Color(0xFFC8D6E4);
|
||||
late Color battleshipGray = const Color(0xFF919191);
|
||||
late Color backgroundTransparent = const Color(0xB9FFFFFF);
|
||||
}
|
||||
|
||||
abstract class Typography {
|
||||
String get displayLargeFamily;
|
||||
bool get displayLargeIsCustom;
|
||||
TextStyle get displayLarge;
|
||||
String get displayMediumFamily;
|
||||
bool get displayMediumIsCustom;
|
||||
TextStyle get displayMedium;
|
||||
String get displaySmallFamily;
|
||||
bool get displaySmallIsCustom;
|
||||
TextStyle get displaySmall;
|
||||
String get headlineLargeFamily;
|
||||
bool get headlineLargeIsCustom;
|
||||
TextStyle get headlineLarge;
|
||||
String get headlineMediumFamily;
|
||||
bool get headlineMediumIsCustom;
|
||||
TextStyle get headlineMedium;
|
||||
String get headlineSmallFamily;
|
||||
bool get headlineSmallIsCustom;
|
||||
TextStyle get headlineSmall;
|
||||
String get titleLargeFamily;
|
||||
bool get titleLargeIsCustom;
|
||||
TextStyle get titleLarge;
|
||||
String get titleMediumFamily;
|
||||
bool get titleMediumIsCustom;
|
||||
TextStyle get titleMedium;
|
||||
String get titleSmallFamily;
|
||||
bool get titleSmallIsCustom;
|
||||
TextStyle get titleSmall;
|
||||
String get labelLargeFamily;
|
||||
bool get labelLargeIsCustom;
|
||||
TextStyle get labelLarge;
|
||||
String get labelMediumFamily;
|
||||
bool get labelMediumIsCustom;
|
||||
TextStyle get labelMedium;
|
||||
String get labelSmallFamily;
|
||||
bool get labelSmallIsCustom;
|
||||
TextStyle get labelSmall;
|
||||
String get bodyLargeFamily;
|
||||
bool get bodyLargeIsCustom;
|
||||
TextStyle get bodyLarge;
|
||||
String get bodyMediumFamily;
|
||||
bool get bodyMediumIsCustom;
|
||||
TextStyle get bodyMedium;
|
||||
String get bodySmallFamily;
|
||||
bool get bodySmallIsCustom;
|
||||
TextStyle get bodySmall;
|
||||
}
|
||||
|
||||
class ThemeTypography extends Typography {
|
||||
ThemeTypography(this.theme);
|
||||
|
||||
final AppTheme theme;
|
||||
|
||||
String get displayLargeFamily => 'Poppins';
|
||||
bool get displayLargeIsCustom => false;
|
||||
TextStyle get displayLarge => GoogleFonts.poppins(
|
||||
color: theme.primaryText,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 64.0,
|
||||
);
|
||||
String get displayMediumFamily => 'Poppins';
|
||||
bool get displayMediumIsCustom => false;
|
||||
TextStyle get displayMedium => GoogleFonts.poppins(
|
||||
color: theme.primaryText,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 44.0,
|
||||
);
|
||||
String get displaySmallFamily => 'Poppins';
|
||||
bool get displaySmallIsCustom => false;
|
||||
TextStyle get displaySmall => GoogleFonts.poppins(
|
||||
color: theme.primaryText,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 36.0,
|
||||
);
|
||||
String get headlineLargeFamily => 'Poppins';
|
||||
bool get headlineLargeIsCustom => false;
|
||||
TextStyle get headlineLarge => GoogleFonts.poppins(
|
||||
color: theme.primaryText,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 32.0,
|
||||
);
|
||||
String get headlineMediumFamily => 'Poppins';
|
||||
bool get headlineMediumIsCustom => false;
|
||||
TextStyle get headlineMedium => GoogleFonts.poppins(
|
||||
color: theme.primaryText,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 28.0,
|
||||
);
|
||||
String get headlineSmallFamily => 'Poppins';
|
||||
bool get headlineSmallIsCustom => false;
|
||||
TextStyle get headlineSmall => GoogleFonts.poppins(
|
||||
color: theme.primaryText,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 24.0,
|
||||
);
|
||||
String get titleLargeFamily => 'Poppins';
|
||||
bool get titleLargeIsCustom => false;
|
||||
TextStyle get titleLarge => GoogleFonts.poppins(
|
||||
color: theme.primaryText,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 20.0,
|
||||
);
|
||||
String get titleMediumFamily => 'Poppins';
|
||||
bool get titleMediumIsCustom => false;
|
||||
TextStyle get titleMedium => GoogleFonts.poppins(
|
||||
color: theme.primaryText,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 18.0,
|
||||
);
|
||||
String get titleSmallFamily => 'Poppins';
|
||||
bool get titleSmallIsCustom => false;
|
||||
TextStyle get titleSmall => GoogleFonts.poppins(
|
||||
color: theme.primaryText,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 16.0,
|
||||
);
|
||||
String get labelLargeFamily => 'Poppins';
|
||||
bool get labelLargeIsCustom => false;
|
||||
TextStyle get labelLarge => GoogleFonts.poppins(
|
||||
color: theme.secondaryText,
|
||||
fontWeight: FontWeight.normal,
|
||||
fontSize: 16.0,
|
||||
);
|
||||
String get labelMediumFamily => 'Poppins';
|
||||
bool get labelMediumIsCustom => false;
|
||||
TextStyle get labelMedium => GoogleFonts.poppins(
|
||||
color: theme.secondaryText,
|
||||
fontWeight: FontWeight.normal,
|
||||
fontSize: 14.0,
|
||||
);
|
||||
String get labelSmallFamily => 'Poppins';
|
||||
bool get labelSmallIsCustom => false;
|
||||
TextStyle get labelSmall => GoogleFonts.poppins(
|
||||
color: theme.secondaryText,
|
||||
fontWeight: FontWeight.normal,
|
||||
fontSize: 12.0,
|
||||
);
|
||||
String get bodyLargeFamily => 'Poppins';
|
||||
bool get bodyLargeIsCustom => false;
|
||||
TextStyle get bodyLarge => GoogleFonts.poppins(
|
||||
color: theme.primaryText,
|
||||
fontWeight: FontWeight.normal,
|
||||
fontSize: 16.0,
|
||||
);
|
||||
String get bodyMediumFamily => 'Poppins';
|
||||
bool get bodyMediumIsCustom => false;
|
||||
TextStyle get bodyMedium => GoogleFonts.poppins(
|
||||
color: theme.primaryText,
|
||||
fontWeight: FontWeight.normal,
|
||||
fontSize: 14.0,
|
||||
);
|
||||
String get bodySmallFamily => 'Poppins';
|
||||
bool get bodySmallIsCustom => false;
|
||||
TextStyle get bodySmall => GoogleFonts.poppins(
|
||||
color: theme.primaryText,
|
||||
fontWeight: FontWeight.normal,
|
||||
fontSize: 12.0,
|
||||
);
|
||||
}
|
||||
|
||||
class DarkModeTheme extends AppTheme {
|
||||
@Deprecated('Use primary instead')
|
||||
Color get primaryColor => primary;
|
||||
@Deprecated('Use secondary instead')
|
||||
Color get secondaryColor => secondary;
|
||||
@Deprecated('Use tertiary instead')
|
||||
Color get tertiaryColor => tertiary;
|
||||
|
||||
late Color primary = const Color(0xFF3D6BB2);
|
||||
late Color secondary = const Color(0xFFB36600);
|
||||
late Color tertiary = const Color(0xFFEE8B60);
|
||||
late Color alternate = const Color(0xFF262D34);
|
||||
late Color primaryText = const Color(0xFFFFFFFF);
|
||||
late Color secondaryText = const Color(0xFF919191);
|
||||
late Color primaryBackground = const Color(0xFF14181B);
|
||||
late Color secondaryBackground = const Color(0xFF1D2428);
|
||||
late Color accent1 = const Color(0xFF003EA2);
|
||||
late Color accent2 = const Color(0xFFB36600);
|
||||
late Color accent3 = const Color(0x4DEE8B60);
|
||||
late Color accent4 = const Color(0xB2262D34);
|
||||
late Color success = const Color(0xFF249689);
|
||||
late Color warning = const Color(0xFFF9CF58);
|
||||
late Color error = const Color(0xFFFF5963);
|
||||
late Color info = const Color(0xFFFFFFFF);
|
||||
|
||||
late Color trueBlue = const Color(0xFF3D6BB2);
|
||||
late Color orangePeel = const Color(0xFFF9A434);
|
||||
late Color white = const Color(0xFFFFFFFF);
|
||||
late Color columbiaBlue = const Color(0xFFC8D6E4);
|
||||
late Color battleshipGray = const Color(0xFF919191);
|
||||
late Color backgroundTransparent = const Color(0x91000000);
|
||||
}
|
||||
|
||||
extension TextStyleHelper on TextStyle {
|
||||
TextStyle override({
|
||||
TextStyle? font,
|
||||
String? fontFamily,
|
||||
Color? color,
|
||||
double? fontSize,
|
||||
FontWeight? fontWeight,
|
||||
double? letterSpacing,
|
||||
FontStyle? fontStyle,
|
||||
bool useGoogleFonts = false,
|
||||
TextDecoration? decoration,
|
||||
double? lineHeight,
|
||||
List<Shadow>? shadows,
|
||||
String? package,
|
||||
}) {
|
||||
if (useGoogleFonts && fontFamily != null) {
|
||||
font = GoogleFonts.getFont(
|
||||
fontFamily,
|
||||
fontWeight: fontWeight ?? this.fontWeight,
|
||||
fontStyle: fontStyle ?? this.fontStyle,
|
||||
);
|
||||
}
|
||||
|
||||
return font != null
|
||||
? font.copyWith(
|
||||
color: color ?? this.color,
|
||||
fontSize: fontSize ?? this.fontSize,
|
||||
letterSpacing: letterSpacing ?? this.letterSpacing,
|
||||
fontWeight: fontWeight ?? this.fontWeight,
|
||||
fontStyle: fontStyle ?? this.fontStyle,
|
||||
decoration: decoration,
|
||||
height: lineHeight,
|
||||
shadows: shadows,
|
||||
)
|
||||
: copyWith(
|
||||
fontFamily: fontFamily,
|
||||
package: package,
|
||||
color: color,
|
||||
fontSize: fontSize,
|
||||
letterSpacing: letterSpacing,
|
||||
fontWeight: fontWeight,
|
||||
fontStyle: fontStyle,
|
||||
decoration: decoration,
|
||||
height: lineHeight,
|
||||
shadows: shadows,
|
||||
);
|
||||
}
|
||||
}
|
11
lib/utils/utils.dart
Normal file
11
lib/utils/utils.dart
Normal file
@ -0,0 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
extension StatefulWidgetExtensions on State<StatefulWidget> {
|
||||
/// Check if the widget exist before safely setting state.
|
||||
void safeSetState(VoidCallback fn) {
|
||||
if (mounted) {
|
||||
// ignore: invalid_use_of_protected_member
|
||||
setState(fn);
|
||||
}
|
||||
}
|
||||
}
|
@ -9,10 +9,12 @@ import connectivity_plus
|
||||
import flutter_secure_storage_macos
|
||||
import mobile_scanner
|
||||
import path_provider_foundation
|
||||
import shared_preferences_foundation
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin"))
|
||||
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
|
||||
MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin"))
|
||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||
}
|
||||
|
64
pubspec.lock
64
pubspec.lock
@ -405,6 +405,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
google_fonts:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: google_fonts
|
||||
sha256: b1ac0fe2832c9cc95e5e88b57d627c5e68c223b9657f4b96e1487aa9098c7b82
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.2.1"
|
||||
graphs:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -709,6 +717,62 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.6.1"
|
||||
shared_preferences:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: shared_preferences
|
||||
sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.3"
|
||||
shared_preferences_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_android
|
||||
sha256: "20cbd561f743a342c76c151d6ddb93a9ce6005751e7aa458baad3858bfbfb6ac"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.10"
|
||||
shared_preferences_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_foundation
|
||||
sha256: "6a52cfcdaeac77cad8c97b539ff688ccfc458c007b4db12be584fbe5c0e49e03"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.4"
|
||||
shared_preferences_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_linux
|
||||
sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
shared_preferences_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_platform_interface
|
||||
sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
shared_preferences_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_web
|
||||
sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.3"
|
||||
shared_preferences_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_windows
|
||||
sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
shelf:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -48,6 +48,8 @@ dependencies:
|
||||
connectivity_plus: ^6.1.4
|
||||
responsive_framework: ^1.5.1
|
||||
flutter_secure_storage: ^9.2.4
|
||||
google_fonts: ^6.2.1
|
||||
shared_preferences: ^2.5.3
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
Loading…
x
Reference in New Issue
Block a user