diff --git a/open_wearable/lib/main.dart b/open_wearable/lib/main.dart index 70bead86..a5aea512 100644 --- a/open_wearable/lib/main.dart +++ b/open_wearable/lib/main.dart @@ -10,6 +10,7 @@ import 'package:open_wearable/router.dart'; import 'package:open_wearable/view_models/sensor_recorder_provider.dart'; import 'package:open_wearable/widgets/app_banner.dart'; import 'package:open_wearable/widgets/global_app_banner_overlay.dart'; +import 'package:open_wearable/widgets/welcome_wizard/theme_settings.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -27,6 +28,7 @@ void main() async { runApp( MultiProvider( providers: [ + ChangeNotifierProvider(create: (context) => ThemeSettings()), ChangeNotifierProvider(create: (context) => WearablesProvider()), ChangeNotifierProvider( create: (context) => FirmwareUpdateRequestProvider(), @@ -71,115 +73,138 @@ class _MyAppState extends State with WidgetsBindingObserver { _unsupportedFirmwareSub = wearablesProvider.unsupportedFirmwareStream.listen((evt) { - // No async/await here. No widget context usage either. - final nav = rootNavigatorKey.currentState; - if (nav == null || !mounted) return; + // No async/await here. No widget context usage either. + final nav = rootNavigatorKey.currentState; + if (nav == null || !mounted) return; - // Push a dialog route via NavigatorState (no BuildContext from this widget) - nav.push( - DialogRoute( - context: rootNavigatorKey - .currentContext!, // from navigator, not this widget - barrierDismissible: true, - builder: (_) => PlatformAlertDialog( - title: const Text('Firmware unsupported'), - content: getUnsupportedAlertText(evt), - actions: [ - PlatformDialogAction( - cupertino: (_, __) => - CupertinoDialogActionData(isDefaultAction: true), - child: const Text('OK'), - // Close via navigator state; no widget context - onPressed: () => rootNavigatorKey.currentState?.pop(), - ), - ], - ), - ), - ); - }); + // Push a dialog route via NavigatorState (no BuildContext from this widget) + nav.push( + DialogRoute( + context: rootNavigatorKey + .currentContext!, // from navigator, not this widget + barrierDismissible: true, + builder: (_) => + PlatformAlertDialog( + title: const Text('Firmware unsupported'), + content: getUnsupportedAlertText(evt), + actions: [ + PlatformDialogAction( + cupertino: (_, __) => + CupertinoDialogActionData(isDefaultAction: true), + child: const Text('OK'), + // Close via navigator state; no widget context + onPressed: () => rootNavigatorKey.currentState?.pop(), + ), + ], + ), + ), + ); + }); - _wearableProvEventSub = wearablesProvider.wearableEventStream.listen((event) { - if (!mounted) return; + _wearableProvEventSub = + wearablesProvider.wearableEventStream.listen((event) { + if (!mounted) return; - // Handle firmware update available events with a dialog - if (event is NewFirmwareAvailableEvent) { - final nav = rootNavigatorKey.currentState; - if (nav == null || !mounted) return; + // Handle firmware update available events with a dialog + if (event is NewFirmwareAvailableEvent) { + final nav = rootNavigatorKey.currentState; + if (nav == null || !mounted) return; - nav.push( - DialogRoute( - context: rootNavigatorKey.currentContext!, - barrierDismissible: true, - builder: (dialogContext) => PlatformAlertDialog( - title: const Text('Firmware Update Available'), - content: Text( - 'A newer firmware version (${event.latestVersion}) is available. You are using version ${event.currentVersion}.', + nav.push( + DialogRoute( + context: rootNavigatorKey.currentContext!, + barrierDismissible: true, + builder: (dialogContext) => + PlatformAlertDialog( + title: const Text('Firmware Update Available'), + content: Text( + 'A newer firmware version (${event + .latestVersion}) is available. You are using version ${event + .currentVersion}.', + ), + actions: [ + PlatformDialogAction( + cupertino: (_, __) => CupertinoDialogActionData(), + child: const Text('Later'), + onPressed: () => rootNavigatorKey.currentState?.pop(), + ), + PlatformDialogAction( + cupertino: (_, __) => + CupertinoDialogActionData(isDefaultAction: true), + child: const Text('Update Now'), + onPressed: () { + // Set the selected peripheral for firmware update + final updateProvider = Provider.of< + FirmwareUpdateRequestProvider>( + rootNavigatorKey.currentContext!, + listen: false, + ); + updateProvider.setSelectedPeripheral( + event.wearable); + rootNavigatorKey.currentState?.pop(); + rootNavigatorKey.currentContext?.push('/fota'); + }, + ), + ], + ), ), - actions: [ - PlatformDialogAction( - cupertino: (_, __) => CupertinoDialogActionData(), - child: const Text('Later'), - onPressed: () => rootNavigatorKey.currentState?.pop(), - ), - PlatformDialogAction( - cupertino: (_, __) => - CupertinoDialogActionData(isDefaultAction: true), - child: const Text('Update Now'), - onPressed: () { - // Set the selected peripheral for firmware update - final updateProvider = Provider.of( - rootNavigatorKey.currentContext!, - listen: false, - ); - updateProvider.setSelectedPeripheral(event.wearable); - rootNavigatorKey.currentState?.pop(); - rootNavigatorKey.currentContext?.push('/fota'); - }, - ), - ], - ), - ), - ); - return; - } - - // Show a banner for other events using AppBannerController - final appBannerController = context.read(); - appBannerController.showBanner( - (id) { - late final Color backgroundColor; - if (event is WearableErrorEvent) { - backgroundColor = Theme.of(context).colorScheme.error; - } else { - backgroundColor = Theme.of(context).colorScheme.primary; + ); + return; } - late final Color textColor; - if (event is WearableErrorEvent) { - textColor = Theme.of(context).colorScheme.onError; - } else { - textColor = Theme.of(context).colorScheme.onPrimary; - } + // Show a banner for other events using AppBannerController + final appBannerController = context.read(); + appBannerController.showBanner( + (id) { + late final Color backgroundColor; + if (event is WearableErrorEvent) { + backgroundColor = Theme + .of(context) + .colorScheme + .error; + } else { + backgroundColor = Theme + .of(context) + .colorScheme + .primary; + } - return AppBanner( - content: Text( - event.description, - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: textColor, - ), - ), - backgroundColor: backgroundColor, - key: ValueKey(id), + late final Color textColor; + if (event is WearableErrorEvent) { + textColor = Theme + .of(context) + .colorScheme + .onError; + } else { + textColor = Theme + .of(context) + .colorScheme + .onPrimary; + } + + return AppBanner( + content: Text( + event.description, + style: Theme + .of(context) + .textTheme + .bodyMedium + ?.copyWith( + color: textColor, + ), + ), + backgroundColor: backgroundColor, + key: ValueKey(id), + ); + }, + duration: const Duration(seconds: 3), ); - }, - duration: const Duration(seconds: 3), - ); - }); + }); final WearableConnector connector = context.read(); final SensorRecorderProvider sensorRecorderProvider = - context.read(); + context.read(); _autoConnector = BluetoothAutoConnector( navStateGetter: () => rootNavigatorKey.currentState, wearableManager: WearableManager(), @@ -215,17 +240,17 @@ class _MyAppState extends State with WidgetsBindingObserver { if (evt is FirmwareTooOldEvent) { return const Text( 'The device has a firmware version that is too old and not supported by this app. ' - 'Please update the device firmware to ensure all features are working as expected.', + 'Please update the device firmware to ensure all features are working as expected.', ); } else if (evt is FirmwareTooNewEvent) { return const Text( 'The device has a firmware version that is too new and not supported by this app. ' - 'Please update the app to ensure all features are working as expected.', + 'Please update the app to ensure all features are working as expected.', ); } else { return const Text( 'The device has a firmware unsupported by this app. ' - 'Please update the app and Firmware to the newest version to ensure all features are working as expected.', + 'Please update the app and Firmware to the newest version to ensure all features are working as expected.', ); } } @@ -242,31 +267,58 @@ class _MyAppState extends State with WidgetsBindingObserver { @override Widget build(BuildContext context) { + final themeNotifier = Provider.of(context); + return PlatformProvider( settings: PlatformSettingsData( iosUsesMaterialWidgets: true, ), - builder: (context) => PlatformTheme( - materialLightTheme: ThemeData( - useMaterial3: true, - colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue), - cardTheme: const CardThemeData( - color: Colors.white, - elevation: 0, - ), - ), - builder: (context) => GlobalAppBannerOverlay( - child: PlatformApp.router( - routerConfig: router, - localizationsDelegates: const >[ - DefaultMaterialLocalizations.delegate, - DefaultWidgetsLocalizations.delegate, - DefaultCupertinoLocalizations.delegate, - ], - title: 'Open Wearable', + builder: (context) => + PlatformTheme( + materialLightTheme: ThemeData( + useMaterial3: true, + colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue), + cardTheme: const CardThemeData( + color: Colors.white, + elevation: 0, + ), + ), + builder: (context) => + GlobalAppBannerOverlay( + child: PlatformApp.router( + routerConfig: router, + localizationsDelegates: const < + LocalizationsDelegate>[ + DefaultMaterialLocalizations.delegate, + DefaultWidgetsLocalizations.delegate, + DefaultCupertinoLocalizations.delegate, + ], + title: 'Open Wearable', + + // ✅ Router variant => return MaterialAppRouterData + material: (_, __) => + MaterialAppRouterData( + themeMode: themeNotifier.themeMode, + theme: ThemeData( + useMaterial3: true, + colorScheme: ColorScheme.fromSeed( + seedColor: Colors.blue), + cardTheme: const CardThemeData( + color: Colors.white, + elevation: 0, + ), + ), + darkTheme: ThemeData( + useMaterial3: true, + colorScheme: ColorScheme.fromSeed( + seedColor: Colors.blue, + brightness: Brightness.dark, + ), + ), + ), + ), + ), ), - ), - ), ); } -} +} \ No newline at end of file diff --git a/open_wearable/lib/widgets/home_page.dart b/open_wearable/lib/widgets/home_page.dart index 54bb8ccf..b0209c9e 100644 --- a/open_wearable/lib/widgets/home_page.dart +++ b/open_wearable/lib/widgets/home_page.dart @@ -4,6 +4,8 @@ import 'package:open_wearable/apps/widgets/apps_page.dart'; import 'package:open_wearable/widgets/devices/devices_page.dart'; import 'package:open_wearable/widgets/sensors/configuration/sensor_configuration_view.dart'; import 'package:open_wearable/widgets/sensors/values/sensor_values_page.dart'; +import 'package:open_wearable/widgets/welcome_wizard/startup_wizard.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import 'sensors/sensor_page.dart'; @@ -50,6 +52,7 @@ class _HomePageState extends State { SensorPage(), const AppsPage(), ]; + _showStartupWizardOnFirstBoot(); } @override @@ -111,4 +114,26 @@ class _HomePageState extends State { items: items(context), ); } + + void _showStartupWizardOnFirstBoot() async { + final prefs = await SharedPreferences.getInstance(); + + // uncomment this line on release so infobox will only be shown once + //final shown = prefs.getBool('info_box_shown') ?? false; + final shown = false; + + if (!shown) { + // Delay to ensure context is ready + WidgetsBinding.instance.addPostFrameCallback((_) { + showDialog( + context: context, + barrierDismissible: false, + builder: (_) => const StartupWizard(), + ).then((_) async { + // Mark as shown + await prefs.setBool('info_box_shown', true); + }); + }); + } + } } diff --git a/open_wearable/lib/widgets/welcome_wizard/assets/encrypted.png b/open_wearable/lib/widgets/welcome_wizard/assets/encrypted.png new file mode 100644 index 00000000..ec12a435 Binary files /dev/null and b/open_wearable/lib/widgets/welcome_wizard/assets/encrypted.png differ diff --git a/open_wearable/lib/widgets/welcome_wizard/assets/teco-logo.png b/open_wearable/lib/widgets/welcome_wizard/assets/teco-logo.png new file mode 100644 index 00000000..2dfa757c Binary files /dev/null and b/open_wearable/lib/widgets/welcome_wizard/assets/teco-logo.png differ diff --git a/open_wearable/lib/widgets/welcome_wizard/startup_wizard.dart b/open_wearable/lib/widgets/welcome_wizard/startup_wizard.dart new file mode 100644 index 00000000..34eb3180 --- /dev/null +++ b/open_wearable/lib/widgets/welcome_wizard/startup_wizard.dart @@ -0,0 +1,226 @@ +import 'package:flutter/material.dart'; +import 'package:open_wearable/widgets/welcome_wizard/theme_settings.dart'; +import 'package:provider/provider.dart'; + +class StartupWizard extends StatefulWidget { + const StartupWizard({Key? key}) : super(key: key); + + @override + State createState() => _StartupWizardDialogState(); +} + +class _StartupWizardDialogState extends State { + int _currentStep = 0; + + late final List steps = [ + // Step 1: Welcome Screen + Column( + children: [ + // TECO Logo Placeholder + Container( + width: 64, + height: 64, + decoration: BoxDecoration( + color: Colors.grey[200], + borderRadius: BorderRadius.circular(8), + ), + alignment: Alignment.center, + child: Image.asset( + 'lib/widgets/welcome_wizard/assets/teco-logo.png', + width: 40, + height: 40, + fit: BoxFit.contain, + ), + ), + const SizedBox(height: 24), + const Text( + 'Welcome to OpenWearable', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 18, + letterSpacing: 0.5, + ), + ), + const SizedBox(height: 18), + const Text( + "Get ready to connect and use your wearable device with advanced posture and heart tracking. This quick setup will guide you through everything you need.", + style: TextStyle(fontSize: 14), + textAlign: TextAlign.center, + ), + ], + ), + // Step 2: Theme Selection + Column( + children: [ + Container( + width: 64, + height: 64, + decoration: BoxDecoration( + color: Colors.grey[200], + borderRadius: BorderRadius.circular(8), + ), + alignment: Alignment.center, + child: Icon(Icons.palette_outlined, size: 36, color: Colors.grey[700]), + ), + const SizedBox(height: 24), + const Text( + 'Choose Your Theme', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 18, + ), + ), + const SizedBox(height: 16), + const Text( + "Pick your favorite look. You can always change this later in the settings.", + style: TextStyle(fontSize: 14), + textAlign: TextAlign.center, + ), + const SizedBox(height: 32), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + OutlinedButton.icon( + icon: const Icon(Icons.light_mode), + label: const Text('Light'), + style: OutlinedButton.styleFrom( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12), + textStyle: const TextStyle(fontSize: 15, fontWeight: FontWeight.w500), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + onPressed: () { + Provider.of(context, listen: false).setTheme(ThemeMode.light); + }, + ), + const SizedBox(width: 18), + ElevatedButton.icon( + icon: const Icon(Icons.dark_mode), + label: const Text('Dark'), + style: ElevatedButton.styleFrom( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12), + textStyle: const TextStyle(fontSize: 15, fontWeight: FontWeight.w500), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + onPressed: () { + Provider.of(context, listen: false).setTheme(ThemeMode.dark); + }, + ), + ], + ), + ], + ), + // Step 3: Data is Info Page + Column( + children: [ + Container( + width: 64, + height: 64, + decoration: BoxDecoration( + border: Border.all(), + borderRadius: BorderRadius.circular(8), + ), + alignment: Alignment.center, + child: Image.asset( + 'lib/widgets/welcome_wizard/assets/encrypted.png', + width: 40, + height: 40, + fit: BoxFit.contain, + ), + ), + const SizedBox(height: 24), + const Text( + 'Your Data, Your Device, 100% Secure', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16, + letterSpacing: 0.5, + ), + ), + const SizedBox(height: 24), + const Text( + "We respect your privacy. All data is stored locally on your device never shared, and never uploaded to the cloud. You're always in control.", + style: TextStyle(fontSize: 14), + textAlign: TextAlign.center, + ), + const SizedBox(height: 24), + ], + ) + ]; + + void _next() { + if (_currentStep < steps.length - 1) { + setState(() => _currentStep++); + } else { + Navigator.of(context).pop(); + } + } + + void _back() { + if (_currentStep > 0) { + setState(() => _currentStep--); + } + } + + @override + Widget build(BuildContext context) { + return Dialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + child: Padding( + padding: const EdgeInsets.all(24.0), + child: SizedBox( + width: 320, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + steps[_currentStep], + const SizedBox(height: 28), + // Progress dots + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: List.generate( + steps.length, + (index) => Container( + margin: const EdgeInsets.symmetric(horizontal: 4), + width: _currentStep == index ? 14 : 8, + height: 8, + decoration: BoxDecoration( + color: _currentStep == index + ? Theme.of(context).colorScheme.primary + : Colors.grey[400], + borderRadius: BorderRadius.circular(4), + ), + ), + ), + ), + const SizedBox(height: 28), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + if (_currentStep > 0) + OutlinedButton( + onPressed: _back, + child: const Text('Back'), + ) + else + const SizedBox(width: 80), + ElevatedButton( + onPressed: _next, + child: Text( + _currentStep == steps.length - 1 ? 'Finish' : 'Next', + ), + ), + ], + ), + ], + ), + ), + ), + ); + } +} \ No newline at end of file diff --git a/open_wearable/lib/widgets/welcome_wizard/theme_settings.dart b/open_wearable/lib/widgets/welcome_wizard/theme_settings.dart new file mode 100644 index 00000000..8922561d --- /dev/null +++ b/open_wearable/lib/widgets/welcome_wizard/theme_settings.dart @@ -0,0 +1,47 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +class ThemeSettings extends ChangeNotifier { + ThemeMode _themeMode = ThemeMode.light; + ThemeMode get themeMode => _themeMode; + + static const String themeKey = 'theme_mode'; + + ThemeSettings() { + _loadThemeFromPrefs(); + } + + void setTheme(ThemeMode mode) async { + _themeMode = mode; + notifyListeners(); + _saveThemeToPrefs(); + } + + Future _loadThemeFromPrefs() async { + final prefs = await SharedPreferences.getInstance(); + final themeString = prefs.getString(themeKey); + + if (themeString == 'dark') { + _themeMode = ThemeMode.dark; + } else if (themeString == 'light') { + _themeMode = ThemeMode.light; + } else if (themeString == 'system') { + _themeMode = ThemeMode.system; + } // else default to light + notifyListeners(); + } + + Future _saveThemeToPrefs() async { + final prefs = await SharedPreferences.getInstance(); + String themeString; + if (_themeMode == ThemeMode.dark) { + themeString = 'dark'; + } else if (_themeMode == ThemeMode.light) { + themeString = 'light'; + } else { + themeString = 'system'; + } + await prefs.setString(themeKey, themeString); + } +} \ No newline at end of file diff --git a/open_wearable/pubspec.lock b/open_wearable/pubspec.lock index 0458b180..b16c18f9 100644 --- a/open_wearable/pubspec.lock +++ b/open_wearable/pubspec.lock @@ -157,34 +157,34 @@ packages: dependency: "direct main" description: name: file_picker - sha256: "7872545770c277236fd32b022767576c562ba28366204ff1a5628853cf8f2200" + sha256: d974b6ba2606371ac71dd94254beefb6fa81185bde0b59bdc1df09885da85fde url: "https://pub.dev" source: hosted - version: "10.3.7" + version: "10.3.8" file_selector: dependency: "direct main" description: name: file_selector - sha256: bd15e43e9268db636b53eeaca9f56324d1622af30e5c34d6e267649758c84d9a + sha256: "5f1d15a7f17115038f433d1b0ea57513cc9e29a9d5338d166cb0bef3fa90a7a0" url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.0.4" file_selector_android: dependency: transitive description: name: file_selector_android - sha256: "51e8fd0446de75e4b62c065b76db2210c704562d072339d333bd89c57a7f8a7c" + sha256: "1ce58b609289551f8ec07265476720e77d19764339cc1d8e4df3c4d34dac6499" url: "https://pub.dev" source: hosted - version: "0.5.2+4" + version: "0.5.1+17" file_selector_ios: dependency: transitive description: name: file_selector_ios - sha256: "628ec99afd8bb40620b4c8707d5fd5fc9e89d83e9b0b327d471fe5f7bc5fc33f" + sha256: fe9f52123af16bba4ad65bd7e03defbbb4b172a38a8e6aaa2a869a0c56a5f5fb url: "https://pub.dev" source: hosted - version: "0.5.3+4" + version: "0.5.3+2" file_selector_linux: dependency: transitive description: @@ -197,10 +197,10 @@ packages: dependency: transitive description: name: file_selector_macos - sha256: "5e0bbe9c312416f1787a68259ea1505b52f258c587f12920422671807c4d618a" + sha256: "19124ff4a3d8864fdc62072b6a2ef6c222d55a3404fe14893a3c02744907b60c" url: "https://pub.dev" source: hosted - version: "0.9.5" + version: "0.9.4+4" file_selector_platform_interface: dependency: transitive description: @@ -237,10 +237,10 @@ packages: dependency: "direct main" description: name: fl_chart - sha256: "7ca9a40f4eb85949190e54087be8b4d6ac09dc4c54238d782a34cf1f7c011de9" + sha256: d3f82f4a38e33ba23d05a08ff304d7d8b22d2a59a5503f20bd802966e915db89 url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.0" flutter: dependency: "direct main" description: flutter @@ -290,10 +290,10 @@ packages: dependency: transitive description: name: flutter_plugin_android_lifecycle - sha256: ee8068e0e1cd16c4a82714119918efdeed33b3ba7772c54b5d094ab53f9b7fd1 + sha256: c2fe1001710127dfa7da89977a08d591398370d099aacdaa6d44da7eb14b8476 url: "https://pub.dev" source: hosted - version: "2.0.33" + version: "2.0.31" flutter_staggered_grid_view: dependency: "direct main" description: @@ -372,26 +372,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" + sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" url: "https://pub.dev" source: hosted - version: "11.0.2" + version: "10.0.9" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" + sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 url: "https://pub.dev" source: hosted - version: "3.0.10" + version: "3.0.9" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "3.0.1" lints: dependency: transitive description: @@ -444,10 +444,10 @@ packages: dependency: transitive description: name: meta - sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.16.0" mime: dependency: transitive description: @@ -564,18 +564,18 @@ packages: dependency: transitive description: name: path_provider_android - sha256: f2c65e21139ce2c3dad46922be8272bb5963516045659e71bb16e151c93b580e + sha256: "3b4c1fc3aa55ddc9cd4aa6759984330d5c8e66aa7702a6223c61540dc6380c37" url: "https://pub.dev" source: hosted - version: "2.2.22" + version: "2.2.19" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "6d13aece7b3f5c5a9731eaf553ff9dcbc2eff41087fd2df587fd0fed9a3eb0c4" + sha256: "16eef174aacb07e09c351502740fa6254c165757638eba1e9116b0a781201bbd" url: "https://pub.dev" source: hosted - version: "2.5.1" + version: "2.4.2" path_provider_linux: dependency: transitive description: @@ -732,18 +732,18 @@ packages: dependency: transitive description: name: shared_preferences_android - sha256: "46a46fd64659eff15f4638bbe19de43f9483f0e0bf024a9fb6b3582064bacc7b" + sha256: bd14436108211b0d4ee5038689a56d4ae3620fd72fd6036e113bf1345bc74d9e url: "https://pub.dev" source: hosted - version: "2.4.17" + version: "2.4.13" shared_preferences_foundation: dependency: transitive description: name: shared_preferences_foundation - sha256: "4e7eaffc2b17ba398759f1151415869a34771ba11ebbccd1b0145472a619a64f" + sha256: "6a52cfcdaeac77cad8c97b539ff688ccfc458c007b4db12be584fbe5c0e49e03" url: "https://pub.dev" source: hosted - version: "2.5.6" + version: "2.5.4" shared_preferences_linux: dependency: transitive description: @@ -825,10 +825,10 @@ packages: dependency: transitive description: name: test_api - sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55 + sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd url: "https://pub.dev" source: hosted - version: "0.7.7" + version: "0.7.4" tuple: dependency: transitive description: @@ -865,18 +865,18 @@ packages: dependency: transitive description: name: url_launcher_android - sha256: "767344bf3063897b5cf0db830e94f904528e6dd50a6dfaf839f0abf509009611" + sha256: "81777b08c498a292d93ff2feead633174c386291e35612f8da438d6e92c4447e" url: "https://pub.dev" source: hosted - version: "6.3.28" + version: "6.3.20" url_launcher_ios: dependency: transitive description: name: url_launcher_ios - sha256: cfde38aa257dae62ffe79c87fab20165dfdf6988c1d31b58ebf59b9106062aad + sha256: d80b3f567a617cb923546034cc94bfe44eb15f989fe670b37f26abdb9d939cb7 url: "https://pub.dev" source: hosted - version: "6.3.6" + version: "6.3.4" url_launcher_linux: dependency: transitive description: @@ -889,10 +889,10 @@ packages: dependency: transitive description: name: url_launcher_macos - sha256: "368adf46f71ad3c21b8f06614adb38346f193f3a59ba8fe9a2fd74133070ba18" + sha256: c043a77d6600ac9c38300567f33ef12b0ef4f4783a2c1f00231d2b1941fea13f url: "https://pub.dev" source: hosted - version: "3.2.5" + version: "3.2.3" url_launcher_platform_interface: dependency: transitive description: @@ -950,7 +950,7 @@ packages: source: hosted version: "1.1.19" vector_math: - dependency: transitive + dependency: "direct overridden" description: name: vector_math sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b @@ -961,10 +961,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60" + sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 url: "https://pub.dev" source: hosted - version: "15.0.2" + version: "15.0.0" web: dependency: transitive description: @@ -998,5 +998,5 @@ packages: source: hosted version: "6.6.1" sdks: - dart: ">=3.9.0 <4.0.0" - flutter: ">=3.35.0" + dart: ">=3.8.0 <4.0.0" + flutter: ">=3.32.0"