From bd74ef18800a78807ec13b634617bac0f45006f1 Mon Sep 17 00:00:00 2001 From: michael kalango Date: Wed, 16 Nov 2022 12:52:31 +0100 Subject: [PATCH 1/2] feat: material 3 --- lib/core/stores/slip_store.dart | 55 ++++++++++++------- lib/features/advice/advice_view.dart | 16 +++--- .../advice/view_model/advice_viewmodel.dart | 42 ++++++++++---- lib/features/advices/advices_view.dart | 2 +- .../advices/view_model/advices_viewmodel.dart | 15 ++++- lib/main.dart | 1 + pubspec.lock | 31 ++++------- 7 files changed, 102 insertions(+), 60 deletions(-) diff --git a/lib/core/stores/slip_store.dart b/lib/core/stores/slip_store.dart index 52ba644..062b725 100644 --- a/lib/core/stores/slip_store.dart +++ b/lib/core/stores/slip_store.dart @@ -17,33 +17,48 @@ class SlipStore { } Future addAdvice({Slip? slip}) async { - _log.i('adding slip: ${slip?.id}'); - await _databaseService.database!.insert(_slipTable, slip!.toJson()); + try { + _log.i('adding slip: ${slip?.id}'); + await _databaseService.database!.insert(_slipTable, slip!.toJson()); + } catch (e) { + _log.e('error adding slip'); + return; + } } Future removeAdvice({Slip? slip}) async { - _log.i('removing slip: ${slip?.id}'); - await _databaseService.database! - .delete(_slipTable, where: "id = ?", whereArgs: [slip?.id]); + try { + _log.i('removing slip: ${slip?.id}'); + await _databaseService.database! + .delete(_slipTable, where: "id = ?", whereArgs: [slip?.id]); + } catch (e) { + _log.e('error deleting slip'); + return; + } } Future isSaved({int? slipId}) async { - _log.i('checking saved state for $slipId'); - bool status = false; - var records = await _databaseService.database!.query( - _slipTable, - where: "id = ?", - whereArgs: [slipId], - ); - var data = records.map((e) => Slip.fromJson(e)).toList(); - if (data.isEmpty) { - status = false; - _log.i('status: $status'); - } else { - status = true; - _log.i('status: $status'); + try { + _log.i('checking saved state for $slipId'); + bool status = false; + var records = await _databaseService.database!.query( + _slipTable, + where: "id = ?", + whereArgs: [slipId], + ); + var data = records.map((e) => Slip.fromJson(e)).toList(); + if (data.isEmpty) { + status = false; + _log.i('status: $status'); + } else { + status = true; + _log.i('status: $status'); + } + return status; + } catch (e) { + _log.e('error checking state'); + return false; } - return status; } Stream isStreamedSaved({String? slipId}) async* { diff --git a/lib/features/advice/advice_view.dart b/lib/features/advice/advice_view.dart index f2308bd..f5dff8b 100644 --- a/lib/features/advice/advice_view.dart +++ b/lib/features/advice/advice_view.dart @@ -75,18 +75,16 @@ class AdviceView extends StatelessWidget { style: theme.textTheme.bodyLarge, ), ), - SizedBox( - height: SizeMg.height(30), - ), - IconButton( - onPressed: () { - model.actionSaveAdvice(slip: slip); - }, - icon: const Icon(Icons.save), - ), ], ), ), + floatingActionButton: FloatingActionButton( + tooltip: 'Save Advice', + onPressed: () { + model.actionconfirmSave(slip: slip); + }, + child: const Icon(Icons.save), + ), ); }, ); diff --git a/lib/features/advice/view_model/advice_viewmodel.dart b/lib/features/advice/view_model/advice_viewmodel.dart index 9adce96..274cf46 100644 --- a/lib/features/advice/view_model/advice_viewmodel.dart +++ b/lib/features/advice/view_model/advice_viewmodel.dart @@ -9,24 +9,34 @@ import '../../../core/stores/slip_store.dart'; class AdviceViewModel extends BaseViewModel { final _snackbarService = locator(); + final _dialogService = locator(); final _slipStore = locator(); final _log = getLogger('AdviceViewModel'); - void actionSaveAdvice({Slip? slip}) async { - _log.i('adding advice'); - if (await _slipStore.isSaved(slipId: slip?.id)) { + void _actionSaveAdvice({Slip? slip}) async { + try { + _log.i('adding advice'); + if (await _slipStore.isSaved(slipId: slip?.id)) { + return _handleSnackbar( + title: 'Error', + message: 'An advice with the ID: ${slip?.id} has already been saved.', + variant: SnackBarType.error, + ); + } + await _slipStore.addAdvice(slip: slip); + return _handleSnackbar( + title: 'Info', + message: 'An advice with an ID: ${slip?.id} has been saved.', + variant: SnackBarType.info, + ); + } catch (e) { + _log.e('well once in awhile, things do break.'); return _handleSnackbar( title: 'Error', - message: 'An advice with the ID: ${slip?.id} has already been saved.', + message: 'Opps! something went wrong', variant: SnackBarType.error, ); } - await _slipStore.addAdvice(slip: slip); - return _handleSnackbar( - title: 'Info', - message: 'An advice with an ID: ${slip?.id} has been saved.', - variant: SnackBarType.info, - ); } void _handleSnackbar({ @@ -40,4 +50,16 @@ class AdviceViewModel extends BaseViewModel { variant: variant, ); } + + void actionconfirmSave({Slip? slip}) async { + final DialogResponse? response = await _dialogService.showDialog( + title: 'Save Advice', + description: 'Are you sure you want to save this advice?', + buttonTitle: 'Yes', + cancelTitle: 'No', + ); + if (response?.confirmed ?? false) { + _actionSaveAdvice(slip: slip); + } + } } diff --git a/lib/features/advices/advices_view.dart b/lib/features/advices/advices_view.dart index 155b233..a8d0d95 100644 --- a/lib/features/advices/advices_view.dart +++ b/lib/features/advices/advices_view.dart @@ -66,7 +66,7 @@ class AdvicesView extends StatelessWidget { trailing: IconButton( color: Colors.redAccent, onPressed: () { - model.actionDeleteAdvice(slip: slip); + model.actionconfirmDelete(slip: slip); }, icon: const Icon(Icons.delete), ), diff --git a/lib/features/advices/view_model/advices_viewmodel.dart b/lib/features/advices/view_model/advices_viewmodel.dart index 309a56a..04c9525 100644 --- a/lib/features/advices/view_model/advices_viewmodel.dart +++ b/lib/features/advices/view_model/advices_viewmodel.dart @@ -10,9 +10,10 @@ import '../../../core/stores/slip_store.dart'; class AdvicesViewModel extends StreamViewModel> { final _slipStore = locator(); final _snackbarService = locator(); + final _dialogService = locator(); final _log = getLogger('AdvicesViewModel'); - void actionDeleteAdvice({Slip? slip}) async { + void _actionDeleteAdvice({Slip? slip}) async { _log.i('deleting advice'); await _slipStore.removeAdvice(slip: slip); _snackbarService.showCustomSnackBar( @@ -24,6 +25,18 @@ class AdvicesViewModel extends StreamViewModel> { initialise(); } + void actionconfirmDelete({Slip? slip}) async { + final DialogResponse? response = await _dialogService.showDialog( + title: 'Delete Advice', + description: 'Are you sure you want to delete this advice?', + buttonTitle: 'Yes', + cancelTitle: 'No', + ); + if (response?.confirmed ?? false) { + _actionDeleteAdvice(slip: slip); + } + } + @override Stream> get stream => _slipStore.getStreamAdvice(); } diff --git a/lib/main.dart b/lib/main.dart index d6326ed..0414489 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -31,6 +31,7 @@ class MyApp extends StatelessWidget { // Notice that the counter didn't reset back to zero; the application // is not restarted. primarySwatch: Colors.blue, + useMaterial3: true, ), navigatorKey: StackedService.navigatorKey, initialRoute: Routes.splashView, diff --git a/pubspec.lock b/pubspec.lock index c8ec73b..e11912c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -28,7 +28,7 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.8.2" + version: "2.9.0" boolean_selector: dependency: transitive description: @@ -98,14 +98,7 @@ packages: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" - charcode: - dependency: transitive - description: - name: charcode - url: "https://pub.dartlang.org" - source: hosted - version: "1.3.1" + version: "1.2.1" checked_yaml: dependency: transitive description: @@ -119,7 +112,7 @@ packages: name: clock url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.1.1" code_builder: dependency: transitive description: @@ -224,7 +217,7 @@ packages: name: fake_async url: "https://pub.dartlang.org" source: hosted - version: "1.3.0" + version: "1.3.1" ffi: dependency: transitive description: @@ -379,21 +372,21 @@ packages: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.11" + version: "0.12.12" material_color_utilities: dependency: transitive description: name: material_color_utilities url: "https://pub.dartlang.org" source: hosted - version: "0.1.4" + version: "0.1.5" meta: dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.7.0" + version: "1.8.0" mime: dependency: transitive description: @@ -428,7 +421,7 @@ packages: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.8.1" + version: "1.8.2" path_provider_linux: dependency: transitive description: @@ -601,7 +594,7 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.2" + version: "1.9.0" sqflite: dependency: "direct main" description: @@ -678,7 +671,7 @@ packages: name: string_scanner url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.1.1" synchronized: dependency: transitive description: @@ -692,14 +685,14 @@ packages: name: term_glyph url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.2.1" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.4.9" + version: "0.4.12" timing: dependency: transitive description: From 309fb40abf5455a388c7eb785a89ae272ebac8ef Mon Sep 17 00:00:00 2001 From: michael kalango Date: Wed, 16 Nov 2022 13:25:36 +0100 Subject: [PATCH 2/2] added theming --- lib/core/app/app.dart | 5 +++ lib/core/app/app.locator.dart | 2 ++ lib/features/home/home_view.dart | 8 ++++- .../home/view_model/home_viewmodel.dart | 8 +++++ lib/features/home/widgets/action_button.dart | 36 ------------------- lib/features/home/widgets/contents.dart | 10 ++++-- lib/main.dart | 36 +++++++++---------- pubspec.lock | 21 +++++++++++ pubspec.yaml | 1 + 9 files changed, 68 insertions(+), 59 deletions(-) delete mode 100644 lib/features/home/widgets/action_button.dart diff --git a/lib/core/app/app.dart b/lib/core/app/app.dart index 060b872..687bcc3 100644 --- a/lib/core/app/app.dart +++ b/lib/core/app/app.dart @@ -1,6 +1,7 @@ import 'package:sqflite_migration_service/sqflite_migration_service.dart'; import 'package:stacked/stacked_annotations.dart'; import 'package:stacked_services/stacked_services.dart'; +import 'package:stacked_themes/stacked_themes.dart'; import '../../features/advice/advice_view.dart'; import '../../features/advices/advices_view.dart'; @@ -64,6 +65,10 @@ import '../stores/slip_store.dart'; classType: AdviceSlipServiceImpl, asType: AdviceSlipService, ), + LazySingleton( + classType: ThemeService, + resolveUsing: ThemeService.getInstance, + ) ], logger: StackedLogger(), ) diff --git a/lib/core/app/app.locator.dart b/lib/core/app/app.locator.dart index 643a559..1115e87 100644 --- a/lib/core/app/app.locator.dart +++ b/lib/core/app/app.locator.dart @@ -9,6 +9,7 @@ import 'package:sqflite_migration_service/sqflite_migration_service.dart'; import 'package:stacked_core/stacked_core.dart'; import 'package:stacked_services/stacked_services.dart'; +import 'package:stacked_themes/stacked_themes.dart'; import '../services/advice_slip_service.dart'; import '../services/connectivity_service.dart'; @@ -39,4 +40,5 @@ Future setupLocator( () => ConnectivityServiceImpl()); locator .registerLazySingleton(() => AdviceSlipServiceImpl()); + locator.registerLazySingleton(() => ThemeService.getInstance()); } diff --git a/lib/features/home/home_view.dart b/lib/features/home/home_view.dart index 4dac9d6..37038c5 100644 --- a/lib/features/home/home_view.dart +++ b/lib/features/home/home_view.dart @@ -37,9 +37,15 @@ class HomeView extends StatelessWidget { appBar: AppBar( backgroundColor: Colors.transparent, elevation: 0, + leading: Switch.adaptive( + value: model.isDark, + onChanged: (value) { + model.actionSwitchTheme(); + }, + ), actions: [ IconButton( - color: Colors.black, + color: theme.iconTheme.color, onPressed: () { model.actionRouteAdvices(); }, diff --git a/lib/features/home/view_model/home_viewmodel.dart b/lib/features/home/view_model/home_viewmodel.dart index 584c495..b6b1278 100644 --- a/lib/features/home/view_model/home_viewmodel.dart +++ b/lib/features/home/view_model/home_viewmodel.dart @@ -1,6 +1,7 @@ import 'package:dio/dio.dart'; import 'package:stacked/stacked.dart'; import 'package:stacked_services/stacked_services.dart'; +import 'package:stacked_themes/stacked_themes.dart'; import '../../../core/app/app.locator.dart'; import '../../../core/app/app.logger.dart'; @@ -15,8 +16,15 @@ class HomeViewModel extends StreamViewModel { final _navigationService = locator(); final _adviceSlipService = locator(); final _connectivityService = locator(); + final _themeService = locator(); final _log = getLogger('HomeViewModel'); + bool get isDark => _themeService.isDarkMode; + + void actionSwitchTheme() { + _themeService.toggleDarkLightTheme(); + } + void actionRouteAdvices() { _navigationService.navigateToAdvicesView(); } diff --git a/lib/features/home/widgets/action_button.dart b/lib/features/home/widgets/action_button.dart deleted file mode 100644 index e0707b8..0000000 --- a/lib/features/home/widgets/action_button.dart +++ /dev/null @@ -1,36 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../../../core/utils/size_manager.dart'; - -class ActionButton extends StatelessWidget { - final VoidCallback? onTap; - const ActionButton({ - Key? key, - this.onTap, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - SizeMg.init(context); - var theme = Theme.of(context); - return Material( - color: Colors.transparent, - child: InkWell( - onTap: onTap, - child: Container( - height: SizeMg.height(70), - width: SizeMg.width(300), - decoration: BoxDecoration( - color: Colors.blueGrey, - borderRadius: BorderRadius.circular(SizeMg.radius(5)), - ), - alignment: Alignment.center, - child: Text( - 'Get Advice', - style: theme.textTheme.button, - ), - ), - ), - ); - } -} diff --git a/lib/features/home/widgets/contents.dart b/lib/features/home/widgets/contents.dart index 150eeca..0d61093 100644 --- a/lib/features/home/widgets/contents.dart +++ b/lib/features/home/widgets/contents.dart @@ -4,7 +4,6 @@ import 'package:stacked/stacked.dart'; import '../../../core/shared_widgets/info_card.dart'; import '../../../core/utils/size_manager.dart'; import '../view_model/home_viewmodel.dart'; -import 'action_button.dart'; class Contents extends ViewModelWidget { const Contents({Key? key}) : super(key: key, reactive: false); @@ -12,6 +11,7 @@ class Contents extends ViewModelWidget { @override Widget build(BuildContext context, HomeViewModel viewModel) { SizeMg.init(context); + var theme = Theme.of(context); return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ @@ -22,10 +22,14 @@ class Contents extends ViewModelWidget { SizedBox( height: SizeMg.height(50), ), - ActionButton( - onTap: () { + ElevatedButton( + onPressed: () { viewModel.actionGetAdviceSlip(); }, + child: Text( + 'Get Advice', + style: theme.textTheme.button, + ), ), ], ); diff --git a/lib/main.dart b/lib/main.dart index 0414489..a5ee6d7 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:stacked_services/stacked_services.dart'; +import 'package:stacked_themes/stacked_themes.dart'; import 'core/app/app.locator.dart'; import 'core/app/app.router.dart'; @@ -8,6 +9,7 @@ import 'core/utils/setup_snackbar_ui.dart'; Future main() async { WidgetsFlutterBinding.ensureInitialized; await setupLocator(); + await ThemeManager.initialise(); setupSnackbarUi(); runApp(const MyApp()); } @@ -18,25 +20,21 @@ class MyApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { - return MaterialApp( - title: 'Advice App', - theme: ThemeData( - // This is the theme of your application. - // - // Try running your application with "flutter run". You'll see the - // application has a blue toolbar. Then, without quitting the app, try - // changing the primarySwatch below to Colors.green and then invoke - // "hot reload" (press "r" in the console where you ran "flutter run", - // or simply save your changes to "hot reload" in a Flutter IDE). - // Notice that the counter didn't reset back to zero; the application - // is not restarted. - primarySwatch: Colors.blue, - useMaterial3: true, - ), - navigatorKey: StackedService.navigatorKey, - initialRoute: Routes.splashView, - onGenerateRoute: StackedRouter().onGenerateRoute, - navigatorObservers: [StackedService.routeObserver], + return ThemeBuilder( + lightTheme: ThemeData.light(useMaterial3: true), + darkTheme: ThemeData.dark(useMaterial3: true), + builder: (context, lightTheme, darkTheme, themeMode) { + return MaterialApp( + title: 'Advice App', + theme: lightTheme, + darkTheme: darkTheme, + themeMode: themeMode, + navigatorKey: StackedService.navigatorKey, + initialRoute: Routes.splashView, + onGenerateRoute: StackedRouter().onGenerateRoute, + navigatorObservers: [StackedService.routeObserver], + ); + }, ); } } diff --git a/pubspec.lock b/pubspec.lock index e11912c..5f53acd 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -258,6 +258,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "5.5.3+2" + flutter_statusbarcolor_ns: + dependency: transitive + description: + name: flutter_statusbarcolor_ns + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.0" flutter_test: dependency: "direct dev" description: flutter @@ -506,6 +513,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "4.0.0" + rxdart: + dependency: transitive + description: + name: rxdart + url: "https://pub.dartlang.org" + source: hosted + version: "0.27.7" shared_preferences: dependency: transitive description: @@ -651,6 +665,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.9.5" + stacked_themes: + dependency: "direct main" + description: + name: stacked_themes + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.9" stream_channel: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index fa49d17..423e942 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -40,6 +40,7 @@ dependencies: stacked: ^2.3.15+1 stacked_core: ^1.2.4 stacked_services: ^0.9.5 + stacked_themes: ^0.3.9 dev_dependencies: build_runner: ^2.2.0