diff --git a/coffee_maker_navigator_2/android/app/src/main/AndroidManifest.xml b/coffee_maker_navigator_2/android/app/src/main/AndroidManifest.xml index c9121d11..89c27e84 100644 --- a/coffee_maker_navigator_2/android/app/src/main/AndroidManifest.xml +++ b/coffee_maker_navigator_2/android/app/src/main/AndroidManifest.xml @@ -2,6 +2,7 @@ (context); - return MaterialApp.router( - debugShowCheckedModeBanner: false, - theme: AppThemeData.themeData(context), - routerDelegate: appLevelDependencyContainer.appRouterDelegate, - backButtonDispatcher: - appLevelDependencyContainer.backButtonDispatcher, + return AppLifecycleObserver( + child: MaterialApp.router( + debugShowCheckedModeBanner: false, + theme: AppThemeData.themeData(context), + routerDelegate: appLevelDependencyContainer.appRouterDelegate, + backButtonDispatcher: + appLevelDependencyContainer.backButtonDispatcher, + ), ); }), ); diff --git a/coffee_maker_navigator_2/lib/app/app_lifecycle/domain/app_lifecyle_service.dart b/coffee_maker_navigator_2/lib/app/app_lifecycle/domain/app_lifecyle_service.dart new file mode 100644 index 00000000..5ef6f283 --- /dev/null +++ b/coffee_maker_navigator_2/lib/app/app_lifecycle/domain/app_lifecyle_service.dart @@ -0,0 +1,25 @@ +import 'package:flutter/foundation.dart'; + +enum AppLifeCycleState { + foreground, + background, +} + +class AppLifeCycleService { + late ValueNotifier lifeCycleState; + + ValueListenable get appLifeStateListenable => + lifeCycleState; + + AppLifeCycleService() { + lifeCycleState = ValueNotifier(AppLifeCycleState.foreground); + } + + void onForegrounded() { + lifeCycleState.value = AppLifeCycleState.foreground; + } + + void onBackgrounded() { + lifeCycleState.value = AppLifeCycleState.background; + } +} diff --git a/coffee_maker_navigator_2/lib/app/app_lifecycle/ui/app_lifecycle_listenable.dart b/coffee_maker_navigator_2/lib/app/app_lifecycle/ui/app_lifecycle_listenable.dart new file mode 100644 index 00000000..e28e5005 --- /dev/null +++ b/coffee_maker_navigator_2/lib/app/app_lifecycle/ui/app_lifecycle_listenable.dart @@ -0,0 +1,46 @@ +import 'package:coffee_maker_navigator_2/app/di/coffee_maker_app_level_dependency_container.dart'; +import 'package:flutter/widgets.dart'; +import 'package:wolt_di/wolt_di.dart'; + +class AppLifecycleObserver extends StatefulWidget { + const AppLifecycleObserver({required this.child, super.key}); + + final Widget child; + + @override + State createState() => _AppLifecycleObserverState(); +} + +class _AppLifecycleObserverState extends State { + late final AppLifecycleListener listener; + + @override + void initState() { + super.initState(); + final appLevelDependencyContainer = + DependencyInjector.container( + context); + final appLifeCycleService = appLevelDependencyContainer.appLifeCycleService; + listener = AppLifecycleListener( + onShow: () { + debugPrint('AppLifecycleListener: onForegrounded'); + appLifeCycleService.onForegrounded(); + }, + onHide: () { + debugPrint('AppLifecycleListener: onBackgrounded'); + appLifeCycleService.onBackgrounded(); + }, + ); + } + + @override + void dispose() { + listener.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return widget.child; + } +} diff --git a/coffee_maker_navigator_2/lib/app/di/coffee_maker_app_level_dependency_container.dart b/coffee_maker_navigator_2/lib/app/di/coffee_maker_app_level_dependency_container.dart index 694daf1b..fc81be10 100644 --- a/coffee_maker_navigator_2/lib/app/di/coffee_maker_app_level_dependency_container.dart +++ b/coffee_maker_navigator_2/lib/app/di/coffee_maker_app_level_dependency_container.dart @@ -1,3 +1,4 @@ +import 'package:coffee_maker_navigator_2/app/app_lifecycle/domain/app_lifecyle_service.dart'; import 'package:coffee_maker_navigator_2/app/auth/data/local/auth_local_data_source.dart'; import 'package:coffee_maker_navigator_2/app/auth/data/repository/auth_repository.dart'; import 'package:coffee_maker_navigator_2/app/auth/domain/auth_service.dart'; @@ -24,6 +25,8 @@ class CoffeeMakerAppLevelDependencyContainer late final _onboardingRepository = _createOnboardingRepository(); late final _onboardingService = _createOnboardingService(); + late final _appLifeCycleService = _createAppLifeCycleService(); + late final _appRouterDelegate = _createAppRouterDelegate(); late final _backButtonDispatcher = _createRootBackButtonDispatcher(); late final _routerViewModel = _createRouterViewModel(); @@ -32,6 +35,7 @@ class CoffeeMakerAppLevelDependencyContainer BackButtonDispatcher get backButtonDispatcher => _backButtonDispatcher; RouterViewModel get routerViewModel => _routerViewModel; AuthService get authService => _authService; + AppLifeCycleService get appLifeCycleService => _appLifeCycleService; CoffeeMakerAppLevelDependencyContainer(); @@ -78,4 +82,8 @@ class CoffeeMakerAppLevelDependencyContainer onboardingService: _onboardingService, ); } + + AppLifeCycleService _createAppLifeCycleService() { + return AppLifeCycleService(); + } } diff --git a/coffee_maker_navigator_2/lib/features/add_water/di/add_water_dependency_container.dart b/coffee_maker_navigator_2/lib/features/add_water/di/add_water_dependency_container.dart index bbc776b1..b781b593 100644 --- a/coffee_maker_navigator_2/lib/features/add_water/di/add_water_dependency_container.dart +++ b/coffee_maker_navigator_2/lib/features/add_water/di/add_water_dependency_container.dart @@ -4,8 +4,7 @@ import 'package:coffee_maker_navigator_2/features/add_water/domain/add_water_ser import 'package:coffee_maker_navigator_2/features/orders/domain/orders_service.dart'; import 'package:coffee_maker_navigator_2/features/add_water/ui/view_model/add_water_view_model.dart'; -class AddWaterDependencyContainer - extends FeatureWithViewModelDependencyContainer { +class AddWaterDependencyContainer extends FeatureLevelDependencyContainer { // Just in the sake to show that we can use a method here to make the service lazily initialized. late final AddWaterService _addWaterService = _createAddWaterService(); // This is an example to non-lazy initialization. @@ -20,7 +19,6 @@ class AddWaterDependencyContainer return AddWaterService(); } - @override AddWaterViewModel createViewModel() => AddWaterViewModel( addWaterService: _addWaterService, ordersService: _ordersService, diff --git a/coffee_maker_navigator_2/lib/features/add_water/ui/view/add_water_screen.dart b/coffee_maker_navigator_2/lib/features/add_water/ui/view/add_water_screen.dart index 2d0897ee..7305c041 100644 --- a/coffee_maker_navigator_2/lib/features/add_water/ui/view/add_water_screen.dart +++ b/coffee_maker_navigator_2/lib/features/add_water/ui/view/add_water_screen.dart @@ -1,11 +1,10 @@ import 'package:coffee_maker_navigator_2/features/add_water/di/add_water_dependency_container.dart'; -import 'package:coffee_maker_navigator_2/features/add_water/domain/entities/water_source.dart'; +import 'package:coffee_maker_navigator_2/features/add_water/ui/view/widgets/add_water_screen_back_button.dart'; +import 'package:coffee_maker_navigator_2/features/add_water/ui/view/widgets/add_water_screen_content.dart'; +import 'package:coffee_maker_navigator_2/features/add_water/ui/view/widgets/add_water_screen_footer.dart'; import 'package:coffee_maker_navigator_2/features/add_water/ui/view_model/add_water_view_model.dart'; -import 'package:coffee_maker_navigator_2/utils/extensions/context_extensions.dart'; import 'package:demo_ui_components/demo_ui_components.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:wolt_di/wolt_di.dart'; class AddWaterScreen extends StatefulWidget { @@ -32,6 +31,12 @@ class _AddWaterScreenState extends State ..onInit(widget.coffeeOrderId); } + @override + void dispose() { + super.dispose(); + _viewModel.dispose(); + } + @override Widget build(BuildContext context) { return SystemUIAnnotationWrapper( @@ -41,13 +46,13 @@ class _AddWaterScreenState extends State top: false, child: Stack( children: [ - _AddWaterScreenContent( + AddWaterScreenContent( onWaterQuantityUpdated: _viewModel.onWaterQuantityUpdated, onWaterSourceUpdated: _viewModel.onWaterSourceUpdated, onWaterTemperatureUpdated: _viewModel.onWaterTemperatureUpdated, ), - const _AddWaterScreenBackButton(), - _AddWaterScreenFooter( + const AddWaterScreenBackButton(), + AddWaterScreenFooter( _viewModel.isReadyToAddWater, _viewModel.errorMessage, _viewModel.onCheckValidityPressed, @@ -60,215 +65,3 @@ class _AddWaterScreenState extends State ); } } - -class _AddWaterScreenContent extends StatelessWidget { - const _AddWaterScreenContent({ - required this.onWaterQuantityUpdated, - required this.onWaterTemperatureUpdated, - required this.onWaterSourceUpdated, - }); - - final void Function(String) onWaterQuantityUpdated; - final void Function(String) onWaterTemperatureUpdated; - final void Function(WaterSource) onWaterSourceUpdated; - - @override - Widget build(BuildContext context) { - return SingleChildScrollView( - child: Column( - children: [ - const Image( - image: AssetImage('lib/assets/images/add_water_description.png'), - fit: BoxFit.cover, - height: 200, - width: double.infinity, - ), - ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 600), - child: Padding( - padding: const EdgeInsets.all(16.0) + - const EdgeInsets.only(bottom: 200), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Add water to the coffee', - style: Theme.of(context).textTheme.headlineSmall!.copyWith( - fontWeight: FontWeight.bold, - ), - ), - const SizedBox(height: 16), - Text( - 'Enter the details to see the quality of the water you are adding to the coffee maker.', - style: Theme.of(context).textTheme.bodyLarge, - ), - const SizedBox(height: 24), - AppTextFormField( - controller: TextEditingController(), - textInputType: - const TextInputType.numberWithOptions(decimal: true), - onChanged: onWaterQuantityUpdated, - onSubmitted: onWaterQuantityUpdated, - inputFormatters: [ - /* Don't allow minus or space */ - FilteringTextInputFormatter.deny(RegExp(r'(\s|-+)')), - ], - labelText: 'Water quantity (ml)', - ), - const SizedBox(height: 16), - AppTextFormField( - controller: TextEditingController(), - textInputType: - const TextInputType.numberWithOptions(decimal: true), - onChanged: onWaterTemperatureUpdated, - onSubmitted: onWaterTemperatureUpdated, - inputFormatters: [ - /* Don't allow minus or space */ - FilteringTextInputFormatter.deny(RegExp(r'(\s|-+)')), - ], - labelText: 'Water temperature (°C)', - ), - const SizedBox(height: 24), - Text( - 'Select the water source:', - style: Theme.of(context).textTheme.titleMedium, - ), - const SizedBox(height: 8), - WoltSelectionList.singleSelect( - tilePadding: - const EdgeInsetsDirectional.symmetric(vertical: 8), - itemTileDataGroup: WoltSelectionListItemDataGroup( - group: WaterSource.values - .map( - (e) => WoltSelectionListItemData( - title: e.label, value: e, isSelected: false), - ) - .toList(), - ), - onSelectionUpdateInSingleSelectionList: (item) { - onWaterSourceUpdated(item.value); - }, - ) - ], - ), - ), - ), - ], - ), - ); - } -} - -class _AddWaterScreenBackButton extends StatelessWidget { - const _AddWaterScreenBackButton(); - - @override - Widget build(BuildContext context) { - return Align( - alignment: Alignment.topLeft, - child: Padding( - padding: const EdgeInsets.all(16.0) + - EdgeInsets.only(top: MediaQuery.paddingOf(context).top), - child: BackButton( - style: ButtonStyle( - backgroundColor: MaterialStateProperty.all(Colors.white), - ), - onPressed: Navigator.of(context).pop, - ), - ), - ); - } -} - -class _AddWaterScreenFooter extends StatelessWidget { - const _AddWaterScreenFooter( - this.isReadyToAddWater, - this.errorMessage, - this.onCheckValidity, - this.onAddWater, - ); - - final ValueListenable isReadyToAddWater; - final ValueListenable errorMessage; - final VoidCallback onCheckValidity; - final VoidCallback onAddWater; - - @override - Widget build(BuildContext context) { - return Align( - alignment: Alignment.bottomCenter, - child: ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 600), - child: Padding( - padding: const EdgeInsets.all(16.0) + - EdgeInsets.only(bottom: MediaQuery.paddingOf(context).bottom), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - ValueListenableBuilder( - valueListenable: errorMessage, - builder: (_, message, __) { - return message == null - ? const SizedBox.shrink() - : Padding( - padding: const EdgeInsets.only(bottom: 12.0), - child: _ErrorNotificationWidget(message), - ); - }), - WoltElevatedButton( - onPressed: onCheckValidity, - child: const Text('Check '), - ), - const SizedBox(height: 12), - ValueListenableBuilder( - valueListenable: isReadyToAddWater, - builder: (_, isEnabled, __) { - return WoltElevatedButton( - enabled: isEnabled, - onPressed: () { - onAddWater(); - context.routerViewModel.onAddWaterStepCompleted(); - }, - child: const Text('Add water'), - ); - }, - ), - ], - ), - ), - ), - ); - } -} - -class _ErrorNotificationWidget extends StatelessWidget { - final String message; - - const _ErrorNotificationWidget(this.message); - - @override - Widget build(BuildContext context) { - return DecoratedBox( - decoration: const ShapeDecoration( - color: WoltColors.red8, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(12)), - ), - ), - child: Padding( - padding: const EdgeInsets.all(12), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Icon(Icons.info, color: Colors.red), - const SizedBox(width: 12), - Expanded( - child: - Text(message, style: Theme.of(context).textTheme.bodyMedium), - ), - ], - ), - ), - ); - } -} diff --git a/coffee_maker_navigator_2/lib/features/add_water/ui/view/widgets/add_water_screen_back_button.dart b/coffee_maker_navigator_2/lib/features/add_water/ui/view/widgets/add_water_screen_back_button.dart new file mode 100644 index 00000000..dadb4019 --- /dev/null +++ b/coffee_maker_navigator_2/lib/features/add_water/ui/view/widgets/add_water_screen_back_button.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; + +class AddWaterScreenBackButton extends StatelessWidget { + const AddWaterScreenBackButton({super.key}); + + @override + Widget build(BuildContext context) { + return Align( + alignment: Alignment.topLeft, + child: Padding( + padding: const EdgeInsets.all(16.0) + + EdgeInsets.only(top: MediaQuery.paddingOf(context).top), + child: BackButton( + style: ButtonStyle( + backgroundColor: MaterialStateProperty.all(Colors.white), + ), + onPressed: Navigator.of(context).pop, + ), + ), + ); + } +} diff --git a/coffee_maker_navigator_2/lib/features/add_water/ui/view/widgets/add_water_screen_content.dart b/coffee_maker_navigator_2/lib/features/add_water/ui/view/widgets/add_water_screen_content.dart new file mode 100644 index 00000000..f758943d --- /dev/null +++ b/coffee_maker_navigator_2/lib/features/add_water/ui/view/widgets/add_water_screen_content.dart @@ -0,0 +1,103 @@ +import 'package:coffee_maker_navigator_2/features/add_water/domain/entities/water_source.dart'; +import 'package:demo_ui_components/demo_ui_components.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +class AddWaterScreenContent extends StatelessWidget { + const AddWaterScreenContent({ + super.key, + required this.onWaterQuantityUpdated, + required this.onWaterTemperatureUpdated, + required this.onWaterSourceUpdated, + }); + + final void Function(String) onWaterQuantityUpdated; + final void Function(String) onWaterTemperatureUpdated; + final void Function(WaterSource) onWaterSourceUpdated; + + @override + Widget build(BuildContext context) { + return SingleChildScrollView( + child: Column( + children: [ + const Image( + image: AssetImage('lib/assets/images/add_water_description.png'), + fit: BoxFit.cover, + height: 200, + width: double.infinity, + ), + ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 600), + child: Padding( + padding: const EdgeInsets.all(16.0) + + const EdgeInsets.only(bottom: 200), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Add water to the coffee', + style: Theme.of(context).textTheme.headlineSmall!.copyWith( + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 16), + Text( + 'Enter the details to see the quality of the water you are adding to the coffee maker.', + style: Theme.of(context).textTheme.bodyLarge, + ), + const SizedBox(height: 24), + AppTextFormField( + controller: TextEditingController(), + textInputType: + const TextInputType.numberWithOptions(decimal: true), + onChanged: onWaterQuantityUpdated, + onSubmitted: onWaterQuantityUpdated, + inputFormatters: [ + /* Don't allow minus or space */ + FilteringTextInputFormatter.deny(RegExp(r'(\s|-+)')), + ], + labelText: 'Water quantity (ml)', + ), + const SizedBox(height: 16), + AppTextFormField( + controller: TextEditingController(), + textInputType: + const TextInputType.numberWithOptions(decimal: true), + onChanged: onWaterTemperatureUpdated, + onSubmitted: onWaterTemperatureUpdated, + inputFormatters: [ + /* Don't allow minus or space */ + FilteringTextInputFormatter.deny(RegExp(r'(\s|-+)')), + ], + labelText: 'Water temperature (°C)', + ), + const SizedBox(height: 24), + Text( + 'Select the water source:', + style: Theme.of(context).textTheme.titleMedium, + ), + const SizedBox(height: 8), + WoltSelectionList.singleSelect( + tilePadding: + const EdgeInsetsDirectional.symmetric(vertical: 8), + itemTileDataGroup: WoltSelectionListItemDataGroup( + group: WaterSource.values + .map( + (e) => WoltSelectionListItemData( + title: e.label, value: e, isSelected: false), + ) + .toList(), + ), + onSelectionUpdateInSingleSelectionList: (item) { + onWaterSourceUpdated(item.value); + }, + ) + ], + ), + ), + ), + ], + ), + ); + } +} diff --git a/coffee_maker_navigator_2/lib/features/add_water/ui/view/widgets/add_water_screen_footer.dart b/coffee_maker_navigator_2/lib/features/add_water/ui/view/widgets/add_water_screen_footer.dart new file mode 100644 index 00000000..7f1a68c8 --- /dev/null +++ b/coffee_maker_navigator_2/lib/features/add_water/ui/view/widgets/add_water_screen_footer.dart @@ -0,0 +1,67 @@ +import 'package:coffee_maker_navigator_2/features/add_water/ui/view/widgets/error_notification_widget.dart'; +import 'package:coffee_maker_navigator_2/utils/extensions/context_extensions.dart'; +import 'package:demo_ui_components/demo_ui_components.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; + +class AddWaterScreenFooter extends StatelessWidget { + const AddWaterScreenFooter( + this.isReadyToAddWater, + this.errorMessage, + this.onCheckValidity, + this.onAddWater, { + super.key, + }); + + final ValueListenable isReadyToAddWater; + final ValueListenable errorMessage; + final VoidCallback onCheckValidity; + final VoidCallback onAddWater; + + @override + Widget build(BuildContext context) { + return Align( + alignment: Alignment.bottomCenter, + child: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 600), + child: Padding( + padding: const EdgeInsets.all(16.0) + + EdgeInsets.only(bottom: MediaQuery.paddingOf(context).bottom), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ValueListenableBuilder( + valueListenable: errorMessage, + builder: (_, message, __) { + return message == null + ? const SizedBox.shrink() + : Padding( + padding: const EdgeInsets.only(bottom: 12.0), + child: ErrorNotificationWidget(message), + ); + }), + WoltElevatedButton( + onPressed: onCheckValidity, + child: const Text('Check '), + ), + const SizedBox(height: 12), + ValueListenableBuilder( + valueListenable: isReadyToAddWater, + builder: (_, isEnabled, __) { + return WoltElevatedButton( + enabled: isEnabled, + onPressed: () { + onAddWater(); + context.routerViewModel.onAddWaterStepCompleted(); + }, + child: const Text('Add water'), + ); + }, + ), + ], + ), + ), + ), + ); + } +} diff --git a/coffee_maker_navigator_2/lib/features/add_water/ui/view/widgets/error_notification_widget.dart b/coffee_maker_navigator_2/lib/features/add_water/ui/view/widgets/error_notification_widget.dart new file mode 100644 index 00000000..22a06ba9 --- /dev/null +++ b/coffee_maker_navigator_2/lib/features/add_water/ui/view/widgets/error_notification_widget.dart @@ -0,0 +1,34 @@ +import 'package:demo_ui_components/demo_ui_components.dart'; +import 'package:flutter/material.dart'; + +class ErrorNotificationWidget extends StatelessWidget { + final String message; + + const ErrorNotificationWidget(this.message, {super.key}); + + @override + Widget build(BuildContext context) { + return DecoratedBox( + decoration: const ShapeDecoration( + color: WoltColors.red8, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(12)), + ), + ), + child: Padding( + padding: const EdgeInsets.all(12), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Icon(Icons.info, color: Colors.red), + const SizedBox(width: 12), + Expanded( + child: + Text(message, style: Theme.of(context).textTheme.bodyMedium), + ), + ], + ), + ), + ); + } +} diff --git a/coffee_maker_navigator_2/lib/features/add_water/ui/view_model/add_water_view_model.dart b/coffee_maker_navigator_2/lib/features/add_water/ui/view_model/add_water_view_model.dart index 1f88af53..dff4da0d 100644 --- a/coffee_maker_navigator_2/lib/features/add_water/ui/view_model/add_water_view_model.dart +++ b/coffee_maker_navigator_2/lib/features/add_water/ui/view_model/add_water_view_model.dart @@ -4,9 +4,8 @@ import 'package:coffee_maker_navigator_2/features/add_water/domain/entities/wate import 'package:coffee_maker_navigator_2/features/orders/domain/entities/coffee_maker_step.dart'; import 'package:coffee_maker_navigator_2/features/orders/domain/orders_service.dart'; import 'package:flutter/foundation.dart'; -import 'package:wolt_di/wolt_di.dart'; -class AddWaterViewModel extends WoltViewModel { +class AddWaterViewModel { final AddWaterService _addWaterService; final OrdersService _ordersService; String _waterQuantityInMl = ''; @@ -14,8 +13,11 @@ class AddWaterViewModel extends WoltViewModel { WaterSource _waterSource = WaterSource.tap; late String _orderId; - final ValueNotifier isReadyToAddWater = ValueNotifier(false); + final ValueNotifier _isReadyToAddWater = ValueNotifier(false); + ValueListenable get isReadyToAddWater => _isReadyToAddWater; + final ValueNotifier _errorMessage = ValueNotifier(null); + ValueListenable get errorMessage => _errorMessage; AddWaterViewModel({ required AddWaterService addWaterService, @@ -60,16 +62,11 @@ class AddWaterViewModel extends WoltViewModel { } } - @override void dispose() { - isReadyToAddWater.dispose(); + _isReadyToAddWater.dispose(); _errorMessage.dispose(); } - ValueListenable get isAddWaterButtonEnabled => isReadyToAddWater; - - ValueListenable get errorMessage => _errorMessage; - // Method to check the validity of the current state void onCheckValidityPressed() { _errorMessage.value = null; // Clear previous errors @@ -79,7 +76,7 @@ class AddWaterViewModel extends WoltViewModel { if (quantity == null || temperature == null) { _errorMessage.value = "Invalid numeric values for quantity or temperature."; - isReadyToAddWater.value = false; + _isReadyToAddWater.value = false; return; } @@ -89,7 +86,7 @@ class AddWaterViewModel extends WoltViewModel { waterSource: _waterSource, currentDate: DateTime.now(), ); - isReadyToAddWater.value = result.isAccepted; + _isReadyToAddWater.value = result.isAccepted; if (!result.isAccepted) { _errorMessage.value = result.message; } diff --git a/coffee_maker_navigator_2/lib/features/login/di/login_screen_dependency_container.dart b/coffee_maker_navigator_2/lib/features/login/di/login_screen_dependency_container.dart index 038ec4a8..bbcacd45 100644 --- a/coffee_maker_navigator_2/lib/features/login/di/login_screen_dependency_container.dart +++ b/coffee_maker_navigator_2/lib/features/login/di/login_screen_dependency_container.dart @@ -3,8 +3,7 @@ import 'package:coffee_maker_navigator_2/app/auth/domain/auth_service.dart'; import 'package:coffee_maker_navigator_2/features/login/ui/view_model/login_screen_view_model.dart'; import 'package:wolt_di/wolt_di.dart'; -class LoginScreenDependencyContainer - extends FeatureWithViewModelDependencyContainer { +class LoginScreenDependencyContainer extends FeatureLevelDependencyContainer { late final AuthService _authService; LoginScreenDependencyContainer() { @@ -14,7 +13,6 @@ class LoginScreenDependencyContainer } // ViewModel should always be created lazily. - @override LoginScreenViewModel createViewModel() { return LoginScreenViewModel(authService: _authService); } diff --git a/coffee_maker_navigator_2/lib/features/login/ui/view/login_screen.dart b/coffee_maker_navigator_2/lib/features/login/ui/view/login_screen.dart index 42f4ed32..d4166daa 100644 --- a/coffee_maker_navigator_2/lib/features/login/ui/view/login_screen.dart +++ b/coffee_maker_navigator_2/lib/features/login/ui/view/login_screen.dart @@ -1,6 +1,6 @@ import 'package:coffee_maker_navigator_2/features/login/di/login_screen_dependency_container.dart'; +import 'package:coffee_maker_navigator_2/features/login/ui/view/widgets/login_screen_content.dart'; import 'package:coffee_maker_navigator_2/features/login/ui/view_model/login_screen_view_model.dart'; -import 'package:demo_ui_components/demo_ui_components.dart'; import 'package:flutter/material.dart'; import 'package:wolt_di/wolt_di.dart'; @@ -13,57 +13,24 @@ class LoginScreen extends StatefulWidget { class _LoginScreenState extends State with - FeatureWithViewModelDependencyContainerSubscriptionMixin< - LoginScreenDependencyContainer, LoginScreenViewModel, LoginScreen> { + DependencyContainerSubscriptionMixin { + late LoginScreenViewModel _viewModel; + @override - Widget build(BuildContext context) { - final textTheme = Theme.of(context).textTheme; + void initState() { + super.initState(); + final container = + DependencyInjector.container(context); + _viewModel = container.createViewModel(); + } - return Scaffold( - body: SafeArea( - child: Center( - child: SingleChildScrollView( - padding: const EdgeInsets.symmetric(horizontal: 40), - child: ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 400), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Image( - image: AssetImage('lib/assets/images/dash_coffee.webp'), - fit: BoxFit.cover, - height: 216, - width: 384, - ), - Text( - 'Welcome to Coffee Maker!', - style: textTheme.titleLarge!, - ), - const SizedBox(height: 50), - const AppTextFormField( - labelText: 'Username', - textInputType: TextInputType.emailAddress, - ), - const SizedBox(height: 20), - const AppTextFormField( - labelText: 'Password', - obscureText: true, - autocorrect: false, - textInputType: TextInputType.visiblePassword, - ), - const SizedBox(height: 30), - WoltElevatedButton( - onPressed: () { - viewModel.onLoginPressed('email', 'password'); - }, - child: const Text('Sign in'), - ), - ], - ), - ), - ), - ), - ), + @override + Widget build(BuildContext context) { + return LoginScreenContent( + onLoginPressed: (email, password) { + _viewModel.onLoginPressed(email, password); + }, ); } } diff --git a/coffee_maker_navigator_2/lib/features/login/ui/view/widgets/login_screen_content.dart b/coffee_maker_navigator_2/lib/features/login/ui/view/widgets/login_screen_content.dart new file mode 100644 index 00000000..4a82415a --- /dev/null +++ b/coffee_maker_navigator_2/lib/features/login/ui/view/widgets/login_screen_content.dart @@ -0,0 +1,58 @@ +import 'package:demo_ui_components/demo_ui_components.dart'; +import 'package:flutter/material.dart'; + +class LoginScreenContent extends StatelessWidget { + const LoginScreenContent({super.key, required this.onLoginPressed}); + + final void Function(String, String) onLoginPressed; + + @override + Widget build(BuildContext context) { + return Scaffold( + body: SafeArea( + child: Center( + child: SingleChildScrollView( + padding: const EdgeInsets.symmetric(horizontal: 40), + child: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 400), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Image( + image: AssetImage('lib/assets/images/dash_coffee.webp'), + fit: BoxFit.cover, + height: 216, + width: 384, + ), + Text( + 'Welcome to Coffee Maker!', + style: Theme.of(context).textTheme.titleLarge!, + ), + const SizedBox(height: 50), + const AppTextFormField( + labelText: 'Username', + textInputType: TextInputType.emailAddress, + ), + const SizedBox(height: 20), + const AppTextFormField( + labelText: 'Password', + obscureText: true, + autocorrect: false, + textInputType: TextInputType.visiblePassword, + ), + const SizedBox(height: 30), + WoltElevatedButton( + onPressed: () { + onLoginPressed('email', 'password'); + }, + child: const Text('Sign in'), + ), + ], + ), + ), + ), + ), + ), + ); + } +} diff --git a/coffee_maker_navigator_2/lib/features/login/ui/view_model/login_screen_view_model.dart b/coffee_maker_navigator_2/lib/features/login/ui/view_model/login_screen_view_model.dart index 80eb0a7c..418c1853 100644 --- a/coffee_maker_navigator_2/lib/features/login/ui/view_model/login_screen_view_model.dart +++ b/coffee_maker_navigator_2/lib/features/login/ui/view_model/login_screen_view_model.dart @@ -1,7 +1,6 @@ import 'package:coffee_maker_navigator_2/app/auth/domain/auth_service.dart'; -import 'package:wolt_di/wolt_di.dart'; -class LoginScreenViewModel extends WoltViewModel { +class LoginScreenViewModel { final AuthService authService; LoginScreenViewModel({required this.authService}); @@ -9,7 +8,4 @@ class LoginScreenViewModel extends WoltViewModel { Future onLoginPressed(String email, String password) async { await authService.logIn(email, password); } - - @override - void dispose() {} } diff --git a/coffee_maker_navigator_2/lib/features/orders/di/orders_dependency_container.dart b/coffee_maker_navigator_2/lib/features/orders/di/orders_dependency_container.dart index 19cc28bf..39d7c78c 100644 --- a/coffee_maker_navigator_2/lib/features/orders/di/orders_dependency_container.dart +++ b/coffee_maker_navigator_2/lib/features/orders/di/orders_dependency_container.dart @@ -1,11 +1,11 @@ +import 'package:coffee_maker_navigator_2/app/di/coffee_maker_app_level_dependency_container.dart'; import 'package:coffee_maker_navigator_2/features/orders/data/remote/orders_remote_data_source.dart'; import 'package:coffee_maker_navigator_2/features/orders/data/repository/orders_repository.dart'; import 'package:coffee_maker_navigator_2/features/orders/domain/orders_service.dart'; import 'package:coffee_maker_navigator_2/features/orders/ui/view_model/orders_screen_view_model.dart'; import 'package:wolt_di/wolt_di.dart'; -class OrdersDependencyContainer - extends FeatureWithViewModelDependencyContainer { +class OrdersDependencyContainer extends FeatureLevelDependencyContainer { late final OrdersRemoteDataSource _ordersRemoteDataSource; late final OrdersRepository _ordersRepository; late final OrdersService _ordersService; @@ -19,10 +19,14 @@ class OrdersDependencyContainer _ordersRepository = OrdersRepository( ordersRemoteDataSource: _ordersRemoteDataSource, ); - _ordersService = OrdersService(ordersRepository: _ordersRepository); + final appLevelDependencies = + bindWith(); + _ordersService = OrdersService( + ordersRepository: _ordersRepository, + appLifeCycleService: appLevelDependencies.appLifeCycleService, + ); } - @override OrdersScreenViewModel createViewModel() { return OrdersScreenViewModel(ordersService: _ordersService); } diff --git a/coffee_maker_navigator_2/lib/features/orders/domain/orders_service.dart b/coffee_maker_navigator_2/lib/features/orders/domain/orders_service.dart index aa7d9e31..93b98e57 100644 --- a/coffee_maker_navigator_2/lib/features/orders/domain/orders_service.dart +++ b/coffee_maker_navigator_2/lib/features/orders/domain/orders_service.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:coffee_maker_navigator_2/app/app_lifecycle/domain/app_lifecyle_service.dart'; import 'package:coffee_maker_navigator_2/features/orders/data/repository/orders_repository.dart'; import 'package:coffee_maker_navigator_2/features/orders/domain/entities/coffee_maker_step.dart'; import 'package:coffee_maker_navigator_2/features/orders/domain/entities/coffee_order.dart'; @@ -15,8 +16,12 @@ class OrdersService { OrdersService({ required OrdersRepository ordersRepository, + required AppLifeCycleService appLifeCycleService, }) : _ordersRepository = ordersRepository { _startPolling(); + appLifeCycleService.appLifeStateListenable.addListener(() { + _onAppLifeCycleChange(appLifeCycleService.appLifeStateListenable.value); + }); } ValueListenable> get orders => _orders; @@ -49,6 +54,17 @@ class OrdersService { } } + void _onAppLifeCycleChange(AppLifeCycleState appLifeCycleState) { + switch (appLifeCycleState) { + case AppLifeCycleState.foreground: + _startPolling(); + break; + case AppLifeCycleState.background: + _stopPolling(); + break; + } + } + /// Starts polling for order updates. void _startPolling() { _pollingTimer?.cancel(); // Cancel any existing timer diff --git a/coffee_maker_navigator_2/lib/features/orders/ui/view/orders_screen.dart b/coffee_maker_navigator_2/lib/features/orders/ui/view/orders_screen.dart index 6b50cce2..077f0b0c 100644 --- a/coffee_maker_navigator_2/lib/features/orders/ui/view/orders_screen.dart +++ b/coffee_maker_navigator_2/lib/features/orders/ui/view/orders_screen.dart @@ -1,22 +1,10 @@ -import 'package:coffee_maker_navigator_2/app/router/view_model/router_view_model.dart'; -import 'package:coffee_maker_navigator_2/app/ui/widgets/app_navigation_drawer.dart'; import 'package:coffee_maker_navigator_2/features/orders/di/orders_dependency_container.dart'; import 'package:coffee_maker_navigator_2/features/orders/domain/entities/coffee_maker_step.dart'; -import 'package:coffee_maker_navigator_2/features/orders/ui/view/widgets/coffee_order_list_view_for_step.dart'; -import 'package:coffee_maker_navigator_2/features/orders/ui/view/widgets/orders_screen_bottom_navigation_bar.dart'; +import 'package:coffee_maker_navigator_2/features/orders/ui/view/widgets/order_screen_content.dart'; import 'package:coffee_maker_navigator_2/features/orders/ui/view_model/orders_screen_view_model.dart'; -import 'package:coffee_maker_navigator_2/features/orders/ui/widgets/top_bar.dart'; -import 'package:coffee_maker_navigator_2/utils/extensions/context_extensions.dart'; -import 'package:demo_ui_components/demo_ui_components.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:wolt_di/wolt_di.dart'; -typedef OnCoffeeOrderUpdate = void Function(String coffeeOrderId); - -typedef OnOrderScreenBottomNavBarItemSelected = void Function( - CoffeeMakerStep selectedStep); - class OrdersScreen extends StatefulWidget { const OrdersScreen(this.destinationBottomNavBarTab, {super.key}); @@ -28,77 +16,31 @@ class OrdersScreen extends StatefulWidget { class _OrdersScreenState extends State with - FeatureWithViewModelDependencyContainerSubscriptionMixin< - OrdersDependencyContainer, OrdersScreenViewModel, OrdersScreen> { - late ValueListenable selectedStepListenable; + DependencyContainerSubscriptionMixin { + late OrdersScreenViewModel viewModel; @override void initState() { super.initState(); - viewModel.onInit(widget.destinationBottomNavBarTab); - selectedStepListenable = viewModel.selectedBottomNavBarItem; + viewModel = DependencyInjector.container(context) + .createViewModel() + ..onInit(widget.destinationBottomNavBarTab); } @override - Widget build(BuildContext context) { - return SystemUIAnnotationWrapper( - hasBottomNavigationBar: true, - child: Scaffold( - body: SafeArea( - child: ValueListenableBuilder( - valueListenable: selectedStepListenable, - builder: (context, selectedTab, _) { - return Column( - children: [ - TopBar(selectedTab: selectedTab), - Expanded( - child: ValueListenableBuilder( - valueListenable: viewModel.groupedCoffeeOrders, - builder: (context, orders, _) { - final routerViewModel = context.routerViewModel; - return CoffeeOrderListViewForStep( - groupedCoffeeOrders: orders, - selectedBottomNavBarItem: selectedTab, - onGrindCoffeeStepSelected: (id) => - _onGrindCoffeeStepSelected(routerViewModel, id), - onAddWaterCoffeeStepSelected: - routerViewModel.onAddWaterCoffeeStepSelected, - onReadyCoffeeStepSelected: (id) => - _onReadyCoffeeStepSelected(routerViewModel, id), - ); - }, - ), - ), - ], - ); - }, - ), - ), - drawer: const AppNavigationDrawer(selectedIndex: 0), - bottomNavigationBar: OrdersScreenBottomNavigationBar( - viewModel.groupedCoffeeOrders, - viewModel.onBottomNavBarItemSelected, - viewModel.selectedBottomNavBarItem, - ), - ), - ); + void dispose() { + super.dispose(); + viewModel.dispose(); } - void _onGrindCoffeeStepSelected(RouterViewModel routerViewModel, String id) { - routerViewModel.onGrindCoffeeStepSelected( - id: id, - onCoffeeGrindCompleted: () => viewModel.onOrderStatusChange( - id, - CoffeeMakerStep.addWater, - ), - onCoffeeGrindRejected: () => viewModel.onOrderStatusChange(id), - ); - } - - void _onReadyCoffeeStepSelected(RouterViewModel routerViewModel, String id) { - routerViewModel.onReadyCoffeeStepSelected( - id: id, - onCoffeeServed: () => viewModel.onOrderStatusChange(id), + @override + Widget build(BuildContext context) { + return OrderScreenContent( + selectedStepListenable: viewModel.selectedBottomNavBarItem, + groupedCoffeeOrders: viewModel.groupedCoffeeOrders, + onBottomNavBarItemSelected: viewModel.onBottomNavBarItemSelected, + onOrderStatusChange: viewModel.onOrderStatusChange, ); } } diff --git a/coffee_maker_navigator_2/lib/features/orders/ui/view/widgets/coffee_order_list_view_for_step.dart b/coffee_maker_navigator_2/lib/features/orders/ui/view/widgets/coffee_order_list_view_for_step.dart index 4d34b160..9132e591 100644 --- a/coffee_maker_navigator_2/lib/features/orders/ui/view/widgets/coffee_order_list_view_for_step.dart +++ b/coffee_maker_navigator_2/lib/features/orders/ui/view/widgets/coffee_order_list_view_for_step.dart @@ -1,8 +1,8 @@ import 'package:coffee_maker_navigator_2/features/orders/domain/entities/coffee_maker_step.dart'; import 'package:coffee_maker_navigator_2/features/orders/domain/entities/coffee_order.dart'; import 'package:coffee_maker_navigator_2/features/orders/domain/entities/grouped_coffee_orders.dart'; -import 'package:coffee_maker_navigator_2/features/orders/ui/view/orders_screen.dart'; import 'package:coffee_maker_navigator_2/features/orders/ui/view/widgets/coffee_order_list_item_tile.dart'; +import 'package:coffee_maker_navigator_2/features/orders/ui/view/widgets/order_screen_content.dart'; import 'package:flutter/material.dart'; class CoffeeOrderListViewForStep extends StatelessWidget { diff --git a/coffee_maker_navigator_2/lib/features/orders/ui/view/widgets/order_screen_content.dart b/coffee_maker_navigator_2/lib/features/orders/ui/view/widgets/order_screen_content.dart new file mode 100644 index 00000000..b2cd74db --- /dev/null +++ b/coffee_maker_navigator_2/lib/features/orders/ui/view/widgets/order_screen_content.dart @@ -0,0 +1,93 @@ +import 'package:coffee_maker_navigator_2/app/router/view_model/router_view_model.dart'; +import 'package:coffee_maker_navigator_2/app/ui/widgets/app_navigation_drawer.dart'; +import 'package:coffee_maker_navigator_2/features/orders/domain/entities/coffee_maker_step.dart'; +import 'package:coffee_maker_navigator_2/features/orders/domain/entities/grouped_coffee_orders.dart'; +import 'package:coffee_maker_navigator_2/features/orders/ui/view/widgets/coffee_order_list_view_for_step.dart'; +import 'package:coffee_maker_navigator_2/features/orders/ui/view/widgets/orders_screen_bottom_navigation_bar.dart'; +import 'package:coffee_maker_navigator_2/features/orders/ui/widgets/top_bar.dart'; +import 'package:coffee_maker_navigator_2/utils/extensions/context_extensions.dart'; +import 'package:demo_ui_components/demo_ui_components.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +typedef OnCoffeeOrderUpdate = void Function(String coffeeOrderId); + +typedef OnCoffeeOrderStatusChange = void Function(String coffeeOrderId, + [CoffeeMakerStep? coffeeMakerStep]); + +typedef OnOrderScreenBottomNavBarItemSelected = void Function( + CoffeeMakerStep selectedStep); + +class OrderScreenContent extends StatelessWidget { + const OrderScreenContent({ + super.key, + required this.selectedStepListenable, + required this.groupedCoffeeOrders, + required this.onBottomNavBarItemSelected, + required this.onOrderStatusChange, + }); + + final ValueListenable selectedStepListenable; + final ValueListenable groupedCoffeeOrders; + final OnOrderScreenBottomNavBarItemSelected onBottomNavBarItemSelected; + final OnCoffeeOrderStatusChange onOrderStatusChange; + + @override + Widget build(BuildContext context) { + return SystemUIAnnotationWrapper( + hasBottomNavigationBar: true, + child: Scaffold( + body: SafeArea( + child: ValueListenableBuilder( + valueListenable: selectedStepListenable, + builder: (context, selectedTab, _) { + return Column( + children: [ + TopBar(selectedTab: selectedTab), + Expanded( + child: ValueListenableBuilder( + valueListenable: groupedCoffeeOrders, + builder: (context, orders, _) { + final routerViewModel = context.routerViewModel; + return CoffeeOrderListViewForStep( + groupedCoffeeOrders: orders, + selectedBottomNavBarItem: selectedTab, + onGrindCoffeeStepSelected: (id) => + _onGrindCoffeeStepSelected(routerViewModel, id), + onAddWaterCoffeeStepSelected: + routerViewModel.onAddWaterCoffeeStepSelected, + onReadyCoffeeStepSelected: (id) => + _onReadyCoffeeStepSelected(routerViewModel, id), + ); + }, + ), + ), + ], + ); + }, + ), + ), + drawer: const AppNavigationDrawer(selectedIndex: 0), + bottomNavigationBar: OrdersScreenBottomNavigationBar( + groupedCoffeeOrders, + onBottomNavBarItemSelected, + selectedStepListenable, + ), + ), + ); + } + + void _onGrindCoffeeStepSelected(RouterViewModel routerViewModel, String id) { + routerViewModel.onGrindCoffeeStepSelected( + id: id, + onCoffeeGrindCompleted: () => + onOrderStatusChange(id, CoffeeMakerStep.addWater), + onCoffeeGrindRejected: () => onOrderStatusChange(id), + ); + } + + void _onReadyCoffeeStepSelected(RouterViewModel routerViewModel, String id) { + routerViewModel.onReadyCoffeeStepSelected( + id: id, onCoffeeServed: () => onOrderStatusChange(id)); + } +} diff --git a/coffee_maker_navigator_2/lib/features/orders/ui/view/widgets/orders_screen_bottom_navigation_bar.dart b/coffee_maker_navigator_2/lib/features/orders/ui/view/widgets/orders_screen_bottom_navigation_bar.dart index 054ff8ec..94c2fa3a 100644 --- a/coffee_maker_navigator_2/lib/features/orders/ui/view/widgets/orders_screen_bottom_navigation_bar.dart +++ b/coffee_maker_navigator_2/lib/features/orders/ui/view/widgets/orders_screen_bottom_navigation_bar.dart @@ -1,6 +1,6 @@ import 'package:coffee_maker_navigator_2/features/orders/domain/entities/coffee_maker_step.dart'; import 'package:coffee_maker_navigator_2/features/orders/domain/entities/grouped_coffee_orders.dart'; -import 'package:coffee_maker_navigator_2/features/orders/ui/view/orders_screen.dart'; +import 'package:coffee_maker_navigator_2/features/orders/ui/view/widgets/order_screen_content.dart'; import 'package:coffee_maker_navigator_2/features/orders/ui/widgets/coffee_maker_custom_divider.dart'; import 'package:demo_ui_components/demo_ui_components.dart'; import 'package:flutter/foundation.dart'; diff --git a/coffee_maker_navigator_2/lib/features/orders/ui/view_model/orders_screen_view_model.dart b/coffee_maker_navigator_2/lib/features/orders/ui/view_model/orders_screen_view_model.dart index 97e98923..e9a414de 100644 --- a/coffee_maker_navigator_2/lib/features/orders/ui/view_model/orders_screen_view_model.dart +++ b/coffee_maker_navigator_2/lib/features/orders/ui/view_model/orders_screen_view_model.dart @@ -1,43 +1,50 @@ import 'package:coffee_maker_navigator_2/features/orders/domain/entities/coffee_maker_step.dart'; import 'package:coffee_maker_navigator_2/features/orders/domain/entities/grouped_coffee_orders.dart'; import 'package:coffee_maker_navigator_2/features/orders/domain/orders_service.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:wolt_di/wolt_di.dart'; +import 'package:flutter/foundation.dart'; -class OrdersScreenViewModel implements WoltViewModel { - final OrdersService _ordersService; - final groupedCoffeeOrders = ValueNotifier(GroupedCoffeeOrders.empty()); - final ValueNotifier selectedBottomNavBarItem = - ValueNotifier(CoffeeMakerStep.grind); - - OrdersScreenViewModel({ - required OrdersService ordersService, - }) : _ordersService = ordersService { +class OrdersScreenViewModel { + OrdersScreenViewModel({required OrdersService ordersService}) + : _ordersService = ordersService { final currentOrders = _ordersService.orders.value; - groupedCoffeeOrders.value = + _groupedCoffeeOrders.value = GroupedCoffeeOrders.fromCoffeeOrders(currentOrders); _ordersService.orders.addListener(_onOrdersReceived); } + final OrdersService _ordersService; + + final _groupedCoffeeOrders = ValueNotifier(GroupedCoffeeOrders.empty()); + + ValueListenable get groupedCoffeeOrders => + _groupedCoffeeOrders; + + final ValueNotifier _selectedBottomNavBarItem = + ValueNotifier(CoffeeMakerStep.grind); + + ValueListenable get selectedBottomNavBarItem => + _selectedBottomNavBarItem; + void onInit(CoffeeMakerStep? initialNavBarItem) { - selectedBottomNavBarItem.value = initialNavBarItem ?? CoffeeMakerStep.grind; + if (initialNavBarItem != null) { + _selectedBottomNavBarItem.value = initialNavBarItem; + } } - @override void dispose() { _ordersService.orders.removeListener(_onOrdersReceived); } - void _onOrdersReceived() { - final orders = _ordersService.orders.value; - groupedCoffeeOrders.value = GroupedCoffeeOrders.fromCoffeeOrders(orders); - } - void onBottomNavBarItemSelected(CoffeeMakerStep selectedStep) { - selectedBottomNavBarItem.value = selectedStep; + _selectedBottomNavBarItem.value = selectedStep; } void onOrderStatusChange(String orderId, [CoffeeMakerStep? newStep]) { _ordersService.updateOrder(orderId, newStep); } + + void _onOrdersReceived() { + final orders = _ordersService.orders.value; + _groupedCoffeeOrders.value = GroupedCoffeeOrders.fromCoffeeOrders(orders); + } } diff --git a/wolt_di/lib/src/view_model/feature_with_view_model_dependency_container.dart b/wolt_di/lib/src/view_model/feature_with_view_model_dependency_container.dart deleted file mode 100644 index ae88751f..00000000 --- a/wolt_di/lib/src/view_model/feature_with_view_model_dependency_container.dart +++ /dev/null @@ -1,8 +0,0 @@ -import 'package:wolt_di/wolt_di.dart'; - -/// An abstract base class for a feature level dependency container that provides a view model. -abstract class FeatureWithViewModelDependencyContainer - extends FeatureLevelDependencyContainer { - /// Creates a new view model instance. - VM createViewModel(); -} diff --git a/wolt_di/lib/src/view_model/feature_with_view_model_dependency_container_subscription_mixin.dart b/wolt_di/lib/src/view_model/feature_with_view_model_dependency_container_subscription_mixin.dart deleted file mode 100644 index e23b5c3e..00000000 --- a/wolt_di/lib/src/view_model/feature_with_view_model_dependency_container_subscription_mixin.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:flutter/widgets.dart'; -import 'package:wolt_di/wolt_di.dart'; - -mixin FeatureWithViewModelDependencyContainerSubscriptionMixin< - C extends FeatureWithViewModelDependencyContainer, - VM extends WoltViewModel, - T extends StatefulWidget> on State - implements DependencyContainerSubscriber { - late DependencyInjector _injector; - late VM _viewModel; - - VM get viewModel => _viewModel; - - @override - void initState() { - super.initState(); - _injector = DependencyInjector.of(context); - _injector.subscribeToDependencyContainer(this); - _viewModel = - DependencyInjector.container(context).createViewModel() as VM; - } - - @override - void dispose() { - _injector.unsubscribeFromDependencyContainer(this); - _viewModel.dispose(); - super.dispose(); - } -} diff --git a/wolt_di/lib/src/view_model/wolt_view_model.dart b/wolt_di/lib/src/view_model/wolt_view_model.dart deleted file mode 100644 index 32903c41..00000000 --- a/wolt_di/lib/src/view_model/wolt_view_model.dart +++ /dev/null @@ -1,8 +0,0 @@ -import 'package:flutter/widgets.dart'; - -abstract class WoltViewModel { - /// Discards any resources used by the view model classes. This method should called in the - /// dispose method of the [StatefulWidget]. After this is called, the object is not in a usable - /// state. - void dispose(); -} diff --git a/wolt_di/lib/wolt_di.dart b/wolt_di/lib/wolt_di.dart index 126c515a..3ac1aaa0 100644 --- a/wolt_di/lib/wolt_di.dart +++ b/wolt_di/lib/wolt_di.dart @@ -3,7 +3,4 @@ export 'src/manager/dependency_container_manager.dart' hide DependencyContainerResolver, DependencyContainerRegistrar; export 'src/subscriber/dependency_container_subscription_mixin.dart'; export 'src/subscriber/dependency_container_subscriber.dart'; -export 'src/view_model/feature_with_view_model_dependency_container.dart'; -export 'src/view_model/wolt_view_model.dart'; -export 'src/view_model/feature_with_view_model_dependency_container_subscription_mixin.dart'; export 'src/dependency_injector.dart';