From 40bdcc44f9c64a718d804a727dd44f36f059551a Mon Sep 17 00:00:00 2001 From: Andreas Fahrecker Date: Mon, 8 Mar 2021 20:48:45 +0100 Subject: [PATCH] Feature/code cleanup (#9) * Implemented hasSettingsLoaded reducer * Added Padding to Progress List View * Created Settings and Time Progress List Store Connector * Rewritten Home Active Tab * Fixed missing onTap in Progress List Tile * Started using new Store Connectors in Inactive and Settings Tab * Created Time Progress Store Connector * Rewritten ProgressDetailScreen with new Store Connectors * Rewritten DatePickerBtn with TextButton * Deleted unused widget * Changed Foreground Color behaviour in ColorPicker BTN * Created Select Duration Button * Rewritten Duration Setting Widget * Updated Version Number Signed-off-by: Andreas Fahrecker --- lib/helper_functions.dart | 17 +++ lib/reducers/app_state_reducer.dart | 5 +- lib/reducers/has_loaded_reducer.dart | 23 ++- lib/screens/home_screen.dart | 12 +- lib/screens/progress_detail_screen.dart | 141 ++++++++---------- .../color_picker_btn.dart | 13 +- .../buttons/create_progress_button.dart | 18 +++ .../{ => buttons}/date_picker_btn.dart | 25 ++-- lib/widgets/buttons/select_duration_btn.dart | 47 ++++++ lib/widgets/home/home_progress_list_tile.dart | 26 ---- .../home/tabs/home_active_progresses_tab.dart | 65 +++----- .../tabs/home_inactive_progresses_tab.dart | 62 +++----- lib/widgets/home/tabs/home_settings_tab.dart | 61 ++------ .../tabs/settings/color_settings_widget.dart | 7 +- .../settings/duration_settings_widget.dart | 52 ++----- lib/widgets/progress_editor_widget.dart | 2 +- .../progress_list_tile.dart | 6 + .../progress_list_view.dart | 19 +-- .../settings_store_connector.dart | 60 ++++++++ .../time_progress_list_store_connector.dart | 40 +++++ .../time_progress_store_connector.dart | 66 ++++++++ pubspec.yaml | 2 +- 22 files changed, 450 insertions(+), 319 deletions(-) create mode 100644 lib/helper_functions.dart rename lib/widgets/{home/tabs/settings => buttons}/color_picker_btn.dart (78%) create mode 100644 lib/widgets/buttons/create_progress_button.dart rename lib/widgets/{ => buttons}/date_picker_btn.dart (54%) create mode 100644 lib/widgets/buttons/select_duration_btn.dart delete mode 100644 lib/widgets/home/home_progress_list_tile.dart create mode 100644 lib/widgets/store_connectors/settings_store_connector.dart create mode 100644 lib/widgets/store_connectors/time_progress_list_store_connector.dart create mode 100644 lib/widgets/store_connectors/time_progress_store_connector.dart diff --git a/lib/helper_functions.dart b/lib/helper_functions.dart new file mode 100644 index 0000000..7dc02a0 --- /dev/null +++ b/lib/helper_functions.dart @@ -0,0 +1,17 @@ +import 'dart:ui'; + +import 'package:time_progress_tracker/models/time_progress.dart'; + +TimeProgress selectProgressById(List tpList, String id) => + tpList.firstWhere((tp) => tp.id == id, orElse: null); + +List selectActiveProgresses(List tpList) => + tpList.where((tp) => tp.hasStarted() && !tp.hasEnded()).toList(); + +List selectInactiveProgresses(List tpList) => + tpList.where((tp) => !tp.hasStarted() || tp.hasEnded()).toList(); + +bool useBrightBackground(Color bC) { + double yiq = ((bC.red * 299) + (bC.green * 587) + (bC.blue * 114)) / 1000; + return yiq >= 186 || (bC.red == 0 && bC.green == 0 && bC.blue == 0); +} diff --git a/lib/reducers/app_state_reducer.dart b/lib/reducers/app_state_reducer.dart index d8787cd..ad684a8 100644 --- a/lib/reducers/app_state_reducer.dart +++ b/lib/reducers/app_state_reducer.dart @@ -7,7 +7,10 @@ import 'package:time_progress_tracker/reducers/time_progress_list_reducer.dart'; AppState appStateReducer(AppState state, dynamic action) { return AppState( - hasProgressesLoaded: hasLoadedReducer(state.hasProgressesLoaded, action), + hasSettingsLoaded: + hasSettingsLoadedReducer(state.hasSettingsLoaded, action), + hasProgressesLoaded: + hasProgressesLoadedReducer(state.hasProgressesLoaded, action), timeProgressList: timeProgressListReducer(state.timeProgressList, action), appSettings: appSettingsReducers(state.appSettings, action), ); diff --git a/lib/reducers/has_loaded_reducer.dart b/lib/reducers/has_loaded_reducer.dart index cb7ab0c..603b3fb 100644 --- a/lib/reducers/has_loaded_reducer.dart +++ b/lib/reducers/has_loaded_reducer.dart @@ -1,15 +1,28 @@ import 'package:redux/redux.dart'; import 'package:time_progress_tracker/actions/actions.dart'; -final hasLoadedReducer = combineReducers([ - TypedReducer(_setLoaded), - TypedReducer(_setUnloaded) +final hasProgressesLoadedReducer = combineReducers([ + TypedReducer(_setProgressesLoaded), + TypedReducer(_setProgressesUnloaded) ]); -bool _setLoaded(bool hasLoaded, TimeProgressListLoadedAction action) { +bool _setProgressesLoaded(bool hasLoaded, TimeProgressListLoadedAction action) { return true; } -bool _setUnloaded(bool hasLoaded, TimeProgressListNotLoadedAction action) { +bool _setProgressesUnloaded(bool hasLoaded, TimeProgressListNotLoadedAction action) { return false; } + +final hasSettingsLoadedReducer = combineReducers([ + TypedReducer(_setSettingsLoaded), + TypedReducer(_setSettingsUnloaded) +]); + +bool _setSettingsLoaded(bool hasLoaded, AppSettingsLoadedActions action) { + return true; +} + +bool _setSettingsUnloaded(bool hasLoaded, AppSettingsNotLoadedAction action) { + return false; +} \ No newline at end of file diff --git a/lib/screens/home_screen.dart b/lib/screens/home_screen.dart index 15ad17c..9b5d2d9 100644 --- a/lib/screens/home_screen.dart +++ b/lib/screens/home_screen.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:time_progress_tracker/screens/progress_creation_screen.dart'; +import 'package:time_progress_tracker/widgets/buttons/create_progress_button.dart'; import 'package:time_progress_tracker/widgets/home/home_bottom_navbar.dart'; import 'package:time_progress_tracker/widgets/home/tabs/home_active_progresses_tab.dart'; import 'package:time_progress_tracker/widgets/home/tabs/home_inactive_progresses_tab.dart'; @@ -37,15 +37,7 @@ class _HomeScreenState extends State { ), body: _children[_currentIndex], floatingActionButtonLocation: FloatingActionButtonLocation.endFloat, - floatingActionButton: _currentIndex != 2 - ? FloatingActionButton( - heroTag: "createProgressBTN", - child: Icon(Icons.add), - onPressed: () { - Navigator.pushNamed(context, ProgressCreationScreen.routeName); - }, - ) - : null, + floatingActionButton: _currentIndex != 2 ? CreateProgressButton() : null, bottomNavigationBar: HomeBottomNavBar( currentIndex: _currentIndex, onTap: onBottomTabTapped, diff --git a/lib/screens/progress_detail_screen.dart b/lib/screens/progress_detail_screen.dart index dbb3bab..be4c7b9 100644 --- a/lib/screens/progress_detail_screen.dart +++ b/lib/screens/progress_detail_screen.dart @@ -1,15 +1,11 @@ import 'package:flutter/material.dart'; -import 'package:flutter_redux/flutter_redux.dart'; -import 'package:redux/redux.dart'; -import 'package:time_progress_tracker/actions/actions.dart'; -import 'package:time_progress_tracker/models/app_settings.dart'; -import 'package:time_progress_tracker/models/app_state.dart'; import 'package:time_progress_tracker/models/time_progress.dart'; import 'package:time_progress_tracker/screens/home_screen.dart'; -import 'package:time_progress_tracker/selectors/time_progress_selectors.dart'; import 'package:time_progress_tracker/widgets/detail_screen_floating_action_buttons.dart'; import 'package:time_progress_tracker/widgets/progress_editor_widget.dart'; import 'package:time_progress_tracker/widgets/progress_view_widget.dart'; +import 'package:time_progress_tracker/widgets/store_connectors/settings_store_connector.dart'; +import 'package:time_progress_tracker/widgets/store_connectors/time_progress_store_connector.dart'; class ProgressDetailScreenArguments { final String id; @@ -31,6 +27,13 @@ class _ProgressDetailScreenState extends State { bool _editMode = false, _isEditedProgressValid = false; TimeProgress _editedProgress, _originalProgress; + void _initEditedProgress(TimeProgress tp) { + if (_editedProgress == null) { + _editedProgress = tp; + _originalProgress = tp; + } + } + void _onEditedProgressChanged( TimeProgress newProgress, bool isNewProgressValid) { setState(() { @@ -52,87 +55,75 @@ class _ProgressDetailScreenState extends State { }); } + List _renderColumnChildren( + SettingsViewModel settingsVm, TimeProgressViewModel tpVm) { + List columnChildren = [ + Expanded( + child: ProgressViewWidget( + timeProgress: _editMode ? _editedProgress : tpVm.tp, + doneColor: settingsVm.appSettings.doneColor, + leftColor: settingsVm.appSettings.leftColor, + )) + ]; + if (_editMode) + columnChildren.add(Expanded( + child: ProgressEditorWidget( + timeProgress: _editedProgress, + onTimeProgressChanged: _onEditedProgressChanged, + ))); + return columnChildren; + } + @override Widget build(BuildContext context) { final ProgressDetailScreenArguments args = ModalRoute.of(context).settings.arguments; + return Scaffold( appBar: AppBar( title: Text(ProgressDetailScreen.title), ), - body: Container( - margin: EdgeInsets.all(8), - child: StoreConnector( - onInit: loadTimeProgressListIfUnloaded, - converter: (store) => timeProgressByIdSelector(store.state, args.id), - builder: (BuildContext context, TimeProgress timeProgress) { - if (timeProgress == null) //+++++Time Progress Not Found Error+++++ - return Center( - child: Text("Error Invalid Time Progress"), - ); - if (_editedProgress == null) { - _editedProgress = timeProgress; - _originalProgress = timeProgress; - } // initialize _editedProgress - - List columnChildren = [ - Expanded( - child: StoreConnector( - onInit: loadSettingsIfUnloaded, - converter: (store) => appSettingsSelector(store.state), - builder: (BuildContext context, AppSettings settings) { - return ProgressViewWidget( - timeProgress: _editMode ? _editedProgress : timeProgress, - doneColor: settings.doneColor, - leftColor: settings.leftColor, - ); - }, - ), - ) - ]; - if (_editMode) - columnChildren.add(Expanded( - child: ProgressEditorWidget( - timeProgress: _editedProgress, - onTimeProgressChanged: _onEditedProgressChanged, - ), - )); - - return Column( - children: columnChildren, - ); - }, - ), + body: SettingsStoreConnector( + loadedBuilder: (context, settingsVm) { + return TimeProgressStoreConnector( + timeProgressId: args.id, + loadedBuilder: (context, tpVm) { + _initEditedProgress(tpVm.tp); + return Container( + margin: EdgeInsets.all(8), + child: Column( + children: _renderColumnChildren(settingsVm, tpVm), + )); + }, + ); + }, ), floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat, - floatingActionButton: StoreConnector( - onInit: loadTimeProgressListIfUnloaded, - converter: (store) => timeProgressByIdSelector(store.state, args.id), - builder: (BuildContext context, TimeProgress timeProgress) { - final Store store = StoreProvider.of(context); + floatingActionButton: TimeProgressStoreConnector( + timeProgressId: args.id, + loadedBuilder: (context, tpVm) { + void _saveEditedProgress() { + tpVm.updateTimeProgress(_editedProgress); + _switchEditMode(false); + } - void _saveEditedProgress() { - store - .dispatch(UpdateTimeProgressAction(args.id, _editedProgress)); - _switchEditMode(false); - } + void _deleteTimeProgress() { + tpVm.deleteTimeProgress(); + Navigator.popUntil( + context, ModalRoute.withName(HomeScreen.routeName)); + } - void _deleteTimeProgress() { - store.dispatch(DeleteTimeProgressAction(args.id)); - Navigator.popUntil( - context, ModalRoute.withName(HomeScreen.routeName)); - } - - return DetailScreenFloatingActionButtons( - editMode: _editMode, - originalProgress: timeProgress, - editedProgress: _editedProgress, - isEditedProgressValid: _isEditedProgressValid, - onEditProgress: () => _switchEditMode(true), - onSaveEditedProgress: _saveEditedProgress, - onCancelEditProgress: _cancelEditMode, - onDeleteProgress: _deleteTimeProgress); - }), + return DetailScreenFloatingActionButtons( + editMode: _editMode, + originalProgress: tpVm.tp, + editedProgress: _editedProgress, + isEditedProgressValid: _isEditedProgressValid, + onEditProgress: () => _switchEditMode(true), + onSaveEditedProgress: _saveEditedProgress, + onCancelEditProgress: _cancelEditMode, + onDeleteProgress: _deleteTimeProgress); + }, + ), ); } } diff --git a/lib/widgets/home/tabs/settings/color_picker_btn.dart b/lib/widgets/buttons/color_picker_btn.dart similarity index 78% rename from lib/widgets/home/tabs/settings/color_picker_btn.dart rename to lib/widgets/buttons/color_picker_btn.dart index b4be9d3..96e1bc5 100644 --- a/lib/widgets/home/tabs/settings/color_picker_btn.dart +++ b/lib/widgets/buttons/color_picker_btn.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_colorpicker/flutter_colorpicker.dart'; +import 'package:time_progress_tracker/helper_functions.dart'; class ColorPickerButton extends StatelessWidget { final String title, dialogTitle; @@ -15,13 +16,7 @@ class ColorPickerButton extends StatelessWidget { @override Widget build(BuildContext context) { - Color getBtnPrimaryColor() => Color.fromARGB( - selectedColor.alpha, - selectedColor.alpha - selectedColor.red, - selectedColor.alpha - selectedColor.green, - selectedColor.alpha - selectedColor.blue, - ); - + ThemeData appTheme = Theme.of(context); return TextButton( onPressed: () { showDialog( @@ -41,7 +36,9 @@ class ColorPickerButton extends StatelessWidget { }, child: Text(title), style: TextButton.styleFrom( - primary: getBtnPrimaryColor(), + primary: useBrightBackground(selectedColor) + ? appTheme.primaryTextTheme.button.color + : appTheme.textTheme.button.color, backgroundColor: selectedColor, ), ); diff --git a/lib/widgets/buttons/create_progress_button.dart b/lib/widgets/buttons/create_progress_button.dart new file mode 100644 index 0000000..5d125f3 --- /dev/null +++ b/lib/widgets/buttons/create_progress_button.dart @@ -0,0 +1,18 @@ +import 'package:flutter/material.dart'; +import 'package:time_progress_tracker/screens/progress_creation_screen.dart'; + +class CreateProgressButton extends StatelessWidget { + final String _heroTag = "createProgressBTN"; + + @override + Widget build(BuildContext context) { + void _onButtonPressed() => + Navigator.pushNamed(context, ProgressCreationScreen.routeName); + + return FloatingActionButton( + heroTag: _heroTag, + child: Icon(Icons.add), + onPressed: _onButtonPressed, + ); + } +} diff --git a/lib/widgets/date_picker_btn.dart b/lib/widgets/buttons/date_picker_btn.dart similarity index 54% rename from lib/widgets/date_picker_btn.dart rename to lib/widgets/buttons/date_picker_btn.dart index 33a9b26..908dbfd 100644 --- a/lib/widgets/date_picker_btn.dart +++ b/lib/widgets/buttons/date_picker_btn.dart @@ -11,21 +11,26 @@ class DatePickerBtn extends StatelessWidget { @required this.onDatePicked, }) : super(); + void _onButtonPressed(BuildContext context) async { + onDatePicked(await showDatePicker( + context: context, + initialDate: pickedDate, + firstDate: DateTime(1900), + lastDate: DateTime(2100), + )); + } + @override Widget build(BuildContext context) { ThemeData appTheme = Theme.of(context); - return FlatButton( - onPressed: () async { - onDatePicked(await showDatePicker( - context: context, - initialDate: pickedDate, - firstDate: DateTime(1900), - lastDate: DateTime(2100), - )); - }, + return TextButton( + onPressed: () => _onButtonPressed(context), child: Text( "$leadingString ${pickedDate.toLocal().toString().split(" ")[0]}"), - color: appTheme.accentColor, + style: TextButton.styleFrom( + primary: appTheme.primaryTextTheme.button.color, + backgroundColor: appTheme.accentColor, + ), ); } } diff --git a/lib/widgets/buttons/select_duration_btn.dart b/lib/widgets/buttons/select_duration_btn.dart new file mode 100644 index 0000000..5490000 --- /dev/null +++ b/lib/widgets/buttons/select_duration_btn.dart @@ -0,0 +1,47 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_picker/flutter_picker.dart'; + +class SelectDurationBtn extends StatelessWidget { + final Duration duration; + final void Function(Duration) updateDuration; + + SelectDurationBtn({ + @required this.duration, + @required this.updateDuration, + }); + + void _onPickerConfirm(Picker picker, List values) { + int years = values[0], months = values[1], days = values[2]; + days = (years * 365) + (months * 31) + days; + Duration newDuration = Duration(days: days); + updateDuration(newDuration); + } + + void _onButtonPressed(BuildContext context, ThemeData appTheme) => Picker( + adapter: NumberPickerAdapter(data: [ + const NumberPickerColumn(begin: 0, end: 999, suffix: Text(" Y")), + const NumberPickerColumn(begin: 0, end: 11, suffix: Text(" M")), + const NumberPickerColumn(begin: 0, end: 31, suffix: Text(" D")), + ]), + hideHeader: false, + title: const Text("Default Duration"), + selectedTextStyle: TextStyle(color: appTheme.accentColor), + onConfirm: _onPickerConfirm) + .showModal(context); + + @override + Widget build(BuildContext context) { + ThemeData appTheme = Theme.of(context); + + int years = duration.inDays ~/ 365; + int months = (duration.inDays - (365 * years)) ~/ 30; + int days = duration.inDays - (365 * years) - (30 * months); + return TextButton( + onPressed: () => _onButtonPressed(context, appTheme), + child: Text("$years Years $months Months $days Days"), + style: TextButton.styleFrom( + primary: appTheme.primaryTextTheme.button.color, + backgroundColor: appTheme.accentColor, + )); + } +} diff --git a/lib/widgets/home/home_progress_list_tile.dart b/lib/widgets/home/home_progress_list_tile.dart deleted file mode 100644 index be8b8d7..0000000 --- a/lib/widgets/home/home_progress_list_tile.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:time_progress_tracker/models/time_progress.dart'; -import 'package:time_progress_tracker/widgets/progress_list_view/progress_list_tile.dart'; - -class HomeProgressListTile extends StatelessWidget { - final TimeProgress timeProgress; - final Color doneColor; - final Color leftColor; - - HomeProgressListTile({ - @required this.timeProgress, - @required this.doneColor, - @required this.leftColor, - }); - - @override - Widget build(BuildContext context) { - return Card( - child: ProgressListTile( - timeProgress: timeProgress, - doneColor: doneColor, - leftColor: leftColor, - ), - ); - } -} diff --git a/lib/widgets/home/tabs/home_active_progresses_tab.dart b/lib/widgets/home/tabs/home_active_progresses_tab.dart index 57af28d..9b7a975 100644 --- a/lib/widgets/home/tabs/home_active_progresses_tab.dart +++ b/lib/widgets/home/tabs/home_active_progresses_tab.dart @@ -1,51 +1,32 @@ import 'package:flutter/material.dart'; -import 'package:flutter_redux/flutter_redux.dart'; -import 'package:time_progress_tracker/actions/actions.dart'; -import 'package:time_progress_tracker/models/app_settings.dart'; -import 'package:time_progress_tracker/models/app_state.dart'; +import 'package:time_progress_tracker/helper_functions.dart'; import 'package:time_progress_tracker/models/time_progress.dart'; -import 'package:time_progress_tracker/selectors/time_progress_selectors.dart'; -import 'package:time_progress_tracker/widgets/home/home_progress_list_tile.dart'; +import 'package:time_progress_tracker/widgets/progress_list_view/progress_list_view.dart'; +import 'package:time_progress_tracker/widgets/store_connectors/settings_store_connector.dart'; +import 'package:time_progress_tracker/widgets/store_connectors/time_progress_list_store_connector.dart'; class HomeActiveProgressesTab extends StatelessWidget { @override Widget build(BuildContext context) { - return StoreConnector( - onInit: loadTimeProgressListIfUnloaded, - converter: (store) => store.state.hasProgressesLoaded, - builder: (context, hasLoaded) { - if (!(hasLoaded as bool)) - return Center( - child: CircularProgressIndicator(), - ); - return StoreConnector( - onInit: loadSettingsIfUnloaded, - converter: (store) => appSettingsSelector(store.state), - builder: (context, AppSettings settings) { - if (settings == null) - return Center(child: CircularProgressIndicator()); - return StoreConnector>( - converter: (store) => activeTimeProgressesSelector(store.state), - builder: (context, List timeProgresses) { - if (timeProgresses.length < 1) - return Container( - padding: EdgeInsets.all(16), - child: Center( - child: Text( - "You don't have any currently active time progresses, that are tracked."), - ), - ); - return ListView( - padding: EdgeInsets.all(8), - children: timeProgresses - .map((timeProgress) => HomeProgressListTile( - timeProgress: timeProgress, - doneColor: settings.doneColor, - leftColor: settings.leftColor, - )) - .toList(), - ); - }, + return SettingsStoreConnector( + loadedBuilder: (context, settingsVm) { + return TimeProgressListStoreConnector( + loadedBuilder: (context, tpListVm) { + List activeTpList = + selectActiveProgresses(tpListVm.tpList); + if (activeTpList.length < 1) + return Container( + padding: EdgeInsets.all(16), + child: Center( + child: Text( + "You don't have any active time progress, that are tracked."), + ), + ); + + return ProgressListView( + timeProgressList: activeTpList, + doneColor: settingsVm.appSettings.doneColor, + leftColor: settingsVm.appSettings.leftColor, ); }, ); diff --git a/lib/widgets/home/tabs/home_inactive_progresses_tab.dart b/lib/widgets/home/tabs/home_inactive_progresses_tab.dart index 8fa2e60..9eb0312 100644 --- a/lib/widgets/home/tabs/home_inactive_progresses_tab.dart +++ b/lib/widgets/home/tabs/home_inactive_progresses_tab.dart @@ -1,50 +1,32 @@ import 'package:flutter/material.dart'; -import 'package:flutter_redux/flutter_redux.dart'; -import 'package:time_progress_tracker/actions/actions.dart'; -import 'package:time_progress_tracker/models/app_settings.dart'; +import 'package:time_progress_tracker/helper_functions.dart'; import 'package:time_progress_tracker/models/time_progress.dart'; -import 'package:time_progress_tracker/selectors/time_progress_selectors.dart'; -import 'package:time_progress_tracker/widgets/home/home_progress_list_tile.dart'; +import 'package:time_progress_tracker/widgets/progress_list_view/progress_list_view.dart'; +import 'package:time_progress_tracker/widgets/store_connectors/settings_store_connector.dart'; +import 'package:time_progress_tracker/widgets/store_connectors/time_progress_list_store_connector.dart'; class HomeInactiveProgressesTab extends StatelessWidget { @override Widget build(BuildContext context) { - return StoreConnector( - onInit: loadSettingsIfUnloaded, - converter: (store) => appSettingsSelector(store.state), - builder: (context, AppSettings settings) { - return StoreConnector( - onInit: loadTimeProgressListIfUnloaded, - converter: (store) => store.state.hasProgressesLoaded, - builder: (BuildContext context, dynamic hasLoaded) { - if (!(hasLoaded as bool)) - return Center( - child: CircularProgressIndicator(), + return SettingsStoreConnector( + loadedBuilder: (context, settingsVm) { + return TimeProgressListStoreConnector( + loadedBuilder: (context, tpListVm) { + List inactiveTpList = + selectInactiveProgresses(tpListVm.tpList); + if (inactiveTpList.length < 1) + return Container( + padding: EdgeInsets.all(16), + child: Center( + child: Text( + "You don't have any currently inactive time progresses, that are tracked."), + ), ); - return StoreConnector( - onInit: loadTimeProgressListIfUnloaded, - converter: (store) => inactiveTimeProgressesSelector(store.state), - builder: - (BuildContext context, List timeProgresses) { - if (timeProgresses.length < 1) - return Container( - padding: EdgeInsets.all(16), - child: Center( - child: Text( - "You don't have any currently inactive time progresses, that are tracked."), - ), - ); - return ListView( - padding: EdgeInsets.all(8), - children: timeProgresses - .map((timeProgress) => HomeProgressListTile( - timeProgress: timeProgress, - doneColor: settings.doneColor, - leftColor: settings.leftColor, - )) - .toList(), - ); - }, + + return ProgressListView( + timeProgressList: inactiveTpList, + doneColor: settingsVm.appSettings.doneColor, + leftColor: settingsVm.appSettings.leftColor, ); }, ); diff --git a/lib/widgets/home/tabs/home_settings_tab.dart b/lib/widgets/home/tabs/home_settings_tab.dart index 277a30f..a4a62e0 100644 --- a/lib/widgets/home/tabs/home_settings_tab.dart +++ b/lib/widgets/home/tabs/home_settings_tab.dart @@ -1,21 +1,14 @@ import 'package:flutter/material.dart'; -import 'package:flutter_redux/flutter_redux.dart'; -import 'package:redux/redux.dart'; -import 'package:time_progress_tracker/actions/actions.dart'; import 'package:time_progress_tracker/app.dart'; -import 'package:time_progress_tracker/models/app_settings.dart'; -import 'package:time_progress_tracker/models/app_state.dart'; -import 'package:time_progress_tracker/selectors/time_progress_selectors.dart'; import 'package:time_progress_tracker/widgets/home/tabs/settings/color_settings_widget.dart'; import 'package:time_progress_tracker/widgets/home/tabs/settings/duration_settings_widget.dart'; +import 'package:time_progress_tracker/widgets/store_connectors/settings_store_connector.dart'; class HomeSettingsTab extends StatelessWidget { @override Widget build(BuildContext context) { - return StoreConnector( - onInit: loadSettingsIfUnloaded, - converter: (store) => _ViewModel.create(store), - builder: (context, _ViewModel vm) { + return SettingsStoreConnector( + loadedBuilder: (context, settingsVm) { return Container( padding: EdgeInsets.all(16), child: Center( @@ -23,16 +16,16 @@ class HomeSettingsTab extends StatelessWidget { children: [ Expanded( child: ColorSettingsWidget( - doneColor: vm.doneColor, - leftColor: vm.leftColor, - updateDoneColor: vm.onDoneColorChanged, - updateLeftColor: vm.onLeftColorChanged, + doneColor: settingsVm.appSettings.doneColor, + leftColor: settingsVm.appSettings.leftColor, + updateDoneColor: settingsVm.updateDoneColor, + updateLeftColor: settingsVm.updateLeftColor, ), ), Expanded( child: DurationSettingsWidget( - duration: vm.duration, - updateDuration: vm.onDurationChanged, + duration: settingsVm.appSettings.duration, + updateDuration: settingsVm.updateDuration, ), ), Spacer(), @@ -57,39 +50,3 @@ class HomeSettingsTab extends StatelessWidget { ); } } - -class _ViewModel { - final Color doneColor, leftColor; - final void Function(Color) onDoneColorChanged, onLeftColorChanged; - final Duration duration; - final void Function(Duration) onDurationChanged; - - _ViewModel({ - @required this.doneColor, - @required this.leftColor, - @required this.onDoneColorChanged, - @required this.onLeftColorChanged, - @required this.duration, - @required this.onDurationChanged, - }); - - factory _ViewModel.create(Store store) { - AppSettings settings = appSettingsSelector(store.state); - - void _onDoneColorChanged(Color c) => store - .dispatch(UpdateAppSettingsActions(settings.copyWith(doneColor: c))); - void _onLeftColorChanged(Color c) => store - .dispatch(UpdateAppSettingsActions(settings.copyWith(leftColor: c))); - - void _onDurationChanged(Duration d) => store - .dispatch(UpdateAppSettingsActions(settings.copyWith(duration: d))); - - return _ViewModel( - doneColor: settings.doneColor, - leftColor: settings.leftColor, - onDoneColorChanged: _onDoneColorChanged, - onLeftColorChanged: _onLeftColorChanged, - duration: settings.duration, - onDurationChanged: _onDurationChanged); - } -} diff --git a/lib/widgets/home/tabs/settings/color_settings_widget.dart b/lib/widgets/home/tabs/settings/color_settings_widget.dart index 4282517..b088f62 100644 --- a/lib/widgets/home/tabs/settings/color_settings_widget.dart +++ b/lib/widgets/home/tabs/settings/color_settings_widget.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:time_progress_tracker/widgets/home/tabs/settings/color_picker_btn.dart'; +import 'package:time_progress_tracker/widgets/buttons/color_picker_btn.dart'; class ColorSettingsWidget extends StatelessWidget { final Color doneColor, leftColor; @@ -14,13 +14,14 @@ class ColorSettingsWidget extends StatelessWidget { @override Widget build(BuildContext context) { + ThemeData appTheme = Theme.of(context); + return Column( children: [ Expanded( child: Text( "Color Settings", - style: - TextStyle(fontWeight: FontWeight.bold, color: Colors.black87), + style: appTheme.textTheme.headline6, ), ), Row( diff --git a/lib/widgets/home/tabs/settings/duration_settings_widget.dart b/lib/widgets/home/tabs/settings/duration_settings_widget.dart index 5a96717..0d80458 100644 --- a/lib/widgets/home/tabs/settings/duration_settings_widget.dart +++ b/lib/widgets/home/tabs/settings/duration_settings_widget.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:flutter_picker/flutter_picker.dart'; +import 'package:time_progress_tracker/widgets/buttons/select_duration_btn.dart'; class DurationSettingsWidget extends StatelessWidget { final Duration duration; @@ -12,49 +12,29 @@ class DurationSettingsWidget extends StatelessWidget { @override Widget build(BuildContext context) { + ThemeData appTheme = Theme.of(context); + int years = duration.inDays ~/ 365; int months = (duration.inDays - (365 * years)) ~/ 30; int days = duration.inDays - (365 * years) - (30 * months); return Column( children: [ Expanded( - child: TextButton( - onPressed: () { - Picker( - adapter: NumberPickerAdapter( - data: [ - const NumberPickerColumn( - begin: 0, - end: 999, - suffix: Text(" Y"), - ), - const NumberPickerColumn( - begin: 0, - end: 11, - suffix: Text(" M"), - ), - const NumberPickerColumn( - begin: 0, - end: 30, - suffix: Text(" D"), - ), - ], - ), - hideHeader: true, - confirmText: "OK", - title: const Text("Select Duration"), - selectedTextStyle: TextStyle(color: Colors.blue), - onConfirm: (Picker picker, List value) { - int years = value[0], months = value[1], days = value[2]; - days = (years * 365) + (months * 30) + days; - Duration newDuration = Duration(days: days); - updateDuration(newDuration); - }, - ).showDialog(context); - }, - child: Text("Default Duration: $years Y - $months M - $days D"), + child: Text( + "Duration Settings", + style: appTheme.textTheme.headline6, ), ), + Row( + children: [ + Expanded( + child: SelectDurationBtn( + duration: duration, + updateDuration: updateDuration, + ), + ) + ], + ) ], ); } diff --git a/lib/widgets/progress_editor_widget.dart b/lib/widgets/progress_editor_widget.dart index 5aa19d2..71f9aaa 100644 --- a/lib/widgets/progress_editor_widget.dart +++ b/lib/widgets/progress_editor_widget.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:time_progress_tracker/models/time_progress.dart'; -import 'package:time_progress_tracker/widgets/date_picker_btn.dart'; +import 'package:time_progress_tracker/widgets/buttons/date_picker_btn.dart'; class ProgressEditorWidget extends StatefulWidget { final TimeProgress timeProgress; diff --git a/lib/widgets/progress_list_view/progress_list_tile.dart b/lib/widgets/progress_list_view/progress_list_tile.dart index 4a2e152..7cdf0a6 100644 --- a/lib/widgets/progress_list_view/progress_list_tile.dart +++ b/lib/widgets/progress_list_view/progress_list_tile.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:percent_indicator/linear_percent_indicator.dart'; import 'package:time_progress_tracker/models/time_progress.dart'; +import 'package:time_progress_tracker/screens/progress_detail_screen.dart'; class ProgressListTileStrings { static String percentString(TimeProgress tp) => @@ -39,9 +40,14 @@ class ProgressListTile extends StatelessWidget { @override Widget build(BuildContext context) { + void _onTileTap() => + Navigator.pushNamed(context, ProgressDetailScreen.routeName, + arguments: ProgressDetailScreenArguments(timeProgress.id)); + return ListTile( title: Text(timeProgress.name), subtitle: _renderSubtitle(context), + onTap: _onTileTap, ); } } diff --git a/lib/widgets/progress_list_view/progress_list_view.dart b/lib/widgets/progress_list_view/progress_list_view.dart index 2165a84..896b36e 100644 --- a/lib/widgets/progress_list_view/progress_list_view.dart +++ b/lib/widgets/progress_list_view/progress_list_view.dart @@ -13,20 +13,21 @@ class ProgressListView extends StatelessWidget { }); List _renderListViewChildren() { - return timeProgressList.map((e) => - Card( - child: ProgressListTile( - timeProgress: e, - doneColor: doneColor, - leftColor: leftColor, - ), - ) - ).toList(growable: false); + return timeProgressList + .map((e) => Card( + child: ProgressListTile( + timeProgress: e, + doneColor: doneColor, + leftColor: leftColor, + ), + )) + .toList(growable: false); } @override Widget build(BuildContext context) { return ListView( + padding: EdgeInsets.all(8), children: _renderListViewChildren(), ); } diff --git a/lib/widgets/store_connectors/settings_store_connector.dart b/lib/widgets/store_connectors/settings_store_connector.dart new file mode 100644 index 0000000..8ebc876 --- /dev/null +++ b/lib/widgets/store_connectors/settings_store_connector.dart @@ -0,0 +1,60 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_redux/flutter_redux.dart'; +import 'package:redux/redux.dart'; +import 'package:time_progress_tracker/actions/actions.dart'; +import 'package:time_progress_tracker/models/app_settings.dart'; +import 'package:time_progress_tracker/models/app_state.dart'; + +class SettingsStoreConnector extends StatelessWidget { + final Widget Function(BuildContext, SettingsViewModel) loadedBuilder; + + SettingsStoreConnector({ + @required this.loadedBuilder, + }); + + @override + Widget build(BuildContext context) { + return StoreConnector( + onInit: loadSettingsIfUnloaded, + converter: (store) => SettingsViewModel._create(store), + builder: (context, SettingsViewModel vm) { + if (!vm.hasSettingsLoaded) + return Center( + child: CircularProgressIndicator(), + ); + return loadedBuilder(context, vm); + }, + ); + } +} + +class SettingsViewModel { + final AppSettings appSettings; + final bool hasSettingsLoaded; + + final void Function(Color) updateDoneColor, updateLeftColor; + final void Function(Duration) updateDuration; + + SettingsViewModel( + this.appSettings, + this.hasSettingsLoaded, + this.updateDoneColor, + this.updateLeftColor, + this.updateDuration, + ); + + factory SettingsViewModel._create(Store store) { + AppSettings _appSettings = store.state.appSettings; + + void _updateDoneColor(Color dC) => store.dispatch( + UpdateAppSettingsActions(_appSettings.copyWith(doneColor: dC))); + void _updateLeftColor(Color lC) => store.dispatch( + UpdateAppSettingsActions(_appSettings.copyWith(leftColor: lC))); + + void _updateDuration(Duration d) => store + .dispatch(UpdateAppSettingsActions(_appSettings.copyWith(duration: d))); + + return SettingsViewModel(_appSettings, store.state.hasSettingsLoaded, + _updateDoneColor, _updateLeftColor, _updateDuration); + } +} diff --git a/lib/widgets/store_connectors/time_progress_list_store_connector.dart b/lib/widgets/store_connectors/time_progress_list_store_connector.dart new file mode 100644 index 0000000..6929281 --- /dev/null +++ b/lib/widgets/store_connectors/time_progress_list_store_connector.dart @@ -0,0 +1,40 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_redux/flutter_redux.dart'; +import 'package:redux/redux.dart'; +import 'package:time_progress_tracker/actions/actions.dart'; +import 'package:time_progress_tracker/models/app_state.dart'; +import 'package:time_progress_tracker/models/time_progress.dart'; + +class TimeProgressListStoreConnector extends StatelessWidget { + final Widget Function(BuildContext, TimeProgressListViewModel) loadedBuilder; + + TimeProgressListStoreConnector({ + @required this.loadedBuilder, + }); + + @override + Widget build(BuildContext context) { + return StoreConnector( + onInit: loadTimeProgressListIfUnloaded, + converter: (store) => TimeProgressListViewModel._create(store), + builder: (context, TimeProgressListViewModel vm) { + if (!vm.hasTpListLoaded) + return Center( + child: CircularProgressIndicator(), + ); + return loadedBuilder(context, vm); + }, + ); + } +} + +class TimeProgressListViewModel { + final List tpList; + final bool hasTpListLoaded; + + TimeProgressListViewModel(this.tpList, this.hasTpListLoaded); + + factory TimeProgressListViewModel._create(Store store) => + TimeProgressListViewModel( + store.state.timeProgressList, store.state.hasProgressesLoaded); +} diff --git a/lib/widgets/store_connectors/time_progress_store_connector.dart b/lib/widgets/store_connectors/time_progress_store_connector.dart new file mode 100644 index 0000000..9237623 --- /dev/null +++ b/lib/widgets/store_connectors/time_progress_store_connector.dart @@ -0,0 +1,66 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_redux/flutter_redux.dart'; +import 'package:redux/redux.dart'; +import 'package:time_progress_tracker/actions/actions.dart'; +import 'package:time_progress_tracker/models/app_state.dart'; +import 'package:time_progress_tracker/models/time_progress.dart'; + +import '../../helper_functions.dart'; + +class TimeProgressStoreConnector extends StatelessWidget { + final String timeProgressId; + final Widget Function(BuildContext, TimeProgressViewModel) loadedBuilder; + + TimeProgressStoreConnector({ + @required this.timeProgressId, + @required this.loadedBuilder, + }); + + @override + Widget build(BuildContext context) { + return StoreConnector( + onInit: loadTimeProgressListIfUnloaded, + converter: (store) => + TimeProgressViewModel._create(store, timeProgressId), + builder: (context, TimeProgressViewModel vm) { + if (!vm.hasTpListLoaded) + return Center( + child: CircularProgressIndicator(), + ); + if (vm.tp == null) + return Center( + child: Text("Error Invalid Time Progress"), + ); + return loadedBuilder(context, vm); + }, + ); + } +} + +class TimeProgressViewModel { + final TimeProgress tp; + final bool hasTpListLoaded; + + final void Function(TimeProgress) updateTimeProgress; + final void Function() deleteTimeProgress; + + TimeProgressViewModel( + this.tp, + this.hasTpListLoaded, + this.updateTimeProgress, + this.deleteTimeProgress, + ); + + factory TimeProgressViewModel._create(Store store, String id) { + void _updateTimeProgress(TimeProgress tp) => + store.dispatch(UpdateTimeProgressAction(id, tp)); + void _deleteTimeProgress() => store.dispatch(DeleteTimeProgressAction(id)); + + return TimeProgressViewModel( + selectProgressById(store.state.timeProgressList, id), + store.state.hasProgressesLoaded, + _updateTimeProgress, + _deleteTimeProgress, + ); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index df5abd4..8f7b999 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 0.0.18+18 +version: 0.0.19+19 environment: sdk: ">=2.7.0 <3.0.0"