New App Layout

Signed-off-by: Andreas Fahrecker <AndreasFahrecker@gmail.com>
This commit is contained in:
Andreas Fahrecker 2021-02-10 19:36:04 +01:00
parent c5e240e813
commit 409ccbcdda
14 changed files with 322 additions and 117 deletions

View File

@ -10,12 +10,10 @@ class TimeProgressTrackerApp extends StatelessWidget {
static const String name = "Time Progress Tracker";
final Store<AppState> 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(),
},
),
);

View File

@ -19,6 +19,5 @@ Future<void> main() async {
TimeProgressRepository(await SharedPreferences.getInstance()),
),
),
appVersion: (await PackageInfo.fromPlatform()).version,
));
}

View File

@ -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;

View File

@ -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<StatefulWidget> createState() {
return _ProgressCreationScreenState();
@ -67,9 +62,9 @@ class _ProgressCreationScreenState extends State<ProgressCreationScreen> {
appBar: AppBar(
title: Text("Create Time Progress"),
),
drawer: AppDrawer(
/*drawer: AppDrawer(
appVersion: widget.appVersion,
),
),*/
body: Container(
padding: EdgeInsets.all(8),
child: Column(

View File

@ -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<StatefulWidget> createState() {
return _ProgressDashboardScreenState();
}
}
ProgressDashboardScreen({
Key key,
@required this.appVersion,
}) : super(key: key);
class _ProgressDashboardScreenState extends State<ProgressDashboardScreen> {
int _currentIndex = 0;
final List<Widget> _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<Widget> startedProgressesTileList = List<Widget>();
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<Widget> futureProgressesTileList = List<Widget>();
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(
floatingActionButton: _currentIndex != 2
? FloatingActionButton(
heroTag: "createProgressBTN",
child: Icon(Icons.add),
onPressed: () {
Navigator.pushNamed(context, ProgressCreationScreen.routeName);
},
)
: null,
bottomNavigationBar: HomeBottomNavBar(
currentIndex: _currentIndex,
onTap: onBottomTabTapped,
),
);
}

View File

@ -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<StatefulWidget> createState() {
return _ProgressDetailScreenState();
@ -154,9 +147,9 @@ class _ProgressDetailScreenState extends State<ProgressDetailScreen> {
appBar: AppBar(
title: Text("Progress"),
),
drawer: AppDrawer(
/*drawer: AppDrawer(
appVersion: widget.appVersion,
),
),*/
body: Container(
margin: EdgeInsets.all(8),
child: StoreConnector(

View File

@ -4,6 +4,21 @@ import 'package:time_progress_tracker/models/time_progress.dart';
List<TimeProgress> timeProgressListSelector(AppState state) =>
state.timeProgressList;
List<TimeProgress> activeTimeProgressesSelector(AppState state) {
return state.timeProgressList
.where((timeProgress) =>
timeProgress.hasStarted() && !timeProgress.hasEnded())
.toList();
}
List<TimeProgress> inactiveTimeProgressesSelector(AppState state) {
return state.timeProgressList
.where((timeProgress) =>
!timeProgress.hasStarted() || timeProgress.hasEnded())
.toList();
}
@Deprecated("use active TimeProgresses Selector instead.")
List<TimeProgress> currentTimeProgressSelector(AppState state) {
int currentTime = DateTime.now().millisecondsSinceEpoch;
return state.timeProgressList

View File

@ -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<TimeProgress> activeTimeProgresses;
final bool hasLoaded;
_ViewModel({
@required this.activeTimeProgresses,
@required this.hasLoaded,
});
static _ViewModel fromStore(Store<AppState> store) {
return _ViewModel(
activeTimeProgresses: activeTimeProgressesSelector(store.state),
hasLoaded: store.state.hasLoaded,
);
}
}

View File

@ -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",
)
],
);
}
}

View File

@ -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<TimeProgress> inactiveTimeProgresses;
final bool hasLoaded;
_ViewModel({
@required this.inactiveTimeProgresses,
@required this.hasLoaded,
});
static _ViewModel fromStore(Store<AppState> store) {
return _ViewModel(
inactiveTimeProgresses: inactiveTimeProgressesSelector(store.state),
hasLoaded: store.state.hasLoaded,
);
}
}

View File

@ -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));
},
),
);
}
}

View File

@ -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"))
],
),
),
);
}
}

View File

@ -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:

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.
# 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"