Created ProgressViewWidget and rewritten ProgressDetailScreen with it
Signed-off-by: Andreas Fahrecker <AndreasFahrecker@gmail.com>
This commit is contained in:
parent
1abfd0c1f5
commit
1826bbe421
@ -98,7 +98,7 @@ class TimeProgress {
|
||||
}
|
||||
|
||||
static bool isNameValid(String name) {
|
||||
return name != null && name != "" && name.length > 3 && name.length < 20;
|
||||
return name != null && name != "" && name.length > 2 && name.length < 21;
|
||||
}
|
||||
|
||||
static bool areTimesValid(DateTime startTime, DateTime endTime) {
|
||||
|
@ -2,10 +2,8 @@ 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_exceptions.dart';
|
||||
import 'package:time_progress_tracker/models/app_state.dart';
|
||||
import 'package:time_progress_tracker/models/time_progress.dart';
|
||||
import 'package:time_progress_tracker/widgets/date_picker_btn.dart';
|
||||
import 'package:time_progress_tracker/widgets/progress_editor_widget.dart';
|
||||
|
||||
class ProgressCreationScreen extends StatefulWidget {
|
||||
|
@ -1,20 +1,16 @@
|
||||
import 'dart:io';
|
||||
|
||||
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_exceptions.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/home_screen.dart';
|
||||
import 'package:time_progress_tracker/selectors/time_progress_selectors.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';
|
||||
import 'package:time_progress_tracker/widgets/progress_editor_widget.dart';
|
||||
import 'package:time_progress_tracker/widgets/progress_view_widget.dart';
|
||||
|
||||
class ProgressDetailScreenArguments {
|
||||
final String id;
|
||||
@ -32,69 +28,55 @@ class ProgressDetailScreen extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _ProgressDetailScreenState extends State<ProgressDetailScreen> {
|
||||
bool _isBeingEdited = false;
|
||||
final TextEditingController _nameController = TextEditingController();
|
||||
|
||||
bool _editMode = false;
|
||||
TimeProgress _editedProgress = TimeProgress.initialDefault();
|
||||
|
||||
bool _validName = true;
|
||||
|
||||
void _onStartDateChanged(DateTime picked) {
|
||||
if (picked != null) {
|
||||
void _onEditedProgressChanged(TimeProgress newProgress) {
|
||||
setState(() {
|
||||
_editedProgress = _editedProgress.copyWith(startTime: picked);
|
||||
_editedProgress = newProgress;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _onEndDateChanged(DateTime picked) {
|
||||
if (picked != null) {
|
||||
setState(() {
|
||||
_editedProgress = _editedProgress.copyWith(endTime: picked);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _onSaveTimeProgress(Store<AppState> store, id) {
|
||||
if (!TimeProgress.isValid(_editedProgress)) return;
|
||||
store.dispatch(UpdateTimeProgressAction(id, _editedProgress));
|
||||
setState(() {
|
||||
_isBeingEdited = false;
|
||||
_editMode = false;
|
||||
});
|
||||
}
|
||||
|
||||
void _showCancelEditTimeProgressDialog(AppState state, id) {
|
||||
TimeProgress originalTp = timeProgressByIdSelector(state, id);
|
||||
if (originalTp != _editedProgress) {
|
||||
String originalName = timeProgressByIdSelector(state, id).name;
|
||||
String originalName = originalTp.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,
|
||||
onYesPressed: () {
|
||||
_cancelEditMode();
|
||||
Navigator.pop(context);
|
||||
},
|
||||
onNoPressed: _onCloseDialog,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
setState(() {
|
||||
_isBeingEdited = false;
|
||||
});
|
||||
_cancelEditMode();
|
||||
}
|
||||
}
|
||||
|
||||
void _onCancelEditTimeProgress() {
|
||||
void _cancelEditMode() {
|
||||
setState(() {
|
||||
_isBeingEdited = false;
|
||||
_editMode = false;
|
||||
});
|
||||
Navigator.pop(context);
|
||||
}
|
||||
|
||||
void _onEditTimeProgress(Store<AppState> store, id) {
|
||||
void _onEditTimeProgress(AppState state, id) {
|
||||
setState(() {
|
||||
_isBeingEdited = true;
|
||||
_editedProgress = timeProgressByIdSelector(store.state, id);
|
||||
_nameController.text = _editedProgress.name;
|
||||
_editMode = true;
|
||||
_editedProgress = timeProgressByIdSelector(state, id);
|
||||
});
|
||||
}
|
||||
|
||||
@ -119,38 +101,17 @@ class _ProgressDetailScreenState extends State<ProgressDetailScreen> {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_nameController.addListener(() {
|
||||
try {
|
||||
TimeProgress editedProgress =
|
||||
_editedProgress.copyWith(name: _nameController.text);
|
||||
setState(() {
|
||||
_editedProgress = editedProgress;
|
||||
_validName = true;
|
||||
});
|
||||
} on TimeProgressInvalidNameException catch (e) {
|
||||
setState(() {
|
||||
_validName = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final ProgressDetailScreenArguments args =
|
||||
ModalRoute.of(context).settings.arguments;
|
||||
final Store<AppState> store = StoreProvider.of<AppState>(context);
|
||||
final ThemeData appTheme = Theme.of(context);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text("Progress"),
|
||||
),
|
||||
/*drawer: AppDrawer(
|
||||
appVersion: widget.appVersion,
|
||||
),*/
|
||||
body: Container(
|
||||
margin: EdgeInsets.all(8),
|
||||
child: StoreConnector(
|
||||
@ -162,119 +123,65 @@ class _ProgressDetailScreenState extends State<ProgressDetailScreen> {
|
||||
return Center(
|
||||
child: Text("Error Invalid Time Progress"),
|
||||
);
|
||||
List<Widget> columnChildren = [
|
||||
Expanded(
|
||||
child: ProgressViewWidget(
|
||||
timeProgress:
|
||||
_editMode ? _editedProgress : vm.timeProgress),
|
||||
)
|
||||
];
|
||||
if (_editMode)
|
||||
columnChildren.add(Expanded(
|
||||
child: ProgressEditorWidget(
|
||||
timeProgress: _editedProgress,
|
||||
onTimeProgressChanged: _onEditedProgressChanged,
|
||||
),
|
||||
));
|
||||
return Column(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: _isBeingEdited
|
||||
? TextField(
|
||||
controller: _nameController,
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText: "Progress Name",
|
||||
errorText: _validName
|
||||
? null
|
||||
: "The Name of the Time Progress has to be set.",
|
||||
),
|
||||
)
|
||||
: (vm.timeProgress.hasStarted() &&
|
||||
!vm.timeProgress.hasEnded())
|
||||
? FittedBox(
|
||||
fit: BoxFit.fitWidth,
|
||||
child: Text(
|
||||
vm.timeProgress.name,
|
||||
textAlign: TextAlign.center,
|
||||
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.timeProgress.hasStarted() && !vm.timeProgress.hasEnded())
|
||||
? Expanded(
|
||||
flex: 2,
|
||||
child: ProgressDetailCircularPercent(
|
||||
percentDone: _isBeingEdited
|
||||
? _editedProgress.percentDone()
|
||||
: vm.timeProgress.percentDone(),
|
||||
),
|
||||
)
|
||||
: Expanded(
|
||||
flex: 2,
|
||||
child: !vm.timeProgress.hasEnded()
|
||||
? Text(
|
||||
"Starts in ${vm.timeProgress.startTime.difference(DateTime.now()).inDays} Days.")
|
||||
: Text(
|
||||
"Ended ${DateTime.now().difference(vm.timeProgress.endTime).inDays} Days ago."),
|
||||
),
|
||||
(vm.timeProgress.hasStarted() && !vm.timeProgress.hasEnded())
|
||||
? Expanded(
|
||||
flex: 1,
|
||||
child: ProgressDetailLinearPercent(
|
||||
timeProgress: _isBeingEdited
|
||||
? _editedProgress
|
||||
: vm.timeProgress,
|
||||
),
|
||||
)
|
||||
: Spacer(
|
||||
flex: 1,
|
||||
),
|
||||
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)
|
||||
],
|
||||
children: columnChildren,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
|
||||
floatingActionButton: _isBeingEdited
|
||||
? ProgressDetailFabEditingRow(
|
||||
onSave: () =>
|
||||
_validName ? _onSaveTimeProgress(store, args.id) : null,
|
||||
onCancelEdit: () =>
|
||||
_showCancelEditTimeProgressDialog(store.state, args.id),
|
||||
)
|
||||
: ProgressDetailFabRow(
|
||||
onEdit: () => _onEditTimeProgress(store, args.id),
|
||||
onDelete: () => _showDeleteTimeProgressDialog(store, args.id),
|
||||
floatingActionButton: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: FloatingActionButton(
|
||||
heroTag: _editMode
|
||||
? "saveEditedTimeProgressBTN"
|
||||
: "editTimeProgressBTN",
|
||||
child: _editMode ? Icon(Icons.save) : Icon(Icons.edit),
|
||||
backgroundColor: _editMode ? Colors.green : appTheme.accentColor,
|
||||
onPressed: _editMode
|
||||
? () {
|
||||
_onSaveTimeProgress(store, args.id);
|
||||
}
|
||||
: () {
|
||||
_onEditTimeProgress(store.state, args.id);
|
||||
},
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: FloatingActionButton(
|
||||
heroTag: _editMode
|
||||
? "cancelEditTimeProgressBTN"
|
||||
: "deleteTimeProgressBTN",
|
||||
child: _editMode ? Icon(Icons.cancel) : Icon(Icons.delete),
|
||||
backgroundColor: Colors.red,
|
||||
onPressed: _editMode
|
||||
? () {
|
||||
_showCancelEditTimeProgressDialog(store.state, args.id);
|
||||
}
|
||||
: () {
|
||||
_showDeleteTimeProgressDialog(store, args.id);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_nameController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
class _ViewModel {
|
||||
|
@ -18,41 +18,53 @@ class ProgressEditorWidget extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _ProgressEditorWidgetState extends State<ProgressEditorWidget> {
|
||||
final _nameTextController = TextEditingController();
|
||||
bool _validName = true, _validDate = true;
|
||||
|
||||
void _onNameChanged(String newName) {
|
||||
if (!TimeProgress.isNameValid(newName))
|
||||
setState(() {
|
||||
_validName = false;
|
||||
});
|
||||
|
||||
widget.onTimeProgressChanged(widget.timeProgress.copyWith(name: newName));
|
||||
void _onNameChanged() {
|
||||
if (TimeProgress.isNameValid(_nameTextController.text)) {
|
||||
widget.onTimeProgressChanged(
|
||||
widget.timeProgress.copyWith(name: _nameTextController.text));
|
||||
setState(() {
|
||||
_validName = true;
|
||||
});
|
||||
} else
|
||||
setState(() {
|
||||
_validName = false;
|
||||
});
|
||||
}
|
||||
|
||||
void _onStartDateChanged(DateTime newStartDate) {
|
||||
if (!TimeProgress.areTimesValid(newStartDate, widget.timeProgress.endTime))
|
||||
setState(() {
|
||||
_validDate = false;
|
||||
});
|
||||
|
||||
if (TimeProgress.areTimesValid(newStartDate, widget.timeProgress.endTime)) {
|
||||
widget.onTimeProgressChanged(
|
||||
widget.timeProgress.copyWith(startTime: newStartDate));
|
||||
setState(() {
|
||||
_validDate = true;
|
||||
});
|
||||
}
|
||||
|
||||
void _onEndDateChanged(DateTime newEndDate) {
|
||||
if (!TimeProgress.areTimesValid(widget.timeProgress.startTime, newEndDate))
|
||||
} else
|
||||
setState(() {
|
||||
_validDate = false;
|
||||
});
|
||||
}
|
||||
|
||||
void _onEndDateChanged(DateTime newEndDate) {
|
||||
if (TimeProgress.areTimesValid(widget.timeProgress.startTime, newEndDate)) {
|
||||
widget.onTimeProgressChanged(
|
||||
widget.timeProgress.copyWith(endTime: newEndDate));
|
||||
setState(() {
|
||||
_validDate = true;
|
||||
});
|
||||
} else
|
||||
setState(() {
|
||||
_validDate = false;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_nameTextController.text = widget.timeProgress.name;
|
||||
_nameTextController.addListener(_onNameChanged);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
@ -60,12 +72,13 @@ class _ProgressEditorWidgetState extends State<ProgressEditorWidget> {
|
||||
List<Widget> columnChildren = [
|
||||
Expanded(
|
||||
child: TextField(
|
||||
onChanged: _onNameChanged,
|
||||
controller: _nameTextController,
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText: "Progress Name",
|
||||
errorText:
|
||||
_validName ? null : "The Name of the Progress can't be empty.",
|
||||
errorText: _validName
|
||||
? null
|
||||
: "The Name need to have at least 3 and at max 20 symbols.",
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -102,7 +115,9 @@ class _ProgressEditorWidgetState extends State<ProgressEditorWidget> {
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: Text(
|
||||
"Invalid Dates. The Start Date has to be before the End Date"),
|
||||
"Invalid Dates. The Start Date has to be before the End Date",
|
||||
style: TextStyle(color: Colors.red),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
57
lib/widgets/progress_view_widget.dart
Normal file
57
lib/widgets/progress_view_widget.dart
Normal file
@ -0,0 +1,57 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:percent_indicator/circular_percent_indicator.dart';
|
||||
import 'package:percent_indicator/linear_percent_indicator.dart';
|
||||
import 'package:time_progress_tracker/models/time_progress.dart';
|
||||
|
||||
class ProgressViewWidget extends StatelessWidget {
|
||||
final TimeProgress timeProgress;
|
||||
|
||||
ProgressViewWidget({
|
||||
@required this.timeProgress,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: FittedBox(
|
||||
fit: BoxFit.fitWidth,
|
||||
child: Text(
|
||||
timeProgress.name,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black87,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: CircularPercentIndicator(
|
||||
radius: 100,
|
||||
lineWidth: 10,
|
||||
percent: timeProgress.percentDone(),
|
||||
progressColor: Colors.green,
|
||||
backgroundColor: Colors.red,
|
||||
center: Text("${(timeProgress.percentDone() * 100).floor()} %"),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: LinearPercentIndicator(
|
||||
padding: EdgeInsets.symmetric(horizontal: 15),
|
||||
percent: timeProgress.percentDone(),
|
||||
leading: Text("${timeProgress.daysBehind()} Days"),
|
||||
center: Text("${(timeProgress.percentDone() * 100).floor()} %"),
|
||||
trailing: Text("${timeProgress.daysLeft()} Days"),
|
||||
progressColor: Colors.green,
|
||||
backgroundColor: Colors.red,
|
||||
lineHeight: 25,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -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.10+10
|
||||
version: 0.0.11+11
|
||||
|
||||
environment:
|
||||
sdk: ">=2.7.0 <3.0.0"
|
||||
|
Loading…
x
Reference in New Issue
Block a user