diff --git a/packages/uni_app/lib/controller/feature_flags/feature_flag_controller.dart b/packages/uni_app/lib/controller/feature_flags/feature_flag_controller.dart new file mode 100644 index 000000000..7f0e788a1 --- /dev/null +++ b/packages/uni_app/lib/controller/feature_flags/feature_flag_controller.dart @@ -0,0 +1,90 @@ +import 'package:uni/controller/feature_flags/feature_flag_info.dart'; +import 'package:uni/controller/feature_flags/feature_flag_state_controller.dart'; +import 'package:uni/model/feature_flags/feature_flag.dart'; +import 'package:uni/model/feature_flags/feature_flag_group.dart'; +import 'package:uni/model/feature_flags/generic_feature_flag.dart'; + +class FeatureFlagController { + static final List _featureFlags = []; // To preserve order + static final Map _featureFlagsMap = + {}; // For fast lookup + static FeatureFlagStateController? _stateController; + + static void parseFeatureFlagTable( + List featureFlagInfos, + ) { + for (final featureFlagInfo in featureFlagInfos) { + final featureFlag = featureFlagInfo is FeatureFlagInfo + ? _createFeatureFlag(featureFlagInfo) + : _createFeatureFlagGroup(featureFlagInfo as FeatureFlagGroupInfo); + + _featureFlags.add(featureFlag); + _featureFlagsMap[featureFlag.code] = featureFlag; + + if (featureFlag is FeatureFlagGroup) { + for (final subFeatureFlag in featureFlag.getFeatureFlags()) { + _featureFlagsMap[subFeatureFlag.code] = subFeatureFlag; + } + } + } + } + + static bool _isEnabled(String code) { + if (_stateController == null) { + throw Exception('FeatureFlagStateController is not initialized.'); + } + + return _stateController!.isEnabled(code); + } + + static Future _saveEnabled(String code, {required bool enabled}) async { + if (_stateController == null) { + throw Exception('FeatureFlagStateController is not initialized.'); + } + + await _stateController!.saveEnabled(code, enabled: enabled); + } + + static FeatureFlag _createFeatureFlag(FeatureFlagInfo featureFlagInfo) { + final code = featureFlagInfo.code; + final getName = featureFlagInfo.getName; + + return FeatureFlag( + code: code, + getName: getName, + isEnabled: () => _isEnabled(code), + saveEnabled: ({required enabled}) => _saveEnabled(code, enabled: enabled), + ); + } + + static GenericFeatureFlag _createFeatureFlagGroup( + FeatureFlagGroupInfo featureFlagGroupInfo, + ) { + final code = featureFlagGroupInfo.code; + final getName = featureFlagGroupInfo.getName; + final featureFlags = + featureFlagGroupInfo.featureFlags.map(_createFeatureFlag).toList(); + + final featureFlagGroup = FeatureFlagGroup( + code: code, + getName: getName, + isEnabled: () => _isEnabled(code), + saveEnabled: ({required enabled}) => _saveEnabled(code, enabled: enabled), + featureFlags: featureFlags, + ); + + return featureFlagGroup; + } + + static GenericFeatureFlag? getFeatureFlag(String code) { + return _featureFlagsMap[code]; + } + + static void setStateController(FeatureFlagStateController stateController) { + _stateController = stateController; + } + + static List getFeatureFlags() { + return _featureFlags; + } +} diff --git a/packages/uni_app/lib/controller/feature_flags/feature_flag_info.dart b/packages/uni_app/lib/controller/feature_flags/feature_flag_info.dart new file mode 100644 index 000000000..f4c36a74a --- /dev/null +++ b/packages/uni_app/lib/controller/feature_flags/feature_flag_info.dart @@ -0,0 +1,28 @@ +import 'package:flutter/material.dart'; + +abstract class GenericFeatureFlagInfo { + const GenericFeatureFlagInfo({ + required this.code, + required this.getName, + }); + + final String code; + final String Function(BuildContext) getName; +} + +class FeatureFlagInfo extends GenericFeatureFlagInfo { + const FeatureFlagInfo({ + required super.code, + required super.getName, + }); +} + +class FeatureFlagGroupInfo extends GenericFeatureFlagInfo { + const FeatureFlagGroupInfo({ + required super.code, + required super.getName, + required this.featureFlags, + }); + + final List featureFlags; +} diff --git a/packages/uni_app/lib/controller/feature_flags/feature_flag_state_controller.dart b/packages/uni_app/lib/controller/feature_flags/feature_flag_state_controller.dart new file mode 100644 index 000000000..d7fec75ec --- /dev/null +++ b/packages/uni_app/lib/controller/feature_flags/feature_flag_state_controller.dart @@ -0,0 +1,18 @@ +import 'package:shared_preferences/shared_preferences.dart'; + +class FeatureFlagStateController { + FeatureFlagStateController(this.preferences); + + final SharedPreferences preferences; + static const _flagPrefix = '__feature_flag__'; + + String _getKey(String code) => '$_flagPrefix$code'; + + bool isEnabled(String code) { + return preferences.getBool(_getKey(code)) ?? false; + } + + Future saveEnabled(String code, {required bool enabled}) { + return preferences.setBool(_getKey(code), enabled); + } +} diff --git a/packages/uni_app/lib/controller/feature_flags/feature_flag_table.dart b/packages/uni_app/lib/controller/feature_flags/feature_flag_table.dart new file mode 100644 index 000000000..036495f42 --- /dev/null +++ b/packages/uni_app/lib/controller/feature_flags/feature_flag_table.dart @@ -0,0 +1,23 @@ +import 'package:uni/controller/feature_flags/feature_flag_info.dart'; +import 'package:uni/generated/l10n.dart'; + +List featureFlagTable = [ + FeatureFlagInfo( + code: 'library_modules', + getName: (context) => S.of(context).library_modules, + ), + FeatureFlagGroupInfo( + code: 'library', + getName: (context) => S.of(context).library_modules, + featureFlags: [ + FeatureFlagInfo( + code: 'library_occupation', + getName: (context) => S.of(context).library_modules, + ), + FeatureFlagInfo( + code: 'library_floors', + getName: (context) => S.of(context).library_modules, + ), + ], + ), +]; diff --git a/packages/uni_app/lib/generated/intl/messages_en.dart b/packages/uni_app/lib/generated/intl/messages_en.dart index 24826a3e3..154c062d6 100644 --- a/packages/uni_app/lib/generated/intl/messages_en.dart +++ b/packages/uni_app/lib/generated/intl/messages_en.dart @@ -131,6 +131,8 @@ class MessageLookup extends MessageLookupByLibrary { "fail_to_authenticate": MessageLookupByLibrary.simpleMessage("Failed to authenticate"), "failed_login": MessageLookupByLibrary.simpleMessage("Login failed"), + "feature_flags": + MessageLookupByLibrary.simpleMessage("Experimental features"), "fee_date": MessageLookupByLibrary.simpleMessage("Deadline for next fee:"), "fee_notification": @@ -157,6 +159,8 @@ class MessageLookup extends MessageLookupByLibrary { "language": MessageLookupByLibrary.simpleMessage("Language"), "last_refresh_time": m0, "last_timestamp": m1, + "library_modules": + MessageLookupByLibrary.simpleMessage("Library modules"), "library_occupation": MessageLookupByLibrary.simpleMessage("Library Occupation"), "load_error": MessageLookupByLibrary.simpleMessage( diff --git a/packages/uni_app/lib/generated/intl/messages_pt_PT.dart b/packages/uni_app/lib/generated/intl/messages_pt_PT.dart index 502e9f9ec..21bb764b8 100644 --- a/packages/uni_app/lib/generated/intl/messages_pt_PT.dart +++ b/packages/uni_app/lib/generated/intl/messages_pt_PT.dart @@ -130,6 +130,8 @@ class MessageLookup extends MessageLookupByLibrary { "fail_to_authenticate": MessageLookupByLibrary.simpleMessage("Falha ao autenticar"), "failed_login": MessageLookupByLibrary.simpleMessage("O login falhou"), + "feature_flags": + MessageLookupByLibrary.simpleMessage("Features experimentais"), "fee_date": MessageLookupByLibrary.simpleMessage( "Data limite próxima prestação:"), "fee_notification": @@ -156,6 +158,8 @@ class MessageLookup extends MessageLookupByLibrary { "language": MessageLookupByLibrary.simpleMessage("Idioma"), "last_refresh_time": m0, "last_timestamp": m1, + "library_modules": + MessageLookupByLibrary.simpleMessage("Módulos da biblioteca"), "library_occupation": MessageLookupByLibrary.simpleMessage("Ocupação da Biblioteca"), "load_error": MessageLookupByLibrary.simpleMessage( diff --git a/packages/uni_app/lib/generated/l10n.dart b/packages/uni_app/lib/generated/l10n.dart index 4201d66c1..cc36680b2 100644 --- a/packages/uni_app/lib/generated/l10n.dart +++ b/packages/uni_app/lib/generated/l10n.dart @@ -1697,6 +1697,26 @@ class S { args: [], ); } + + /// `Experimental features` + String get feature_flags { + return Intl.message( + 'Experimental features', + name: 'feature_flags', + desc: '', + args: [], + ); + } + + /// `Library modules` + String get library_modules { + return Intl.message( + 'Library modules', + name: 'library_modules', + desc: '', + args: [], + ); + } } class AppLocalizationDelegate extends LocalizationsDelegate { diff --git a/packages/uni_app/lib/l10n/intl_en.arb b/packages/uni_app/lib/l10n/intl_en.arb index 5e3eb22a6..5787fe644 100644 --- a/packages/uni_app/lib/l10n/intl_en.arb +++ b/packages/uni_app/lib/l10n/intl_en.arb @@ -333,5 +333,9 @@ "wrong_credentials_exception": "Invalid credentials", "@wrong_credentials_exception": {}, "internet_status_exception": "Check your internet connection", - "@internet_status_exception": {} + "@internet_status_exception": {}, + "feature_flags": "Experimental features", + "@feature_flags": {}, + "library_modules": "Library modules", + "@library_modules": {} } \ No newline at end of file diff --git a/packages/uni_app/lib/l10n/intl_pt_PT.arb b/packages/uni_app/lib/l10n/intl_pt_PT.arb index 4212fe69c..26b29d437 100644 --- a/packages/uni_app/lib/l10n/intl_pt_PT.arb +++ b/packages/uni_app/lib/l10n/intl_pt_PT.arb @@ -333,5 +333,9 @@ "wrong_credentials_exception": "Credenciais inválidas", "@wrong_credentials_exception": {}, "internet_status_exception": "Verifique sua conexão com a internet", - "@internet_status_exception": {} + "@internet_status_exception": {}, + "feature_flags": "Features experimentais", + "@feature_flags": {}, + "library_modules": "Módulos da biblioteca", + "@library_modules": {} } \ No newline at end of file diff --git a/packages/uni_app/lib/main.dart b/packages/uni_app/lib/main.dart index 9956d5e50..daf563fcf 100644 --- a/packages/uni_app/lib/main.dart +++ b/packages/uni_app/lib/main.dart @@ -14,6 +14,9 @@ import 'package:shared_preferences/shared_preferences.dart'; import 'package:ua_client_hints/ua_client_hints.dart'; import 'package:uni/controller/background_workers/background_callback.dart'; import 'package:uni/controller/cleanup.dart'; +import 'package:uni/controller/feature_flags/feature_flag_controller.dart'; +import 'package:uni/controller/feature_flags/feature_flag_state_controller.dart'; +import 'package:uni/controller/feature_flags/feature_flag_table.dart'; import 'package:uni/controller/fetchers/terms_and_conditions_fetcher.dart'; import 'package:uni/controller/local_storage/preferences_controller.dart'; import 'package:uni/generated/l10n.dart'; @@ -181,6 +184,11 @@ Future main() async { ); }, ); + + final featureFlagStateController = + FeatureFlagStateController(PreferencesController.prefs); + FeatureFlagController.setStateController(featureFlagStateController); + FeatureFlagController.parseFeatureFlagTable(featureFlagTable); } /// Manages the state of the app. diff --git a/packages/uni_app/lib/model/feature_flags/feature_flag.dart b/packages/uni_app/lib/model/feature_flags/feature_flag.dart new file mode 100644 index 000000000..a4ebde068 --- /dev/null +++ b/packages/uni_app/lib/model/feature_flags/feature_flag.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; +import 'package:uni/model/feature_flags/generic_feature_flag.dart'; + +class FeatureFlag extends GenericFeatureFlag { + FeatureFlag({ + required this.code, + required String Function(BuildContext) getName, + required bool Function() isEnabled, + required Future Function({required bool enabled}) saveEnabled, + }) : _getName = getName, + _isEnabled = isEnabled, + _saveEnabled = saveEnabled; + + @override + final String code; + final String Function(BuildContext) _getName; + final bool Function() _isEnabled; + final Future Function({required bool enabled}) _saveEnabled; + + @override + String getName(BuildContext context) => _getName(context); + + @override + bool isEnabled() => _isEnabled(); + + @override + Future setEnabled({required bool enabled}) => + _saveEnabled(enabled: enabled); +} diff --git a/packages/uni_app/lib/model/feature_flags/feature_flag_group.dart b/packages/uni_app/lib/model/feature_flags/feature_flag_group.dart new file mode 100644 index 000000000..f53310860 --- /dev/null +++ b/packages/uni_app/lib/model/feature_flags/feature_flag_group.dart @@ -0,0 +1,42 @@ +import 'package:flutter/material.dart'; +import 'package:uni/model/feature_flags/feature_flag.dart'; +import 'package:uni/model/feature_flags/generic_feature_flag.dart'; + +class FeatureFlagGroup extends GenericFeatureFlag { + FeatureFlagGroup({ + required this.code, + required String Function(BuildContext) getName, + required bool Function() isEnabled, + required Future Function({required bool enabled}) saveEnabled, + required List featureFlags, + }) : _getName = getName, + _isEnabled = isEnabled, + _saveEnabled = saveEnabled, + _featureFlags = featureFlags; + + @override + final String code; + final String Function(BuildContext) _getName; + final bool Function() _isEnabled; + final Future Function({required bool enabled}) _saveEnabled; + final List _featureFlags; + + @override + String getName(BuildContext context) => _getName(context); + + @override + bool isEnabled() => _isEnabled(); + + @override + Future setEnabled({required bool enabled}) async { + await _saveEnabled(enabled: enabled); + + for (final featureFlag in _featureFlags) { + await featureFlag.setEnabled(enabled: enabled); + } + } + + List getFeatureFlags() { + return _featureFlags; + } +} diff --git a/packages/uni_app/lib/model/feature_flags/generic_feature_flag.dart b/packages/uni_app/lib/model/feature_flags/generic_feature_flag.dart new file mode 100644 index 000000000..c402d631d --- /dev/null +++ b/packages/uni_app/lib/model/feature_flags/generic_feature_flag.dart @@ -0,0 +1,8 @@ +import 'package:flutter/material.dart'; + +abstract class GenericFeatureFlag { + String get code; + String getName(BuildContext context); + bool isEnabled(); + Future setEnabled({required bool enabled}); +} diff --git a/packages/uni_app/lib/view/common_widgets/experimental_feature_wrapper.dart b/packages/uni_app/lib/view/common_widgets/experimental_feature_wrapper.dart new file mode 100644 index 000000000..44145a855 --- /dev/null +++ b/packages/uni_app/lib/view/common_widgets/experimental_feature_wrapper.dart @@ -0,0 +1,24 @@ +import 'package:flutter/cupertino.dart'; +import 'package:uni/model/feature_flags/generic_feature_flag.dart'; + +class ExperimentalFeatureWrapper extends StatelessWidget { + const ExperimentalFeatureWrapper({ + required this.featureFlag, + required this.onEnabled, + this.onDisabled = _defaultOnDisabled, + super.key, + }); + + final GenericFeatureFlag featureFlag; + final Widget Function(BuildContext) onEnabled; + final Widget Function(BuildContext) onDisabled; + + static Widget _defaultOnDisabled(BuildContext context) { + return Container(); + } + + @override + Widget build(BuildContext context) { + return featureFlag.isEnabled() ? onEnabled(context) : onDisabled(context); + } +} diff --git a/packages/uni_app/lib/view/library/library.dart b/packages/uni_app/lib/view/library/library.dart index a86bc19c0..48000dec1 100644 --- a/packages/uni_app/lib/view/library/library.dart +++ b/packages/uni_app/lib/view/library/library.dart @@ -1,10 +1,12 @@ import 'package:flutter/material.dart'; import 'package:percent_indicator/linear_percent_indicator.dart'; import 'package:provider/provider.dart'; +import 'package:uni/controller/feature_flags/feature_flag_controller.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/library_occupation.dart'; import 'package:uni/model/providers/lazy/library_occupation_provider.dart'; import 'package:uni/utils/navigation_items.dart'; +import 'package:uni/view/common_widgets/experimental_feature_wrapper.dart'; import 'package:uni/view/common_widgets/page_title.dart'; import 'package:uni/view/common_widgets/pages_layouts/secondary/secondary.dart'; import 'package:uni/view/lazy_consumer.dart'; @@ -18,25 +20,44 @@ class LibraryPage extends StatefulWidget { } class LibraryPageState extends SecondaryPageViewState { + static final libraryFeatureFlag = + FeatureFlagController.getFeatureFlag('library')!; + static final libraryOccupationFeatureFlag = + FeatureFlagController.getFeatureFlag('library_occupation')!; + static final libraryFloorsFeatureFlag = + FeatureFlagController.getFeatureFlag('library_floors')!; + @override Widget getBody(BuildContext context) { - return ListView( - shrinkWrap: true, - children: [ - LibraryOccupationCard(), - PageTitle(name: S.of(context).floors), - LazyConsumer( - builder: getFloorRows, - hasContent: (occupation) => occupation.floors.isNotEmpty, - onNullContent: Center( - child: Text( - S.of(context).no_library_info, - style: const TextStyle(fontSize: 18), + return ExperimentalFeatureWrapper( + featureFlag: libraryFeatureFlag, + onEnabled: (context) { + return ListView( + shrinkWrap: true, + children: [ + ExperimentalFeatureWrapper( + featureFlag: libraryOccupationFeatureFlag, + onEnabled: (_) => LibraryOccupationCard(), ), - ), - contentLoadingWidget: const CircularProgressIndicator(), - ), - ], + PageTitle(name: S.of(context).floors), + ExperimentalFeatureWrapper( + featureFlag: libraryFloorsFeatureFlag, + onEnabled: (_) => + LazyConsumer( + builder: getFloorRows, + hasContent: (occupation) => occupation.floors.isNotEmpty, + onNullContent: Center( + child: Text( + S.of(context).no_library_info, + style: const TextStyle(fontSize: 18), + ), + ), + contentLoadingWidget: const CircularProgressIndicator(), + ), + ), + ], + ); + }, ); } diff --git a/packages/uni_app/lib/view/settings/settings.dart b/packages/uni_app/lib/view/settings/settings.dart index ba48f5ab7..3351cf2e2 100644 --- a/packages/uni_app/lib/view/settings/settings.dart +++ b/packages/uni_app/lib/view/settings/settings.dart @@ -4,6 +4,7 @@ import 'package:uni/generated/l10n.dart'; import 'package:uni/view/about/about.dart'; import 'package:uni/view/bug_report/bug_report.dart'; import 'package:uni/view/common_widgets/pages_layouts/secondary/secondary.dart'; +import 'package:uni/view/settings/widgets/feature_flags/feature_flags_dialog.dart'; import 'package:uni/view/settings/widgets/locale_switch_button.dart'; import 'package:uni/view/settings/widgets/notifications_dialog.dart'; import 'package:uni/view/settings/widgets/theme_switch_button.dart'; @@ -63,6 +64,14 @@ class SettingsPageState extends SecondaryPageViewState { ); }, ), + ListTile( + title: Text(S.of(context).feature_flags), + trailing: const Icon(Icons.arrow_forward_ios), + onTap: () => showDialog( + context: context, + builder: (context) => const FeatureFlagsDialog(), + ), + ), ListTile( title: Text(S.of(context).about), trailing: const Icon(Icons.arrow_forward_ios), diff --git a/packages/uni_app/lib/view/settings/widgets/feature_flags/feature_flags_dialog.dart b/packages/uni_app/lib/view/settings/widgets/feature_flags/feature_flags_dialog.dart new file mode 100644 index 000000000..80c980b88 --- /dev/null +++ b/packages/uni_app/lib/view/settings/widgets/feature_flags/feature_flags_dialog.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; +import 'package:uni/controller/feature_flags/feature_flag_controller.dart'; + +import 'package:uni/generated/l10n.dart'; +import 'package:uni/view/settings/widgets/feature_flags/feature_switch_tile.dart'; + +class FeatureFlagsDialog extends StatelessWidget { + const FeatureFlagsDialog({super.key}); + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text(S.of(context).feature_flags), + content: Column( + mainAxisSize: MainAxisSize.min, + children: FeatureFlagController.getFeatureFlags() + .map((featureFlag) => FeatureSwitchTile(featureFlag: featureFlag)) + .toList(), + ), + ); + } +} diff --git a/packages/uni_app/lib/view/settings/widgets/feature_flags/feature_switch_tile.dart b/packages/uni_app/lib/view/settings/widgets/feature_flags/feature_switch_tile.dart new file mode 100644 index 000000000..dc84c0972 --- /dev/null +++ b/packages/uni_app/lib/view/settings/widgets/feature_flags/feature_switch_tile.dart @@ -0,0 +1,64 @@ +import 'package:flutter/material.dart'; +import 'package:uni/model/feature_flags/feature_flag.dart'; +import 'package:uni/model/feature_flags/feature_flag_group.dart'; +import 'package:uni/model/feature_flags/generic_feature_flag.dart'; + +class FeatureSwitchTile extends StatefulWidget { + const FeatureSwitchTile({ + required this.featureFlag, + super.key, + }); + + final GenericFeatureFlag featureFlag; + + @override + FeatureSwitchTileState createState() => FeatureSwitchTileState(); +} + +class FeatureSwitchTileState extends State { + void refresh() { + setState(() {}); + } + + Future _onChanged(bool value) async { + await widget.featureFlag.setEnabled(enabled: value); + refresh(); + } + + @override + Widget build(BuildContext context) { + return widget.featureFlag is FeatureFlag + ? ListTile( + title: Text(widget.featureFlag.getName(context)), + trailing: Switch.adaptive( + value: widget.featureFlag.isEnabled(), + onChanged: _onChanged, + ), + ) + : Column( + children: [ + ListTile( + title: Text( + widget.featureFlag.getName(context), + ), + trailing: Switch.adaptive( + value: widget.featureFlag.isEnabled(), + onChanged: _onChanged, + ), + ), + Padding( + padding: const EdgeInsets.only(left: 24), + child: Column( + children: (widget.featureFlag as FeatureFlagGroup) + .getFeatureFlags() + .map( + (featureFlag) => + FeatureSwitchTile(featureFlag: featureFlag), + ) + .toList(), + ), + ), + ], + ); + } +} diff --git a/packages/uni_app/lib/view/settings/widgets/generic_switch.dart b/packages/uni_app/lib/view/settings/widgets/generic_switch.dart new file mode 100644 index 000000000..f49433d7a --- /dev/null +++ b/packages/uni_app/lib/view/settings/widgets/generic_switch.dart @@ -0,0 +1,37 @@ +import 'package:flutter/material.dart'; + +abstract class GenericSwitch extends StatefulWidget { + const GenericSwitch({super.key}); + + @override + GenericSwitchState createState(); +} + +abstract class GenericSwitchState extends State { + GenericSwitchState(); + + @override + void initState() { + super.initState(); + value = initializeValue(); + } + + late bool value; + bool initializeValue(); + Future storeValue({required bool value}); + + Future changeValue({required bool value}) async { + await storeValue(value: value); + setState(() { + this.value = value; + }); + } + + @override + Widget build(BuildContext context) { + return Switch.adaptive( + value: value, + onChanged: (value) => changeValue(value: value), + ); + } +} diff --git a/packages/uni_app/pubspec.lock b/packages/uni_app/pubspec.lock index 5a4c5d044..a9b4a53f7 100644 --- a/packages/uni_app/pubspec.lock +++ b/packages/uni_app/pubspec.lock @@ -69,10 +69,10 @@ packages: dependency: transitive description: name: asn1lib - sha256: "58082b3f0dca697204dbab0ef9ff208bfaea7767ea771076af9a343488428dda" + sha256: "6b151826fcc95ff246cd219a0bf4c753ea14f4081ad71c61939becf3aba27f70" url: "https://pub.dev" source: hosted - version: "1.5.3" + version: "1.5.5" async: dependency: transitive description: @@ -93,10 +93,10 @@ packages: dependency: transitive description: name: battery_plus_platform_interface - sha256: "942707f90e2f7481dcb178df02e22a9c6971b3562b848d6a1b8c7cff9f1a1fec" + sha256: e8342c0f32de4b1dfd0223114b6785e48e579bfc398da9471c9179b907fa4910 url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "2.0.1" boolean_selector: dependency: transitive description: @@ -173,26 +173,26 @@ packages: dependency: "direct main" description: name: cached_network_image - sha256: "28ea9690a8207179c319965c13cd8df184d5ee721ae2ce60f398ced1219cea1f" + sha256: "7c1183e361e5c8b0a0f21a28401eecdbde252441106a9816400dd4c2b2424916" url: "https://pub.dev" source: hosted - version: "3.3.1" + version: "3.4.1" cached_network_image_platform_interface: dependency: transitive description: name: cached_network_image_platform_interface - sha256: "9e90e78ae72caa874a323d78fa6301b3fb8fa7ea76a8f96dc5b5bf79f283bf2f" + sha256: "35814b016e37fbdc91f7ae18c8caf49ba5c88501813f73ce8a07027a395e2829" url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "4.1.1" cached_network_image_web: dependency: transitive description: name: cached_network_image_web - sha256: "205d6a9f1862de34b93184f22b9d2d94586b2f05c581d546695e3d8f6a805cd7" + sha256: "980842f4e8e2535b8dbd3d5ca0b1f0ba66bf61d14cc3a17a9b4788a3685ba062" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.3.1" characters: dependency: transitive description: @@ -277,18 +277,18 @@ packages: dependency: transitive description: name: coverage - sha256: "3945034e86ea203af7a056d98e98e42a5518fff200d6e8e6647e1886b07e936e" + sha256: c1fb2dce3c0085f39dc72668e85f8e0210ec7de05345821ff58530567df345a5 url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.9.2" crypto: dependency: "direct main" description: name: crypto - sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab + sha256: ec30d999af904f33454ba22ed9a86162b35e52b44ac4807d1d93c288041d7d27 url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.5" crypto_keys: dependency: transitive description: @@ -317,10 +317,10 @@ packages: dependency: "direct main" description: name: currency_text_input_formatter - sha256: d2eed8c7d40520729ac38252368954aba430a7ee510e5d5357e45cde1f6417a6 + sha256: a2a8ea078b211054539f9b391e87bdfc0e650e9d39b9bef4a9767b62d525b5a4 url: "https://pub.dev" source: hosted - version: "2.2.3" + version: "2.2.5" custom_lint: dependency: "direct dev" description: @@ -365,10 +365,10 @@ packages: dependency: "direct main" description: name: diacritic - sha256: "96db5db6149cbe4aa3cfcbfd170aca9b7648639be7e48025f9d458517f807fe4" + sha256: "12981945ec38931748836cd76f2b38773118d0baef3c68404bdfde9566147876" url: "https://pub.dev" source: hosted - version: "0.1.5" + version: "0.1.6" email_validator: dependency: "direct main" description: @@ -405,10 +405,10 @@ packages: dependency: transitive description: name: ffi - sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21" + sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.3" figma_squircle: dependency: transitive description: @@ -442,10 +442,10 @@ packages: dependency: "direct main" description: name: flutter_cache_manager - sha256: "395d6b7831f21f3b989ebedbb785545932adb9afe2622c1ffacf7f4b53a7e544" + sha256: "400b6592f16a4409a7f2bb929a9a7e38c72cceb8ffb99ee57bbf2cb2cecf8386" url: "https://pub.dev" source: hosted - version: "3.3.2" + version: "3.4.1" flutter_dotenv: dependency: "direct main" description: @@ -474,10 +474,10 @@ packages: dependency: transitive description: name: flutter_local_notifications_linux - sha256: "33f741ef47b5f63cc7f78fe75eeeac7e19f171ff3c3df054d84c1e38bedb6a03" + sha256: c49bd06165cad9beeb79090b18cd1eb0296f4bf4b23b84426e37dd7c027fc3af url: "https://pub.dev" source: hosted - version: "4.0.0+1" + version: "4.0.1" flutter_local_notifications_platform_interface: dependency: transitive description: @@ -593,18 +593,18 @@ packages: dependency: transitive description: name: freezed_annotation - sha256: f54946fdb1fa7b01f780841937b1a80783a20b393485f3f6cdf336fd6f4705f2 + sha256: c2e2d632dd9b8a2b7751117abcfc2b4888ecfe181bd9fca7170d9ef02e595fe2 url: "https://pub.dev" source: hosted - version: "2.4.2" + version: "2.4.4" frontend_server_client: dependency: transitive description: name: frontend_server_client - sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" + sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 url: "https://pub.dev" source: hosted - version: "3.2.0" + version: "4.0.0" glob: dependency: transitive description: @@ -617,10 +617,10 @@ packages: dependency: transitive description: name: graphs - sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19 + sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0" url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.3.2" gtk: dependency: transitive description: @@ -649,10 +649,10 @@ packages: dependency: "direct main" description: name: http - sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" + sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010 url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.2.2" http_multi_server: dependency: transitive description: @@ -713,18 +713,18 @@ packages: dependency: "direct main" description: name: json_annotation - sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" url: "https://pub.dev" source: hosted - version: "4.8.1" + version: "4.9.0" json_serializable: dependency: "direct main" description: name: json_serializable - sha256: aa1f5a8912615733e0fdc7a02af03308933c93235bdc8d50d0b0c8a8ccb0b969 + sha256: ea1432d167339ea9b5bb153f0571d0039607a873d6e04e0117af043f14a1fd4b url: "https://pub.dev" source: hosted - version: "6.7.1" + version: "6.8.0" latlong2: dependency: "direct main" description: @@ -777,10 +777,10 @@ packages: dependency: "direct main" description: name: logger - sha256: af05cc8714f356fd1f3888fb6741cbe9fbe25cdb6eedbab80e1a6db21047d4a4 + sha256: "697d067c60c20999686a0add96cf6aba723b3aa1f83ecf806a8097231529ec32" url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "2.4.0" logging: dependency: transitive description: @@ -841,10 +841,10 @@ packages: dependency: transitive description: name: mime - sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" + sha256: "801fd0b26f14a4a58ccb09d5892c3fbdeff209594300a542492cf13fba9d247a" url: "https://pub.dev" source: hosted - version: "1.0.5" + version: "1.0.6" mockito: dependency: "direct dev" description: @@ -881,10 +881,10 @@ packages: dependency: transitive description: name: octo_image - sha256: "45b40f99622f11901238e18d48f5f12ea36426d8eced9f4cbf58479c7aa2430d" + sha256: "34faa6639a78c7e3cbe79be6f9f96535867e879748ade7d17c9b1ae7536293bd" url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "2.1.0" open_file_plus: dependency: "direct main" description: @@ -914,18 +914,18 @@ packages: dependency: transitive description: name: package_info_plus - sha256: b93d8b4d624b4ea19b0a5a208b2d6eff06004bc3ce74c06040b120eeadd00ce0 + sha256: a75164ade98cb7d24cfd0a13c6408927c6b217fa60dee5a7ff5c116a58f28918 url: "https://pub.dev" source: hosted - version: "8.0.0" + version: "8.0.2" package_info_plus_platform_interface: dependency: transitive description: name: package_info_plus_platform_interface - sha256: f49918f3433a3146047372f9d4f1f847511f2acd5cd030e1f44fe5a50036b70e + sha256: ac1f4a4847f1ade8e6a87d1f39f5d7c67490738642e2542f559ec38c37489a66 url: "https://pub.dev" source: hosted - version: "3.0.0" + version: "3.0.1" path: dependency: "direct main" description: @@ -946,18 +946,18 @@ packages: dependency: "direct main" description: name: path_provider - sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161 + sha256: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378 url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.1.4" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: bca87b0165ffd7cdb9cad8edd22d18d2201e886d9a9f19b4fb3452ea7df3a72a + sha256: "6f01f8e37ec30b07bc424b4deabac37cacb1bc7e2e515ad74486039918a37eb7" url: "https://pub.dev" source: hosted - version: "2.2.6" + version: "2.2.10" path_provider_foundation: dependency: transitive description: @@ -986,10 +986,10 @@ packages: dependency: transitive description: name: path_provider_windows - sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" + sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "2.3.0" percent_indicator: dependency: "direct main" description: @@ -1098,10 +1098,10 @@ packages: dependency: transitive description: name: quiver - sha256: b1c1ac5ce6688d77f65f3375a9abb9319b3cb32486bdc7a1e0fdf004d7ba4e47 + sha256: ea0b925899e64ecdfbf9c7becb60d5b50e706ade44a85b2363be2a22d88117d2 url: "https://pub.dev" source: hosted - version: "3.2.1" + version: "3.2.2" rxdart: dependency: transitive description: @@ -1130,58 +1130,58 @@ packages: dependency: "direct main" description: name: shared_preferences - sha256: d3bbe5553a986e83980916ded2f0b435ef2e1893dfaa29d5a7a790d0eca12180 + sha256: "746e5369a43170c25816cc472ee016d3a66bc13fcf430c0bc41ad7b4b2922051" url: "https://pub.dev" source: hosted - version: "2.2.3" + version: "2.3.2" shared_preferences_android: dependency: transitive description: name: shared_preferences_android - sha256: "93d0ec9dd902d85f326068e6a899487d1f65ffcd5798721a95330b26c8131577" + sha256: "480ba4345773f56acda9abf5f50bd966f581dac5d514e5fc4a18c62976bbba7e" url: "https://pub.dev" source: hosted - version: "2.2.3" + version: "2.3.2" shared_preferences_foundation: dependency: transitive description: name: shared_preferences_foundation - sha256: "0a8a893bf4fd1152f93fec03a415d11c27c74454d96e2318a7ac38dd18683ab7" + sha256: c4b35f6cb8f63c147312c054ce7c2254c8066745125264f0c88739c417fc9d9f url: "https://pub.dev" source: hosted - version: "2.4.0" + version: "2.5.2" shared_preferences_linux: dependency: transitive description: name: shared_preferences_linux - sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa" + sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.4.1" shared_preferences_platform_interface: dependency: transitive description: name: shared_preferences_platform_interface - sha256: "22e2ecac9419b4246d7c22bfbbda589e3acf5c0351137d87dd2939d984d37c3b" + sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.4.1" shared_preferences_web: dependency: transitive description: name: shared_preferences_web - sha256: "9aee1089b36bd2aafe06582b7d7817fd317ef05fc30e6ba14bff247d0933042a" + sha256: d2ca4132d3946fec2184261726b355836a82c33d7d5b67af32692aff18a4684e url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "2.4.2" shared_preferences_windows: dependency: transitive description: name: shared_preferences_windows - sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59" + sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.4.1" shelf: dependency: transitive description: @@ -1202,10 +1202,10 @@ packages: dependency: transitive description: name: shelf_static - sha256: a41d3f53c4adf0f57480578c1d61d90342cd617de7fc8077b1304643c2d85c1e + sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3 url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.1.3" shelf_web_socket: dependency: transitive description: @@ -1247,10 +1247,10 @@ packages: dependency: transitive description: name: source_map_stack_trace - sha256: "84cf769ad83aa6bb61e0aa5a18e53aea683395f196a6f39c4c881fb90ed4f7ae" + sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" source_maps: dependency: transitive description: @@ -1303,10 +1303,10 @@ packages: dependency: transitive description: name: sqlite3 - sha256: "6d17989c0b06a5870b2190d391925186f944cb943e5262d0d3f778fcfca3bc6e" + sha256: "45f168ae2213201b54e09429ed0c593dc2c88c924a1488d6f9c523a255d567cb" url: "https://pub.dev" source: hosted - version: "2.4.4" + version: "2.4.6" stack_trace: dependency: transitive description: @@ -1423,10 +1423,10 @@ packages: dependency: "direct main" description: name: ua_client_hints - sha256: ee3da4e4b6ed211fe54deaa19832dd25613f11fc2951912d4e62a093da03fbbb + sha256: dfea54a1b4d259c057d0f33f198094cf4e09e1a21d347baadbe6dbd3d820c0d4 url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.4.0" uni_ui: dependency: "direct main" description: @@ -1470,34 +1470,34 @@ packages: dependency: transitive description: name: url_launcher_android - sha256: ceb2625f0c24ade6ef6778d1de0b2e44f2db71fded235eb52295247feba8c5cf + sha256: f0c73347dfcfa5b3db8bc06e1502668265d39c08f310c29bff4e28eea9699f79 url: "https://pub.dev" source: hosted - version: "6.3.3" + version: "6.3.9" url_launcher_ios: dependency: transitive description: name: url_launcher_ios - sha256: "7068716403343f6ba4969b4173cbf3b84fc768042124bc2c011e5d782b24fe89" + sha256: e43b677296fadce447e987a2f519dcf5f6d1e527dc35d01ffab4fff5b8a7063e url: "https://pub.dev" source: hosted - version: "6.3.0" + version: "6.3.1" url_launcher_linux: dependency: transitive description: name: url_launcher_linux - sha256: ab360eb661f8879369acac07b6bb3ff09d9471155357da8443fd5d3cf7363811 + sha256: e2b9622b4007f97f504cd64c0128309dfb978ae66adbe944125ed9e1750f06af url: "https://pub.dev" source: hosted - version: "3.1.1" + version: "3.2.0" url_launcher_macos: dependency: transitive description: name: url_launcher_macos - sha256: "9a1a42d5d2d95400c795b2914c36fdcb525870c752569438e4ebb09a2b5d90de" + sha256: "769549c999acdb42b8bcfa7c43d72bf79a382ca7441ab18a808e101149daf672" url: "https://pub.dev" source: hosted - version: "3.2.0" + version: "3.2.1" url_launcher_platform_interface: dependency: transitive description: @@ -1510,26 +1510,26 @@ packages: dependency: transitive description: name: url_launcher_web - sha256: "8d9e750d8c9338601e709cd0885f95825086bd8b642547f26bda435aade95d8a" + sha256: "772638d3b34c779ede05ba3d38af34657a05ac55b06279ea6edd409e323dca8e" url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.3.3" url_launcher_windows: dependency: transitive description: name: url_launcher_windows - sha256: ecf9725510600aa2bb6d7ddabe16357691b6d2805f66216a97d1b881e21beff7 + sha256: "49c10f879746271804767cb45551ec5592cdab00ee105c06dddde1a98f73b185" url: "https://pub.dev" source: hosted - version: "3.1.1" + version: "3.1.2" uuid: dependency: transitive description: name: uuid - sha256: "814e9e88f21a176ae1359149021870e87f7cddaf633ab678a5d2b0bff7fd1ba8" + sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff url: "https://pub.dev" source: hosted - version: "4.4.0" + version: "4.5.1" vector_graphics: dependency: transitive description: @@ -1590,18 +1590,18 @@ packages: dependency: transitive description: name: web - sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" + sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "1.1.0" web_socket_channel: dependency: transitive description: name: web_socket_channel - sha256: "58c6666b342a38816b2e7e50ed0f1e261959630becd4c879c4f26bfa14aa5a42" + sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b url: "https://pub.dev" source: hosted - version: "2.4.5" + version: "2.4.0" webkit_inspection_protocol: dependency: transitive description: @@ -1614,10 +1614,10 @@ packages: dependency: transitive description: name: win32 - sha256: a79dbe579cb51ecd6d30b17e0cae4e0ea15e2c0e66f69ad4198f22a6789e94f4 + sha256: "68d1e89a91ed61ad9c370f9f8b6effed9ae5e0ede22a270bdfa6daf79fc2290a" url: "https://pub.dev" source: hosted - version: "5.5.1" + version: "5.5.4" wkt_parser: dependency: transitive description: @@ -1638,10 +1638,10 @@ packages: dependency: transitive description: name: x509 - sha256: "3262dc9a7d45b0876f886c01bfc7d5e765704f7dfd31f8cf5224fc875c17a6c6" + sha256: cbd1a63846884afd273cda247b0365284c8d85a365ca98e110413f93d105b935 url: "https://pub.dev" source: hosted - version: "0.2.4+2" + version: "0.2.4+3" xdg_directories: dependency: transitive description: