diff --git a/lib/app.dart b/lib/app.dart index d08d099..6e416fc 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -10,12 +10,10 @@ class TimeProgressTrackerApp extends StatelessWidget { static const String name = "Time Progress Tracker"; final Store store; - final String appVersion; TimeProgressTrackerApp({ Key key, this.store, - this.appVersion, }) : super(key: key); @override @@ -33,17 +31,11 @@ class TimeProgressTrackerApp extends StatelessWidget { initialRoute: ProgressDashboardScreen.routeName, routes: { ProgressDashboardScreen.routeName: (BuildContext context) => - ProgressDashboardScreen( - appVersion: appVersion, - ), + ProgressDashboardScreen(), ProgressDetailScreen.routeName: (BuildContext context) => - ProgressDetailScreen( - appVersion: appVersion, - ), + ProgressDetailScreen(), ProgressCreationScreen.routeName: (BuildContext context) => - ProgressCreationScreen( - appVersion: appVersion, - ), + ProgressCreationScreen(), }, ), ); diff --git a/lib/main.dart b/lib/main.dart index af21c77..5d11f18 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -19,6 +19,5 @@ Future main() async { TimeProgressRepository(await SharedPreferences.getInstance()), ), ), - appVersion: (await PackageInfo.fromPlatform()).version, )); } diff --git a/lib/models/time_progress.dart b/lib/models/time_progress.dart index 8675720..dbd9cc0 100644 --- a/lib/models/time_progress.dart +++ b/lib/models/time_progress.dart @@ -19,7 +19,8 @@ class TimeProgress { factory TimeProgress.initialDefault() { int thisYear = DateTime.now().year; - return TimeProgress("Initial Name", DateTime(thisYear - 1), DateTime(thisYear + 1)); + return TimeProgress( + "Initial Name", DateTime(thisYear - 1), DateTime(thisYear + 1)); } TimeProgress copyWith( @@ -48,6 +49,16 @@ class TimeProgress { return this.daysBehind() / (this.allDays() / 100) / 100; } + bool hasStarted() { + return DateTime.now().millisecondsSinceEpoch > + startTime.millisecondsSinceEpoch; + } + + bool hasEnded() { + return DateTime.now().millisecondsSinceEpoch > + endTime.millisecondsSinceEpoch; + } + @override int get hashCode => id.hashCode ^ name.hashCode ^ startTime.hashCode ^ endTime.hashCode; diff --git a/lib/screens/progress_creation_screen.dart b/lib/screens/progress_creation_screen.dart index 94c9407..a36f18c 100644 --- a/lib/screens/progress_creation_screen.dart +++ b/lib/screens/progress_creation_screen.dart @@ -11,11 +11,6 @@ import 'package:time_progress_tracker/widgets/app_drawer_widget.dart'; class ProgressCreationScreen extends StatefulWidget { static const routeName = "/progress-creation"; - final String appVersion; - - ProgressCreationScreen({Key key, @required this.appVersion}) - : super(key: key); - @override State createState() { return _ProgressCreationScreenState(); @@ -67,9 +62,9 @@ class _ProgressCreationScreenState extends State { appBar: AppBar( title: Text("Create Time Progress"), ), - drawer: AppDrawer( + /*drawer: AppDrawer( appVersion: widget.appVersion, - ), + ),*/ body: Container( padding: EdgeInsets.all(8), child: Column( diff --git a/lib/screens/progress_dashboard_screen.dart b/lib/screens/progress_dashboard_screen.dart index 8382997..cc33250 100644 --- a/lib/screens/progress_dashboard_screen.dart +++ b/lib/screens/progress_dashboard_screen.dart @@ -1,38 +1,49 @@ import 'package:flutter/material.dart'; -import 'package:flutter_redux/flutter_redux.dart'; -import 'package:percent_indicator/linear_percent_indicator.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 'package:time_progress_tracker/screens/progress_creation_screen.dart'; -import 'package:time_progress_tracker/screens/progress_detail_screen.dart'; import 'package:time_progress_tracker/selectors/time_progress_selectors.dart'; -import 'package:time_progress_tracker/widgets/app_drawer_widget.dart'; +import 'package:time_progress_tracker/widgets/home_widgets/home_bottom_navbar.dart'; +import 'package:time_progress_tracker/widgets/home_widgets/home_active_progresses_tab.dart'; +import 'package:time_progress_tracker/widgets/home_widgets/home_inactive_progresses_tab.dart'; +import 'package:time_progress_tracker/widgets/home_widgets/home_settings_tab.dart'; -class ProgressDashboardScreen extends StatelessWidget { +class ProgressDashboardScreen extends StatefulWidget { static const routeName = "/progress-dashboard"; - static const title = "Time Progress Dashboard"; + static const title = "Time Progress Tracker"; - final String appVersion; + @override + State createState() { + return _ProgressDashboardScreenState(); + } +} - ProgressDashboardScreen({ - Key key, - @required this.appVersion, - }) : super(key: key); +class _ProgressDashboardScreenState extends State { + int _currentIndex = 0; + final List _children = [ + HomeActiveProgressesTab(), + HomeInactiveProgressesTab(), + HomeSettingsTab(), + ]; + + void onBottomTabTapped(int index) { + setState(() { + _currentIndex = index; + }); + } @override Widget build(BuildContext context) { - AppBar appBar = AppBar( - title: Text(title), - ); + ThemeData appTheme = Theme.of(context); return Scaffold( - appBar: appBar, - drawer: AppDrawer( - appVersion: appVersion, + appBar: AppBar( + title: Text(ProgressDashboardScreen.title), ), - body: StoreConnector( + body: _children[_currentIndex], + /* + StoreConnector( converter: _ViewModel.fromStore, onInit: loadTimeProgressListIfUnloaded, builder: (BuildContext context, _ViewModel vm) { @@ -43,29 +54,6 @@ class ProgressDashboardScreen extends StatelessWidget { } List startedProgressesTileList = List(); - if (vm.hasCurrentProgresses) { - for (TimeProgress tp in vm.currentTimeProgresses) { - startedProgressesTileList.add( - Card( - child: ListTile( - title: Text(tp.name), - subtitle: LinearPercentIndicator( - center: Text("${(tp.percentDone() * 100).floor()} %"), - percent: tp.percentDone(), - progressColor: Colors.green, - backgroundColor: Colors.red, - lineHeight: 20, - ), - onTap: () { - Navigator.pushNamed( - context, ProgressDetailScreen.routeName, - arguments: ProgressDetailScreenArguments(tp.id)); - }, - ), - ), - ); - } - } List futureProgressesTileList = List(); if (vm.hasFutureProgresses) { @@ -107,8 +95,7 @@ class ProgressDashboardScreen extends StatelessWidget { double dividerHeight = 1; double screenHeight = MediaQuery.of(context).size.height - - appBar.preferredSize.height - - 24 - + 50 - dividerHeight - 1; //Divider @@ -118,43 +105,13 @@ class ProgressDashboardScreen extends StatelessWidget { vm.pastTimeProgresses.length; if (vm.hasCurrentProgresses) { columnChildren.add(Container( - height: - (screenHeight / tpCount) * vm.currentTimeProgresses.length, + height: screenHeight - 100, child: ListView( padding: EdgeInsets.all(8), children: startedProgressesTileList, ), )); } - if (vm.hasCurrentProgresses && vm.hasFutureProgresses) { - columnChildren.add(Divider( - height: dividerHeight, - )); - } - if (vm.hasFutureProgresses) { - columnChildren.add(Container( - height: (screenHeight / tpCount) * vm.futureTimeProgresses.length, - child: ListView( - padding: EdgeInsets.all(8), - children: futureProgressesTileList, - ), - )); - } - if ((vm.hasCurrentProgresses || vm.hasFutureProgresses) && - vm.pastTimeProgresses.length > 0) { - columnChildren.add(Divider( - height: dividerHeight, - )); - } - if (vm.pastTimeProgresses.length > 0) { - columnChildren.add(Container( - height: (screenHeight / tpCount) * vm.pastTimeProgresses.length, - child: ListView( - padding: EdgeInsets.all(8), - children: pastProgressesTileList, - ), - )); - } if (!vm.hasCurrentProgresses && !vm.hasFutureProgresses && @@ -171,14 +128,21 @@ class ProgressDashboardScreen extends StatelessWidget { children: columnChildren, ); }, - ), + ) + */ floatingActionButtonLocation: FloatingActionButtonLocation.endFloat, - floatingActionButton: FloatingActionButton( - heroTag: "createProgressBTN", - child: Icon(Icons.add), - onPressed: () { - Navigator.pushNamed(context, ProgressCreationScreen.routeName); - }, + floatingActionButton: _currentIndex != 2 + ? FloatingActionButton( + heroTag: "createProgressBTN", + child: Icon(Icons.add), + onPressed: () { + Navigator.pushNamed(context, ProgressCreationScreen.routeName); + }, + ) + : null, + bottomNavigationBar: HomeBottomNavBar( + currentIndex: _currentIndex, + onTap: onBottomTabTapped, ), ); } diff --git a/lib/screens/progress_detail_screen.dart b/lib/screens/progress_detail_screen.dart index e5c9287..f465a62 100644 --- a/lib/screens/progress_detail_screen.dart +++ b/lib/screens/progress_detail_screen.dart @@ -24,13 +24,6 @@ class ProgressDetailScreenArguments { class ProgressDetailScreen extends StatefulWidget { static const routeName = "/progress-detail"; - final String appVersion; - - ProgressDetailScreen({ - Key key, - @required this.appVersion, - }) : super(key: key); - @override State createState() { return _ProgressDetailScreenState(); @@ -154,9 +147,9 @@ class _ProgressDetailScreenState extends State { appBar: AppBar( title: Text("Progress"), ), - drawer: AppDrawer( + /*drawer: AppDrawer( appVersion: widget.appVersion, - ), + ),*/ body: Container( margin: EdgeInsets.all(8), child: StoreConnector( diff --git a/lib/selectors/time_progress_selectors.dart b/lib/selectors/time_progress_selectors.dart index 69b7dae..328679a 100644 --- a/lib/selectors/time_progress_selectors.dart +++ b/lib/selectors/time_progress_selectors.dart @@ -4,6 +4,21 @@ import 'package:time_progress_tracker/models/time_progress.dart'; List timeProgressListSelector(AppState state) => state.timeProgressList; +List activeTimeProgressesSelector(AppState state) { + return state.timeProgressList + .where((timeProgress) => + timeProgress.hasStarted() && !timeProgress.hasEnded()) + .toList(); +} + +List inactiveTimeProgressesSelector(AppState state) { + return state.timeProgressList + .where((timeProgress) => + !timeProgress.hasStarted() || timeProgress.hasEnded()) + .toList(); +} + +@Deprecated("use active TimeProgresses Selector instead.") List currentTimeProgressSelector(AppState state) { int currentTime = DateTime.now().millisecondsSinceEpoch; return state.timeProgressList diff --git a/lib/widgets/home_widgets/home_active_progresses_tab.dart b/lib/widgets/home_widgets/home_active_progresses_tab.dart new file mode 100644 index 0000000..8ad92df --- /dev/null +++ b/lib/widgets/home_widgets/home_active_progresses_tab.dart @@ -0,0 +1,57 @@ +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 'package:time_progress_tracker/selectors/time_progress_selectors.dart'; +import 'package:time_progress_tracker/widgets/home_widgets/home_progress_list_tile.dart'; + +class HomeActiveProgressesTab extends StatelessWidget { + @override + Widget build(BuildContext context) { + ThemeData appTheme = Theme.of(context); + return StoreConnector( + converter: _ViewModel.fromStore, + onInit: loadTimeProgressListIfUnloaded, + builder: (BuildContext scContext, _ViewModel vm) { + if (!vm.hasLoaded) + return Center( + child: CircularProgressIndicator(), + ); + if (vm.activeTimeProgresses.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: vm.activeTimeProgresses + .map((timeProgress) => HomeProgressListTile( + timeProgress: timeProgress, + )) + .toList(), + ); + }); + } +} + +class _ViewModel { + final List activeTimeProgresses; + final bool hasLoaded; + + _ViewModel({ + @required this.activeTimeProgresses, + @required this.hasLoaded, + }); + + static _ViewModel fromStore(Store store) { + return _ViewModel( + activeTimeProgresses: activeTimeProgressesSelector(store.state), + hasLoaded: store.state.hasLoaded, + ); + } +} diff --git a/lib/widgets/home_widgets/home_bottom_navbar.dart b/lib/widgets/home_widgets/home_bottom_navbar.dart new file mode 100644 index 0000000..3dd666b --- /dev/null +++ b/lib/widgets/home_widgets/home_bottom_navbar.dart @@ -0,0 +1,45 @@ +import 'package:flutter/material.dart'; + +class HomeBottomNavBar extends StatelessWidget { + final int currentIndex; + final Function onTap; + + HomeBottomNavBar({ + Key key, + @required this.currentIndex, + @required this.onTap, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + ThemeData appTheme = Theme.of(context); + + return BottomNavigationBar( + onTap: onTap, + currentIndex: currentIndex, + items: [ + BottomNavigationBarItem( + icon: new Icon( + Icons.alarm, + color: appTheme.primaryColor, + ), + label: "Active Progresses", + ), + BottomNavigationBarItem( + icon: new Icon( + Icons.alarm_off, + color: appTheme.primaryColor, + ), + label: "Inactive Progresses", + ), + BottomNavigationBarItem( + icon: new Icon( + Icons.settings, + color: appTheme.primaryColor, + ), + label: "Settings", + ) + ], + ); + } +} diff --git a/lib/widgets/home_widgets/home_inactive_progresses_tab.dart b/lib/widgets/home_widgets/home_inactive_progresses_tab.dart new file mode 100644 index 0000000..6705205 --- /dev/null +++ b/lib/widgets/home_widgets/home_inactive_progresses_tab.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_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/home_widgets/home_progress_list_tile.dart'; + +class HomeInactiveProgressesTab extends StatelessWidget { + @override + Widget build(BuildContext context) { + ThemeData appTheme = Theme.of(context); + // TODO: implement build + return StoreConnector( + converter: _ViewModel.fromStore, + onInit: loadTimeProgressListIfUnloaded, + builder: (BuildContext scContext, _ViewModel vm) { + if (!vm.hasLoaded) + return Center( + child: CircularProgressIndicator(), + ); + if (vm.inactiveTimeProgresses.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: vm.inactiveTimeProgresses + .map((timeProgress) => + HomeProgressListTile(timeProgress: timeProgress)) + .toList(), + ); + }); + return Container( + child: Text("Inactive Progresses"), + ); + } +} + +class _ViewModel { + final List inactiveTimeProgresses; + final bool hasLoaded; + + _ViewModel({ + @required this.inactiveTimeProgresses, + @required this.hasLoaded, + }); + + static _ViewModel fromStore(Store store) { + return _ViewModel( + inactiveTimeProgresses: inactiveTimeProgressesSelector(store.state), + hasLoaded: store.state.hasLoaded, + ); + } +} diff --git a/lib/widgets/home_widgets/home_progress_list_tile.dart b/lib/widgets/home_widgets/home_progress_list_tile.dart new file mode 100644 index 0000000..996531a --- /dev/null +++ b/lib/widgets/home_widgets/home_progress_list_tile.dart @@ -0,0 +1,44 @@ +import 'package:flutter/cupertino.dart'; +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 HomeProgressListTile extends StatelessWidget { + final TimeProgress timeProgress; + + HomeProgressListTile({ + Key key, + @required this.timeProgress, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + Widget listTileSubTitle; + if (timeProgress.hasStarted() && !timeProgress.hasEnded()) + listTileSubTitle = LinearPercentIndicator( + center: Text("${(timeProgress.percentDone() * 100).floor()} %"), + percent: timeProgress.percentDone(), + progressColor: Colors.green, + backgroundColor: Colors.red, + lineHeight: 20, + ); + if (!timeProgress.hasStarted()) + listTileSubTitle = Text( + "Starts in ${timeProgress.startTime.difference(DateTime.now()).inDays} Days"); + if (timeProgress.hasEnded()) + listTileSubTitle = Text( + "Ended ${DateTime.now().difference(timeProgress.endTime).inDays} Days ago."); + + return Card( + child: ListTile( + title: Text(timeProgress.name), + subtitle: listTileSubTitle, + onTap: () { + Navigator.pushNamed(context, ProgressDetailScreen.routeName, + arguments: ProgressDetailScreenArguments(timeProgress.id)); + }, + ), + ); + } +} diff --git a/lib/widgets/home_widgets/home_settings_tab.dart b/lib/widgets/home_widgets/home_settings_tab.dart new file mode 100644 index 0000000..97dc0ee --- /dev/null +++ b/lib/widgets/home_widgets/home_settings_tab.dart @@ -0,0 +1,30 @@ +import 'package:flutter/material.dart'; +import 'package:package_info/package_info.dart'; +import 'package:time_progress_tracker/app.dart'; + +class HomeSettingsTab extends StatelessWidget { + @override + Widget build(BuildContext context) { + ThemeData appTheme = Theme.of(context); + + return Container( + padding: EdgeInsets.all(16), + child: Center( + child: Column( + children: [ + Text("The Settings of this App are not yet implemented."), + FlatButton( + onPressed: () { + showAboutDialog( + context: context, + applicationName: TimeProgressTrackerApp.name, + applicationVersion: "Beta", + applicationLegalese: '\u00a9Andreas Fahrecker 2020-2021'); + }, + child: Text("About")) + ], + ), + ), + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index 043a91f..a525b8c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -134,7 +134,7 @@ packages: name: image url: "https://pub.dartlang.org" source: hosted - version: "2.1.18" + version: "2.1.19" intl: dependency: transitive description: @@ -162,7 +162,7 @@ packages: name: package_info url: "https://pub.dartlang.org" source: hosted - version: "0.4.3+2" + version: "0.4.3+4" path: dependency: transitive description: @@ -197,7 +197,7 @@ packages: name: percent_indicator url: "https://pub.dartlang.org" source: hosted - version: "2.1.8" + version: "2.1.9+1" petitparser: dependency: transitive description: @@ -274,7 +274,7 @@ packages: name: shared_preferences_windows url: "https://pub.dartlang.org" source: hosted - version: "0.0.1+3" + version: "0.0.2+3" sky_engine: dependency: transitive description: flutter @@ -342,7 +342,7 @@ packages: name: win32 url: "https://pub.dartlang.org" source: hosted - version: "1.7.3" + version: "1.7.4+1" xdg_directories: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 06fbf13..4a6a9d7 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.7+7 +version: 0.0.8+8 environment: sdk: ">=2.7.0 <3.0.0"