Feature/BugFix-01 Future Time Progresses (#4)

- Fixed Bug with Future Time Progresses
  Now no longer shows future time progresses in dashboard or in app drawer
- Dashboard Now Shows started and future times.
  These cards are divided based on their count
- Fixed Bug Future Progress In Detail Screen
- Progress Detail Screen Now Shows in how many Days a progress starts
- BugFix App Version

Signed-off-by: Andreas Fahrecker <AndreasFahrecker@gmail.com>
This commit is contained in:
Andreas Fahrecker 2020-12-04 05:15:04 +01:00 committed by GitHub
parent 988e8f3c72
commit 319f539b48
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 245 additions and 60 deletions

View File

@ -10,8 +10,13 @@ class TimeProgressTrackerApp extends StatelessWidget {
static const String name = "Time Progress Tracker"; static const String name = "Time Progress Tracker";
final Store<AppState> store; final Store<AppState> store;
final String appVersion;
TimeProgressTrackerApp({Key key, this.store}) : super(key: key); TimeProgressTrackerApp({
Key key,
this.store,
this.appVersion,
}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -26,11 +31,17 @@ class TimeProgressTrackerApp extends StatelessWidget {
initialRoute: ProgressDashboardScreen.routeName, initialRoute: ProgressDashboardScreen.routeName,
routes: { routes: {
ProgressDashboardScreen.routeName: (BuildContext context) => ProgressDashboardScreen.routeName: (BuildContext context) =>
ProgressDashboardScreen(), ProgressDashboardScreen(
appVersion: appVersion,
),
ProgressDetailScreen.routeName: (BuildContext context) => ProgressDetailScreen.routeName: (BuildContext context) =>
ProgressDetailScreen(), ProgressDetailScreen(
appVersion: appVersion,
),
ProgressCreationScreen.routeName: (BuildContext context) => ProgressCreationScreen.routeName: (BuildContext context) =>
ProgressCreationScreen(), ProgressCreationScreen(
appVersion: appVersion,
),
}, },
), ),
); );

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:package_info/package_info.dart';
import 'package:redux/redux.dart'; import 'package:redux/redux.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:time_progress_tracker/app.dart'; import 'package:time_progress_tracker/app.dart';
@ -18,5 +19,6 @@ Future<void> main() async {
TimeProgressRepository(await SharedPreferences.getInstance()), TimeProgressRepository(await SharedPreferences.getInstance()),
), ),
), ),
appVersion: (await PackageInfo.fromPlatform()).version,
)); ));
} }

View File

