diff --git a/lib/models/app_settings.dart b/lib/models/app_settings.dart index 919c6f5..120da4e 100644 --- a/lib/models/app_settings.dart +++ b/lib/models/app_settings.dart @@ -13,19 +13,26 @@ class AppSettings { this.duration, }); - factory AppSettings.defaults() => - AppSettings(doneColor: Colors.green, leftColor: Colors.red); + factory AppSettings.defaults() => AppSettings( + doneColor: Colors.green, + leftColor: Colors.red, + duration: Duration(days: 365), + ); AppSettings copyWith({ Color doneColor, Color leftColor, + Duration duration, }) => AppSettings( - doneColor: doneColor ?? this.doneColor, - leftColor: leftColor ?? this.leftColor); + doneColor: doneColor ?? this.doneColor, + leftColor: leftColor ?? this.leftColor, + duration: duration ?? this.duration, + ); @override - int get hashCode => doneColor.hashCode ^ leftColor.hashCode; + int get hashCode => + doneColor.hashCode ^ leftColor.hashCode ^ duration.hashCode; @override bool operator ==(Object other) => @@ -33,12 +40,15 @@ class AppSettings { other is AppSettings && runtimeType == other.runtimeType && doneColor == other.doneColor && - leftColor == other.leftColor; + leftColor == other.leftColor && + duration == other.duration; AppSettingsEntity toEntity() => - AppSettingsEntity(doneColor.value, leftColor.value); + AppSettingsEntity(doneColor.value, leftColor.value, duration.inDays); static AppSettings fromEntity(AppSettingsEntity entity) => AppSettings( - doneColor: Color(entity.doneColorValue), - leftColor: Color(entity.leftColorValue)); + doneColor: Color(entity.doneColorValue), + leftColor: Color(entity.leftColorValue), + duration: Duration(days: entity.durationDays), + ); } diff --git a/lib/models/time_progress.dart b/lib/models/time_progress.dart index 7b8d89d..ef758e8 100644 --- a/lib/models/time_progress.dart +++ b/lib/models/time_progress.dart @@ -19,6 +19,9 @@ class TimeProgress { "Initial Name", DateTime(thisYear - 1), DateTime(thisYear + 1)); } + factory TimeProgress.defaultFromDuration(Duration duration) => + TimeProgress("", DateTime.now(), DateTime.now().add(duration)); + TimeProgress copyWith( {String id, String name, DateTime startTime, DateTime endTime}) => TimeProgress( diff --git a/lib/persistence/app_settings.dart b/lib/persistence/app_settings.dart index b071400..f83a2fe 100644 --- a/lib/persistence/app_settings.dart +++ b/lib/persistence/app_settings.dart @@ -23,10 +23,13 @@ class AppSettingsRepository { } class AppSettingsEntity { - static const String _doneKey = "doneColorValue", _leftKey = "leftColorValue"; - final int doneColorValue, leftColorValue; + static const String _doneKey = "doneColorValue", + _leftKey = "leftColorValue", + _durationDaysKey = "durationDays"; + final int doneColorValue, leftColorValue, durationDays; - AppSettingsEntity(this.doneColorValue, this.leftColorValue); + AppSettingsEntity( + this.doneColorValue, this.leftColorValue, this.durationDays); factory AppSettingsEntity.defaults() => AppSettings.defaults().toEntity(); @@ -41,9 +44,16 @@ class AppSettingsEntity { doneColorValue == other.doneColorValue && leftColorValue == other.leftColorValue; - Map toJson() => - {_doneKey: doneColorValue, _leftKey: leftColorValue}; + Map toJson() => { + _doneKey: doneColorValue, + _leftKey: leftColorValue, + _durationDaysKey: durationDays, + }; static AppSettingsEntity fromJson(Map json) => - AppSettingsEntity(json[_doneKey], json[_leftKey]); + AppSettingsEntity( + json[_doneKey], + json[_leftKey], + json[_durationDaysKey], + ); } diff --git a/lib/screens/progress_creation_screen.dart b/lib/screens/progress_creation_screen.dart index 440f424..22acb22 100644 --- a/lib/screens/progress_creation_screen.dart +++ b/lib/screens/progress_creation_screen.dart @@ -1,9 +1,12 @@ import 'package:flutter/material.dart'; import 'package:flutter/widgets.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/selectors/time_progress_selectors.dart'; import 'package:time_progress_tracker/widgets/progress_editor_widget.dart'; class ProgressCreationScreen extends StatefulWidget { @@ -17,10 +20,16 @@ class ProgressCreationScreen extends StatefulWidget { } class _ProgressCreationScreenState extends State { - TimeProgress timeProgressToCreate = - TimeProgress("", DateTime.now(), DateTime(DateTime.now().year + 1)); + TimeProgress timeProgressToCreate; bool _isProgressValid = false; + void initTimeProgress(TimeProgress timeProgress) { + if (timeProgressToCreate == null) + setState(() { + timeProgressToCreate = timeProgress; + }); + } + void onTimeProgressChanged( TimeProgress newTimeProgress, bool isNewProgressValid) { setState(() { @@ -37,25 +46,34 @@ class _ProgressCreationScreenState extends State { ), body: Container( padding: EdgeInsets.all(12), - child: ProgressEditorWidget( - timeProgress: timeProgressToCreate, - onTimeProgressChanged: onTimeProgressChanged, - ), + child: StoreConnector( + onInit: loadSettingsIfUnloaded, + converter: (store) => _ViewModel.create(store), + builder: (context, _ViewModel viewModel) { + initTimeProgress(viewModel.defaultDurationProgress); + return ProgressEditorWidget( + timeProgress: timeProgressToCreate, + onTimeProgressChanged: onTimeProgressChanged, + ); + }), ), floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat, floatingActionButton: Row( children: [ Expanded( - child: FloatingActionButton( - heroTag: "createTimeProgressBTN", - child: Icon(Icons.save), - onPressed: _isProgressValid - ? () { - StoreProvider.of(context).dispatch( - AddTimeProgressAction(timeProgressToCreate)); - Navigator.pop(context); - } - : null, + child: StoreConnector( + onInit: loadSettingsIfUnloaded, + converter: (store) => _ViewModel.create(store), + builder: (context, _ViewModel vm) => FloatingActionButton( + heroTag: "createTimeProgressBTN", + child: Icon(Icons.save), + onPressed: _isProgressValid + ? () { + vm.onAddTimeProgress(timeProgressToCreate); + Navigator.pop(context); + } + : null, + ), ), ), Expanded( @@ -72,3 +90,27 @@ class _ProgressCreationScreenState extends State { ); } } + +class _ViewModel { + final TimeProgress defaultDurationProgress; + final void Function(TimeProgress) onAddTimeProgress; + + _ViewModel({ + @required this.defaultDurationProgress, + @required this.onAddTimeProgress, + }); + + factory _ViewModel.create(Store store) { + AppSettings settings = appSettingsSelector(store.state); + + _onAddTimeProgress(TimeProgress tp) { + if (TimeProgress.isValid(tp)) store.dispatch(AddTimeProgressAction(tp)); + } + + return _ViewModel( + defaultDurationProgress: + TimeProgress.defaultFromDuration(settings.duration), + onAddTimeProgress: _onAddTimeProgress, + ); + } +} diff --git a/lib/widgets/home/tabs/home_settings_tab.dart b/lib/widgets/home/tabs/home_settings_tab.dart index 4af6a3c..277a30f 100644 --- a/lib/widgets/home/tabs/home_settings_tab.dart +++ b/lib/widgets/home/tabs/home_settings_tab.dart @@ -7,24 +7,15 @@ 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'; class HomeSettingsTab extends StatelessWidget { @override Widget build(BuildContext context) { - return StoreConnector( + return StoreConnector( onInit: loadSettingsIfUnloaded, - converter: (store) => appSettingsSelector(store.state), - builder: (context, AppSettings settings) { - Store store = StoreProvider.of(context); - void updateDoneColor(Color newDoneColor) => store.dispatch( - UpdateAppSettingsActions( - settings.copyWith(doneColor: newDoneColor)), - ); - void updateLeftColor(Color newLeftColor) => store.dispatch( - UpdateAppSettingsActions( - settings.copyWith(leftColor: newLeftColor)), - ); - + converter: (store) => _ViewModel.create(store), + builder: (context, _ViewModel vm) { return Container( padding: EdgeInsets.all(16), child: Center( @@ -32,10 +23,16 @@ class HomeSettingsTab extends StatelessWidget { children: [ Expanded( child: ColorSettingsWidget( - doneColor: settings.doneColor, - leftColor: settings.leftColor, - updateDoneColor: updateDoneColor, - updateLeftColor: updateLeftColor, + doneColor: vm.doneColor, + leftColor: vm.leftColor, + updateDoneColor: vm.onDoneColorChanged, + updateLeftColor: vm.onLeftColorChanged, + ), + ), + Expanded( + child: DurationSettingsWidget( + duration: vm.duration, + updateDuration: vm.onDurationChanged, ), ), Spacer(), @@ -60,3 +57,39 @@ 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/direct_select_item.dart b/lib/widgets/home/tabs/settings/direct_select_item.dart deleted file mode 100644 index fad22ab..0000000 --- a/lib/widgets/home/tabs/settings/direct_select_item.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'package:flutter/material.dart'; - -class DirectSelectItem extends StatelessWidget { - final String title; - final bool isForList; - - DirectSelectItem({this.title, this.isForList}); - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 60, - child: isForList - ? Padding( - child: _buildItem(context), - padding: EdgeInsets.all(10), - ) - : Card( - margin: EdgeInsets.symmetric(horizontal: 10), - child: Stack( - children: [ - _buildItem(context), - Align( - alignment: Alignment.centerRight, - child: Icon(Icons.arrow_drop_down), - ) - ], - ), - )); - } - - Container _buildItem(BuildContext context) { - return Container( - width: MediaQuery.of(context).size.width, - alignment: Alignment.center, - child: Text(title), - ); - } -} diff --git a/lib/widgets/home/tabs/settings/duration_settings_widget.dart b/lib/widgets/home/tabs/settings/duration_settings_widget.dart new file mode 100644 index 0000000..5a96717 --- /dev/null +++ b/lib/widgets/home/tabs/settings/duration_settings_widget.dart @@ -0,0 +1,61 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_picker/flutter_picker.dart'; + +class DurationSettingsWidget extends StatelessWidget { + final Duration duration; + final void Function(Duration) updateDuration; + + DurationSettingsWidget({ + @required this.duration, + @required this.updateDuration, + }); + + @override + Widget build(BuildContext 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"), + ), + ), + ], + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index 9ffd8b6..6e3f677 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -78,13 +78,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.1.3" - direct_select: - dependency: "direct main" - description: - name: direct_select - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.0" fake_async: dependency: transitive description: @@ -125,6 +118,15 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.8.1" + flutter_picker: + dependency: "direct main" + description: + path: "." + ref: HEAD + resolved-ref: e95d121f54faba889fbf8a850c86dd5cf4aa5c5a + url: "git://github.com/yangyxd/flutter_picker.git" + source: git + version: "1.1.5" flutter_redux: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 6ceb5b7..51d9030 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.17+17 +version: 0.0.18+18 environment: sdk: ">=2.7.0 <3.0.0" @@ -25,7 +25,8 @@ dependencies: sdk: flutter flutter_colorpicker: flutter_redux: - direct_select: + flutter_picker: + git: git://github.com/yangyxd/flutter_picker.git meta: package_info: percent_indicator: