Feature/basic app (#1)
Feature/Basic App (#1) Basic App Structure Consists of Time Progress Dashboard, Time Progress Detail View and Time Progress Creator. All of these have an AppDrawer with a Link To the Dashboard and all your Track Time Progresses, also an About Button. Commits: * Undetailed Commit more work * Changed isEditing ? in Detail Screen and Extracted FAB row to widget * Extracted Progress Detail Fab Row and Progress Detail select Date Btn to widgets * Create Progress Detail Widgets Folder * Extracted Edit Dates Row Widget * Extracted Functions from ui * Made some fields private * LoadTimerProgressList if unloaded function * Created App Yes No Dialog Widget * Using Yes No Dialog in Detail Screen * Created TimeProgress Initial Default factory * Renamed to Time Progress Tracker * Added About Button in App Drawer * Code cleanup * Code clean up and fixed Bug with null as string in Repository Signed-off-by Andreas Fahrecker <AndreasFahrecker@gmail.com>
This commit is contained in:
committed by
GitHub
parent
976fbec455
commit
f013c0de65
143
lib/screens/progress_creation_screen.dart
Normal file
143
lib/screens/progress_creation_screen.dart
Normal file
@ -0,0 +1,143 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_redux/flutter_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_dashboard_screen.dart';
|
||||
import 'package:time_progress_tracker/widgets/app_drawer_widget.dart';
|
||||
|
||||
class ProgressCreationScreen extends StatefulWidget {
|
||||
static const routeName = "/progress-creation";
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() {
|
||||
return _ProgressCreationScreenState();
|
||||
}
|
||||
}
|
||||
|
||||
class _ProgressCreationScreenState extends State<ProgressCreationScreen> {
|
||||
final TextEditingController _nameController = TextEditingController();
|
||||
DateTime pickedStartTime = DateTime.now();
|
||||
DateTime pickedEndTime = DateTime(
|
||||
DateTime.now().year + 1, DateTime.now().month, DateTime.now().day);
|
||||
|
||||
Future<DateTime> _selectDate(
|
||||
BuildContext context, DateTime initialDate) async {
|
||||
return await showDatePicker(
|
||||
context: context,
|
||||
initialDate: initialDate,
|
||||
firstDate: DateTime(DateTime.now().year - 5),
|
||||
lastDate: DateTime(DateTime.now().year + 5));
|
||||
}
|
||||
|
||||
void _createTimeProgress(BuildContext context) {
|
||||
StoreProvider.of<AppState>(context).dispatch(AddTimeProgressAction(
|
||||
TimeProgress(_nameController.text, pickedStartTime, pickedEndTime),
|
||||
));
|
||||
Navigator.pushNamed(context, ProgressDashboardScreen.routeName);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_nameController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text("Create Time Progress"),
|
||||
),
|
||||
drawer: AppDrawer(),
|
||||
body: Container(
|
||||
padding: EdgeInsets.all(8),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: TextField(
|
||||
controller: _nameController,
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(), labelText: "Progress Name"),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Text("${_nameController.text}"),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
flex: 5,
|
||||
child: FlatButton(
|
||||
color: Colors.blue,
|
||||
child: Text(
|
||||
"Start Date: ${pickedStartTime.toLocal().toString().split(" ")[0]}"),
|
||||
onPressed: () async {
|
||||
DateTime dt =
|
||||
await _selectDate(context, pickedStartTime);
|
||||
if (dt != null) {
|
||||
setState(() {
|
||||
pickedStartTime = dt;
|
||||
});
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
Spacer(
|
||||
flex: 1,
|
||||
),
|
||||
Expanded(
|
||||
flex: 5,
|
||||
child: FlatButton(
|
||||
color: Colors.blue,
|
||||
child: Text(
|
||||
"End Date: ${pickedEndTime.toLocal().toString().split(" ")[0]}"),
|
||||
onPressed: () async {
|
||||
DateTime dt = await _selectDate(context, pickedEndTime);
|
||||
if (dt != null) {
|
||||
setState(() {
|
||||
pickedEndTime = dt;
|
||||
});
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Spacer(
|
||||
flex: 5,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
|
||||
floatingActionButton: Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: FloatingActionButton(
|
||||
heroTag: "createTimeProgressBTN",
|
||||
child: Icon(Icons.save),
|
||||
onPressed: () {
|
||||
_createTimeProgress(context);
|
||||
},
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: FloatingActionButton(
|
||||
heroTag: "cancelTimeProgressCreationBTN",
|
||||
child: Icon(Icons.cancel),
|
||||
onPressed: () {
|
||||
Navigator.pushNamed(context, ProgressDashboardScreen.routeName);
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
94
lib/screens/progress_dashboard_screen.dart
Normal file
94
lib/screens/progress_dashboard_screen.dart
Normal file
@ -0,0 +1,94 @@
|
||||
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/widgets/app_drawer_widget.dart';
|
||||
|
||||
class ProgressDashboardScreen extends StatelessWidget {
|
||||
static const routeName = "/progress-dashboard";
|
||||
static const title = "Time Progress Dashboard";
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(title),
|
||||
),
|
||||
drawer: AppDrawer(),
|
||||
body: StoreConnector(
|
||||
converter: _ViewModel.fromStore,
|
||||
onInit: loadTimeProgressListIfUnloaded,
|
||||
builder: (BuildContext context, _ViewModel vm) {
|
||||
if (!vm.hasLoaded) {
|
||||
return Center(
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
}
|
||||
List<Widget> dashboardTileList = List<Widget>();
|
||||
|
||||
if (vm.timeProgressList.length > 0) {
|
||||
for (TimeProgress tp in vm.timeProgressList) {
|
||||
dashboardTileList.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));
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
dashboardTileList.add(ListTile(
|
||||
title: Text("You don't have any tracked Progress."),
|
||||
));
|
||||
}
|
||||
|
||||
return ListView(
|
||||
padding: EdgeInsets.all(8),
|
||||
children: dashboardTileList,
|
||||
);
|
||||
},
|
||||
),
|
||||
floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
|
||||
floatingActionButton: FloatingActionButton(
|
||||
child: Icon(Icons.add),
|
||||
onPressed: () {
|
||||
Navigator.pushNamed(context, ProgressCreationScreen.routeName);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ViewModel {
|
||||
final List<TimeProgress> timeProgressList;
|
||||
final bool hasLoaded;
|
||||
|
||||
_ViewModel({
|
||||
@required this.timeProgressList,
|
||||
@required this.hasLoaded,
|
||||
});
|
||||
|
||||
static _ViewModel fromStore(Store<AppState> store) {
|
||||
return _ViewModel(
|
||||
timeProgressList: store.state.timeProgressList,
|
||||
hasLoaded: store.state.hasLoaded,
|
||||
);
|
||||
}
|
||||
}
|
239
lib/screens/progress_detail_screen.dart
Normal file
239
lib/screens/progress_detail_screen.dart
Normal file
@ -0,0 +1,239 @@
|
||||
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/screens/progress_dashboard_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_yes_no_dialog_widget.dart';
|
||||
import 'package:time_progress_tracker/widgets/progress_detail_widgets/progress_detail_circular_percent_widget.dart';
|
||||
import 'package:time_progress_tracker/widgets/progress_detail_widgets/progress_detail_edit_dates_row_widget.dart';
|
||||
import 'package:time_progress_tracker/widgets/progress_detail_widgets/progress_detail_fab_editing_row_widget.dart';
|
||||
import 'package:time_progress_tracker/widgets/progress_detail_widgets/progress_detail_fab_row_widget.dart';
|
||||
import 'package:time_progress_tracker/widgets/progress_detail_widgets/progress_detail_linear_percent_widget.dart';
|
||||
|
||||
class ProgressDetailScreenArguments {
|
||||
final String id;
|
||||
|
||||
ProgressDetailScreenArguments(this.id);
|
||||
}
|
||||
|
||||
class ProgressDetailScreen extends StatefulWidget {
|
||||
static const routeName = "/progress-detail";
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() {
|
||||
return _ProgressDetailScreenState();
|
||||
}
|
||||
}
|
||||
|
||||
class _ProgressDetailScreenState extends State<ProgressDetailScreen> {
|
||||
final TextEditingController _nameController = TextEditingController();
|
||||
bool _isBeingEdited = false;
|
||||
TimeProgress _editedProgress = TimeProgress.initialDefault();
|
||||
|
||||
void _onStartDateChanged(DateTime picked) {
|
||||
if (picked != null) {
|
||||
setState(() {
|
||||
_editedProgress = _editedProgress.copyWith(startTime: picked);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _onEndDateChanged(DateTime picked) {
|
||||
if (picked != null) {
|
||||
setState(() {
|
||||
_editedProgress = _editedProgress.copyWith(endTime: picked);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _onSaveTimeProgress(Store<AppState> store, id) {
|
||||
store.dispatch(UpdateTimeProgressAction(id, _editedProgress));
|
||||
setState(() {
|
||||
_isBeingEdited = false;
|
||||
});
|
||||
}
|
||||
|
||||
void _showCancelEditTimeProgressDialog(AppState state, id) {
|
||||
TimeProgress originalTp = timeProgressByIdSelector(state, id);
|
||||
if (originalTp != _editedProgress) {
|
||||
String originalName = timeProgressByIdSelector(state, id).name;
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (_) => AppYesNoDialog(
|
||||
titleText: "Cancel Editing of $originalName",
|
||||
contentText:
|
||||
"Are you sure that you want to discard the changes done to $originalName",
|
||||
onYesPressed: _onCancelEditTimeProgress,
|
||||
onNoPressed: _onCloseDialog,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
setState(() {
|
||||
_isBeingEdited = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _onCancelEditTimeProgress() {
|
||||
setState(() {
|
||||
_isBeingEdited = false;
|
||||
});
|
||||
Navigator.pop(context);
|
||||
}
|
||||
|
||||
void _onEditTimeProgress(Store<AppState> store, id) {
|
||||
setState(() {
|
||||
_isBeingEdited = true;
|
||||
_editedProgress = timeProgressByIdSelector(store.state, id);
|
||||
_nameController.text = _editedProgress.name;
|
||||
});
|
||||
}
|
||||
|
||||
void _showDeleteTimeProgressDialog(Store<AppState> store, id) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (_) => AppYesNoDialog(
|
||||
titleText: "Delete ${timeProgressByIdSelector(store.state, id).name}",
|
||||
contentText: "Are you sure you want to delete this time progress?",
|
||||
onYesPressed: () => _onDeleteTimeProgress(store, id),
|
||||
onNoPressed: _onCloseDialog,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _onDeleteTimeProgress(Store<AppState> store, String id) {
|
||||
store.dispatch(DeleteTimeProgressAction(id));
|
||||
Navigator.popAndPushNamed(context, ProgressDashboardScreen.routeName);
|
||||
}
|
||||
|
||||
void _onCloseDialog() {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_nameController.addListener(() {
|
||||
this.setState(() {
|
||||
this._editedProgress =
|
||||
this._editedProgress.copyWith(name: _nameController.text);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final ProgressDetailScreenArguments args =
|
||||
ModalRoute.of(context).settings.arguments;
|
||||
final Store<AppState> store = StoreProvider.of<AppState>(context);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text("Progress"),
|
||||
),
|
||||
drawer: AppDrawer(),
|
||||
body: Container(
|
||||
margin: EdgeInsets.all(8),
|
||||
child: StoreConnector(
|
||||
converter: (Store<AppState> store) =>
|
||||
_ViewModel.fromStoreAndArg(store, args),
|
||||
onInit: loadTimeProgressListIfUnloaded,
|
||||
builder: (BuildContext context, _ViewModel vm) {
|
||||
return Column(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: _isBeingEdited
|
||||
? TextField(
|
||||
controller: _nameController,
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText: "Progress Name"),
|
||||
)
|
||||
: FittedBox(
|
||||
fit: BoxFit.fitWidth,
|
||||
child: Text(
|
||||
vm.timeProgress.name,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black87,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: ProgressDetailCircularPercent(
|
||||
percentDone: _isBeingEdited
|
||||
? _editedProgress.percentDone()
|
||||
: vm.timeProgress.percentDone(),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: ProgressDetailLinearPercent(
|
||||
timeProgress:
|
||||
_isBeingEdited ? _editedProgress : vm.timeProgress,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Text(
|
||||
"${_isBeingEdited ? _editedProgress.allDays() : vm.timeProgress.allDays()} Days"),
|
||||
),
|
||||
this._isBeingEdited
|
||||
? Expanded(
|
||||
flex: 1,
|
||||
child: ProgressDetailEditDatesRow(
|
||||
startTime: _editedProgress.startTime,
|
||||
endTime: _editedProgress.endTime,
|
||||
onStartTimeChanged: _onStartDateChanged,
|
||||
onEndTimeChanged: _onEndDateChanged,
|
||||
),
|
||||
)
|
||||
: Spacer(flex: 1),
|
||||
Spacer(flex: 1)
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
|
||||
floatingActionButton: _isBeingEdited
|
||||
? ProgressDetailFabEditingRow(
|
||||
onSave: () => _onSaveTimeProgress(store, args.id),
|
||||
onCancelEdit: () =>
|
||||
_showCancelEditTimeProgressDialog(store.state, args.id),
|
||||
)
|
||||
: ProgressDetailFabRow(
|
||||
onEdit: () => _onEditTimeProgress(store, args.id),
|
||||
onDelete: () => _showDeleteTimeProgressDialog(store, args.id),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_nameController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
class _ViewModel {
|
||||
final TimeProgress timeProgress;
|
||||
|
||||
_ViewModel({
|
||||
@required this.timeProgress,
|
||||
});
|
||||
|
||||
static _ViewModel fromStoreAndArg(
|
||||
Store<AppState> store, ProgressDetailScreenArguments args) {
|
||||
return _ViewModel(
|
||||
timeProgress: timeProgressByIdSelector(store.state, args.id),
|
||||
);
|
||||
}
|
||||
}
|
@ -1,190 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_redux/flutter_redux.dart';
|
||||
import 'package:percent_indicator/circular_percent_indicator.dart';
|
||||
import 'package:percent_indicator/linear_percent_indicator.dart';
|
||||
import 'package:redux/redux.dart';
|
||||
import 'package:time_progress_calculator/actions/actions.dart';
|
||||
import 'package:time_progress_calculator/models/app_state.dart';
|
||||
import 'package:time_progress_calculator/models/time_progress.dart';
|
||||
|
||||
class ProgressScreen extends StatefulWidget {
|
||||
const ProgressScreen({Key key, @required this.context, this.name})
|
||||
: super(key: key);
|
||||
|
||||
final BuildContext context;
|
||||
final String name;
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() {
|
||||
return _ProgressScreenState();
|
||||
}
|
||||
}
|
||||
|
||||
class _ProgressScreenState extends State<ProgressScreen> {
|
||||
void _selectStartDate(BuildContext context) async {
|
||||
Store<AppState> store = StoreProvider.of<AppState>(context);
|
||||
final DateTime picked = await showDatePicker(
|
||||
context: context,
|
||||
initialDate: store.state.timeProgressList[0].startTime,
|
||||
firstDate: DateTime(2000),
|
||||
lastDate: DateTime(2100));
|
||||
if (picked != null && picked != store.state.timeProgressList[0].startTime) {
|
||||
store.dispatch(UpdateTimeProgressAction(
|
||||
store.state.timeProgressList[0].id,
|
||||
store.state.timeProgressList[0].copyWith(startTime: picked),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
void _selectEndDate(BuildContext context) async {
|
||||
Store<AppState> store = StoreProvider.of<AppState>(context);
|
||||
final DateTime picked = await showDatePicker(
|
||||
context: context,
|
||||
initialDate: store.state.timeProgressList[0].endTime,
|
||||
firstDate: DateTime(2000),
|
||||
lastDate: DateTime(2100));
|
||||
if (picked != null && picked != store.state.timeProgressList[0].endTime) {
|
||||
store.dispatch(UpdateTimeProgressAction(
|
||||
store.state.timeProgressList[0].id,
|
||||
store.state.timeProgressList[0].copyWith(endTime: picked),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (StoreProvider.of<AppState>(widget.context).state.timeProgressList.length < 1) {
|
||||
StoreProvider.of<AppState>(widget.context).dispatch(AddTimeProgressAction(TimeProgress(
|
||||
DateTime(2000),
|
||||
DateTime(2100),
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text("${widget.name} Progress"),
|
||||
),
|
||||
body: StoreConnector(
|
||||
converter: _ViewModel.fromStore,
|
||||
builder: (context, _ViewModel vm) {
|
||||
final int daysDone =
|
||||
DateTime.now().difference(vm.timeProgress.startTime).inDays;
|
||||
final int daysLeft =
|
||||
vm.timeProgress.endTime.difference(DateTime.now()).inDays;
|
||||
final int allDays =
|
||||
vm.timeProgress.endTime.difference(vm.timeProgress.startTime).inDays;
|
||||
final double percent = daysDone / (allDays / 100) / 100;
|
||||
|
||||
return Container(
|
||||
margin: const EdgeInsets.all(5),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Text(
|
||||
"Start Date:",
|
||||
textAlign: TextAlign.right,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Text(
|
||||
"${vm.timeProgress.startTime.toLocal()}".split(" ")[0]),
|
||||
),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: RaisedButton(
|
||||
onPressed: () => _selectStartDate(context),
|
||||
child: Text("Change"),
|
||||
color: Colors.lightBlueAccent,
|
||||
),
|
||||
),
|
||||
Spacer(flex: 1)
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Text(
|
||||
"End Date:",
|
||||
textAlign: TextAlign.right,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Text(
|
||||
"${vm.timeProgress.endTime.toLocal()}".split(" ")[0]),
|
||||
),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: RaisedButton(
|
||||
onPressed: () => _selectEndDate(context),
|
||||
child: Text("Change"),
|
||||
color: Colors.lightBlueAccent,
|
||||
),
|
||||
),
|
||||
Spacer(flex: 1)
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 5,
|
||||
child: CircularPercentIndicator(
|
||||
radius: 100,
|
||||
lineWidth: 10,
|
||||
percent: percent,
|
||||
progressColor: Colors.green,
|
||||
backgroundColor: Colors.red,
|
||||
center: Text("${(percent * 100).floor()} %"),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: LinearPercentIndicator(
|
||||
padding: EdgeInsets.symmetric(horizontal: 15.0),
|
||||
percent: percent,
|
||||
leading: Text("$daysDone Days"),
|
||||
center: Text("${(percent * 100).floor()} %"),
|
||||
trailing: Text("$daysLeft Days"),
|
||||
progressColor: Colors.green,
|
||||
backgroundColor: Colors.red,
|
||||
lineHeight: 25,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Text("$allDays Days"),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ViewModel {
|
||||
final TimeProgress timeProgress;
|
||||
|
||||
_ViewModel({
|
||||
@required this.timeProgress,
|
||||
});
|
||||
|
||||
static _ViewModel fromStore(Store<AppState> store) {
|
||||
return _ViewModel(
|
||||
timeProgress: store.state.timeProgressList[0],
|
||||
);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user