@ -11,6 +11,11 @@ import 'package:time_progress_tracker/widgets/app_drawer_widget.dart';
class ProgressCreationScreen extends StatefulWidget { class ProgressCreationScreen extends StatefulWidget {
static const routeName = "/progress-creation"; static const routeName = "/progress-creation";
final String appVersion;
ProgressCreationScreen({Key key, @required this.appVersion})
: super(key: key);
@override @override
State<StatefulWidget> createState() { State<StatefulWidget> createState() {
return _ProgressCreationScreenState(); return _ProgressCreationScreenState();
@ -60,7 +65,9 @@ class _ProgressCreationScreenState extends State<ProgressCreationScreen> {
appBar: AppBar( appBar: AppBar(
title: Text("Create Time Progress"), title: Text("Create Time Progress"),
), ),
drawer: AppDrawer(), drawer: AppDrawer(
appVersion: widget.appVersion,
),
body: Container( body: Container(
padding: EdgeInsets.all(8), padding: EdgeInsets.all(8),
child: Column( child: Column(

View File

@ -7,19 +7,31 @@ import 'package:time_progress_tracker/models/app_state.dart';
import 'package:time_progress_tracker/models/time_progress.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_creation_screen.dart';
import 'package:time_progress_tracker/screens/progress_detail_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/app_drawer_widget.dart';
class ProgressDashboardScreen extends StatelessWidget { class ProgressDashboardScreen extends StatelessWidget {
static const routeName = "/progress-dashboard"; static const routeName = "/progress-dashboard";
static const title = "Time Progress Dashboard"; static const title = "Time Progress Dashboard";
final String appVersion;
ProgressDashboardScreen({
Key key,
@required this.appVersion,
}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
AppBar appBar = AppBar(
title: Text(title),
);
return Scaffold( return Scaffold(
appBar: AppBar( appBar: appBar,
title: Text(title), drawer: AppDrawer(
appVersion: appVersion,
), ),
drawer: AppDrawer(),
body: StoreConnector( body: StoreConnector(
converter: _ViewModel.fromStore, converter: _ViewModel.fromStore,
onInit: loadTimeProgressListIfUnloaded, onInit: loadTimeProgressListIfUnloaded,
@ -29,11 +41,11 @@ class ProgressDashboardScreen extends StatelessWidget {
child: CircularProgressIndicator(), child: CircularProgressIndicator(),
); );
} }
List<Widget> dashboardTileList = List<Widget>();
if (vm.timeProgressList.length > 0) { List<Widget> startedProgressesTileList = List<Widget>();
for (TimeProgress tp in vm.timeProgressList) { if (vm.hasStartedProgresses) {
dashboardTileList.add( for (TimeProgress tp in vm.startedTimeProgreses) {
startedProgressesTileList.add(
Card( Card(
child: ListTile( child: ListTile(
title: Text(tp.name), title: Text(tp.name),
@ -53,20 +65,82 @@ class ProgressDashboardScreen extends StatelessWidget {
), ),
); );
} }
} else { }
dashboardTileList.add(ListTile(
title: Text("You don't have any tracked Progress."), List<Widget> futureProgressesTileList = List<Widget>();
if (vm.hasFutureProgresses) {
for (TimeProgress tp in vm.futureTimeProgresses) {
futureProgressesTileList.add(
Card(
child: ListTile(
title: Text(tp.name),
subtitle: Text(
"Starts in ${tp.startTime.difference(DateTime.now()).inDays} Days."),
onTap: () {
Navigator.pushNamed(
context, ProgressDetailScreen.routeName,
arguments: ProgressDetailScreenArguments(tp.id));
},
),
),
);
}
}
double dividerHeight = 1;
double screenHeight = MediaQuery.of(context).size.height -
appBar.preferredSize.height -
24 -
dividerHeight; //Divider
List<Widget> columnChildren = List<Widget>();
int tpCount =
vm.startedTimeProgreses.length + vm.futureTimeProgresses.length;
if (vm.hasStartedProgresses) {
columnChildren.add(Container(
height: vm.hasFutureProgresses
? (screenHeight / tpCount) * vm.startedTimeProgreses.length
: screenHeight,
child: ListView(
padding: EdgeInsets.all(8),
children: startedProgressesTileList,
),
));
}
if (vm.hasStartedProgresses && vm.hasFutureProgresses) {
columnChildren.add(Divider(
height: dividerHeight,
));
}
if (vm.hasFutureProgresses) {
columnChildren.add(Container(
height: vm.hasStartedProgresses
? (screenHeight / tpCount) * vm.futureTimeProgresses.length
: screenHeight,
child: ListView(
padding: EdgeInsets.all(8),
children: futureProgressesTileList,
),
)); ));
} }
return ListView( if (!vm.hasStartedProgresses && !vm.hasFutureProgresses) {
padding: EdgeInsets.all(8), columnChildren.add(Container(
children: dashboardTileList, margin: EdgeInsets.all(16),
child: Center(
child: Text("You don't have any tracked Progress."),
),
));
}
return Column(
children: columnChildren,
); );
}, },
), ),
floatingActionButtonLocation: FloatingActionButtonLocation.endFloat, floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
floatingActionButton: FloatingActionButton( floatingActionButton: FloatingActionButton(
heroTag: "createProgressBTN",
child: Icon(Icons.add), child: Icon(Icons.add),
onPressed: () { onPressed: () {
Navigator.pushNamed(context, ProgressCreationScreen.routeName); Navigator.pushNamed(context, ProgressCreationScreen.routeName);
@ -77,17 +151,30 @@ class ProgressDashboardScreen extends StatelessWidget {
} }
class _ViewModel { class _ViewModel {
final List<TimeProgress> timeProgressList; final List<TimeProgress> startedTimeProgreses;
final bool hasStartedProgresses;
final List<TimeProgress> futureTimeProgresses;
final bool hasFutureProgresses;
final bool hasLoaded; final bool hasLoaded;
_ViewModel({ _ViewModel({
@required this.timeProgressList, @required this.startedTimeProgreses,
@required this.hasStartedProgresses,
@required this.futureTimeProgresses,
@required this.hasFutureProgresses,
@required this.hasLoaded, @required this.hasLoaded,
}); });
static _ViewModel fromStore(Store<AppState> store) { static _ViewModel fromStore(Store<AppState> store) {
List<TimeProgress> startedTPList =
startedTimeProgressesSelector(store.state);
List<TimeProgress> furtureTPList =
futureTimeProgressesSelector(store.state);
return _ViewModel( return _ViewModel(
timeProgressList: store.state.timeProgressList, startedTimeProgreses: startedTPList,
hasStartedProgresses: startedTPList.length > 0,
futureTimeProgresses: furtureTPList,
hasFutureProgresses: furtureTPList.length > 0,
hasLoaded: store.state.hasLoaded, hasLoaded: store.state.hasLoaded,
); );
} }

View File

@ -24,6 +24,13 @@ class ProgressDetailScreenArguments {
class ProgressDetailScreen extends StatefulWidget { class ProgressDetailScreen extends StatefulWidget {
static const routeName = "/progress-detail"; static const routeName = "/progress-detail";
final String appVersion;
ProgressDetailScreen({
Key key,
@required this.appVersion,
}) : super(key: key);
@override @override
State<StatefulWidget> createState() { State<StatefulWidget> createState() {
return _ProgressDetailScreenState(); return _ProgressDetailScreenState();
@ -147,7 +154,9 @@ class _ProgressDetailScreenState extends State<ProgressDetailScreen> {
appBar: AppBar( appBar: AppBar(
title: Text("Progress"), title: Text("Progress"),
), ),
drawer: AppDrawer(), drawer: AppDrawer(
appVersion: widget.appVersion,
),
body: Container( body: Container(
margin: EdgeInsets.all(8), margin: EdgeInsets.all(8),
child: StoreConnector( child: StoreConnector(
@ -170,32 +179,58 @@ class _ProgressDetailScreenState extends State<ProgressDetailScreen> {
: "The Name of the Time Progress has to be set.", : "The Name of the Time Progress has to be set.",
), ),
) )
: FittedBox( : vm.hasProgressStarted
fit: BoxFit.fitWidth, ? FittedBox(
child: Text( fit: BoxFit.fitWidth,
vm.timeProgress.name, child: Text(
style: TextStyle( vm.timeProgress.name,
fontWeight: FontWeight.bold, textAlign: TextAlign.center,
color: Colors.black87, style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.black87,
),
),
)
: Center(
child: FittedBox(
fit: BoxFit.fitWidth,
child: Text(
vm.timeProgress.name,
textAlign: TextAlign.center,
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.black87,
),
),
),
), ),
), ),
vm.hasProgressStarted
? Expanded(
flex: 2,
child: ProgressDetailCircularPercent(
percentDone: _isBeingEdited
? _editedProgress.percentDone()
: vm.timeProgress.percentDone(),
), ),
), )
Expanded( : Expanded(
flex: 2, flex: 2,
child: ProgressDetailCircularPercent( child: Text(
percentDone: _isBeingEdited "Starts in ${vm.timeProgress.startTime.difference(DateTime.now()).inDays} Days."),
? _editedProgress.percentDone() ),
: vm.timeProgress.percentDone(), vm.hasProgressStarted
), ? Expanded(
), flex: 1,
Expanded( child: ProgressDetailLinearPercent(
flex: 1, timeProgress: _isBeingEdited
child: ProgressDetailLinearPercent( ? _editedProgress
timeProgress: : vm.timeProgress,
_isBeingEdited ? _editedProgress : vm.timeProgress, ),
), )
), : Spacer(
flex: 1,
),
Expanded( Expanded(
flex: 1, flex: 1,
child: Text( child: Text(
@ -242,15 +277,19 @@ class _ProgressDetailScreenState extends State<ProgressDetailScreen> {
class _ViewModel { class _ViewModel {
final TimeProgress timeProgress; final TimeProgress timeProgress;
final bool hasProgressStarted;
_ViewModel({ _ViewModel({
@required this.timeProgress, @required this.timeProgress,
@required this.hasProgressStarted,
}); });
static _ViewModel fromStoreAndArg( static _ViewModel fromStoreAndArg(
Store<AppState> store, ProgressDetailScreenArguments args) { Store<AppState> store, ProgressDetailScreenArguments args) {
TimeProgress tp = timeProgressByIdSelector(store.state, args.id);
return _ViewModel( return _ViewModel(
timeProgress: timeProgressByIdSelector(store.state, args.id), timeProgress: tp,
); hasProgressStarted: DateTime.now().millisecondsSinceEpoch >
tp.startTime.millisecondsSinceEpoch);
} }
} }

View File

@ -4,6 +4,20 @@ import 'package:time_progress_tracker/models/time_progress.dart';
List<TimeProgress> timeProgressListSelector(AppState state) => List<TimeProgress> timeProgressListSelector(AppState state) =>
state.timeProgressList; state.timeProgressList;
List<TimeProgress> startedTimeProgressesSelector(AppState state) =>
state.timeProgressList
.where((timeProgress) =>
DateTime.now().millisecondsSinceEpoch >=
timeProgress.startTime.millisecondsSinceEpoch)
.toList();
List<TimeProgress> futureTimeProgressesSelector(AppState state) =>
state.timeProgressList
.where((timeProgress) =>
DateTime.now().millisecondsSinceEpoch <
timeProgress.startTime.millisecondsSinceEpoch)
.toList();
TimeProgress timeProgressByIdSelector(AppState state, String id) { TimeProgress timeProgressByIdSelector(AppState state, String id) {
if (state.timeProgressList.length < 1) return null; if (state.timeProgressList.length < 1) return null;
return state.timeProgressList return state.timeProgressList

View File

@ -9,8 +9,16 @@ import 'package:time_progress_tracker/models/app_state.dart';
import 'package:time_progress_tracker/models/time_progress.dart'; import 'package:time_progress_tracker/models/time_progress.dart';
import 'package:time_progress_tracker/screens/progress_dashboard_screen.dart'; import 'package:time_progress_tracker/screens/progress_dashboard_screen.dart';
import 'package:time_progress_tracker/screens/progress_detail_screen.dart'; import 'package:time_progress_tracker/screens/progress_detail_screen.dart';
import 'package:time_progress_tracker/selectors/time_progress_selectors.dart';
class AppDrawer extends StatelessWidget { class AppDrawer extends StatelessWidget {
final String appVersion;
AppDrawer({
Key key,
@required this.appVersion,
}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Drawer( return Drawer(
@ -18,6 +26,11 @@ class AppDrawer extends StatelessWidget {
converter: _ViewModel.fromStore, converter: _ViewModel.fromStore,
onInit: loadTimeProgressListIfUnloaded, onInit: loadTimeProgressListIfUnloaded,
builder: (context, _ViewModel vm) { builder: (context, _ViewModel vm) {
if (!vm.hasLoaded) {
return Center(
child: CircularProgressIndicator(),
);
}
List<Widget> drawerTileList = List<Widget>(); List<Widget> drawerTileList = List<Widget>();
drawerTileList.add(DrawerHeader( drawerTileList.add(DrawerHeader(
child: Text(TimeProgressTrackerApp.name), child: Text(TimeProgressTrackerApp.name),
@ -36,8 +49,8 @@ class AppDrawer extends StatelessWidget {
}, },
), ),
)); ));
if (vm.timeProgressList.length > 0) { if (vm.startedTimeProgresses.length > 0) {
for (TimeProgress tp in vm.timeProgressList) { for (TimeProgress tp in vm.startedTimeProgresses) {
drawerTileList.add(ListTile( drawerTileList.add(ListTile(
title: Text(tp.name), title: Text(tp.name),
trailing: CircularPercentIndicator( trailing: CircularPercentIndicator(
@ -60,7 +73,7 @@ class AppDrawer extends StatelessWidget {
); );
}, },
)); ));
if (vm.timeProgressList.last != tp) { if (vm.startedTimeProgresses.last != tp) {
drawerTileList.add(Divider( drawerTileList.add(Divider(
color: Colors.black12, color: Colors.black12,
)); ));
@ -80,11 +93,10 @@ class AppDrawer extends StatelessWidget {
title: Text("About"), title: Text("About"),
onTap: () { onTap: () {
showAboutDialog( showAboutDialog(
context: context, context: context,
applicationName: TimeProgressTrackerApp.name, applicationName: TimeProgressTrackerApp.name,
applicationVersion: ' Version 0.0.1', applicationVersion: " Version $appVersion",
applicationLegalese: '\u00a9Andreas Fahrecker 2020' applicationLegalese: '\u00a9Andreas Fahrecker 2020');
);
}, },
), ),
)); ));
@ -98,13 +110,18 @@ class AppDrawer extends StatelessWidget {
} }
class _ViewModel { class _ViewModel {
final List<TimeProgress> timeProgressList; final List<TimeProgress> startedTimeProgresses;
final bool hasLoaded;
_ViewModel({@required this.timeProgressList}); _ViewModel({
@required this.startedTimeProgresses,
@required this.hasLoaded,
});
static _ViewModel fromStore(Store<AppState> store) { static _ViewModel fromStore(Store<AppState> store) {
return _ViewModel( return _ViewModel(
timeProgressList: store.state.timeProgressList, startedTimeProgresses: startedTimeProgressesSelector(store.state),
hasLoaded: store.state.hasLoaded,
); );
} }
} }

View File

@ -156,6 +156,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.3.0-nullsafety.3" version: "1.3.0-nullsafety.3"
package_info:
dependency: "direct main"
description:
name: package_info
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.3+2"
path: path:
dependency: transitive dependency: transitive
description: description:

View File

@ -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. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at # Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 0.0.2+2 version: 0.0.3+3
environment: environment:
sdk: ">=2.7.0 <3.0.0" sdk: ">=2.7.0 <3.0.0"
@ -25,6 +25,7 @@ dependencies:
sdk: flutter sdk: flutter
flutter_redux: flutter_redux:
meta: meta:
package_info:
percent_indicator: percent_indicator:
redux: redux:
shared_preferences: shared_preferences: