added selector for repeat-interval. Needs UI-polish though..

This commit is contained in:
Nicole Dresselhaus 2023-01-11 17:18:40 +01:00
parent db39eeea55
commit 43f95cb321
4 changed files with 84 additions and 63 deletions

View File

@ -6,7 +6,7 @@ A Reminder based on todo.txt synced via nextcloud
- [x] make repeat-datatype (like: daily, weekly on mo/th/fr, bi-monthly, etc.) - [x] make repeat-datatype (like: daily, weekly on mo/th/fr, bi-monthly, etc.)
- [x] define isomorphism for 'repeat:'-meta-tag - [x] define isomorphism for 'repeat:'-meta-tag
- [ ] add interface for repeat-datatype in addReminder.dart - [x] add interface for repeat-datatype in addReminder.dart
- [x] save/load data to/from disk - [x] save/load data to/from disk
- [x] adding/removing tasks - [x] adding/removing tasks
- [x] respect ordering that was used when starting the app when saving. - [x] respect ordering that was used when starting the app when saving.

View File

@ -1,7 +1,10 @@
import 'package:date_field/date_field.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:nextcloud_reminder/repeating_task.dart'; import 'package:nextcloud_reminder/repeating_task.dart';
import 'package:nextcloud_reminder/types/repeat.dart'; import 'package:nextcloud_reminder/types/repeat.dart';
import 'package:nextcloud_reminder/types/tasks.dart'; import 'package:nextcloud_reminder/types/tasks.dart';
import 'package:tuple/tuple.dart';
class AddTaskWidget extends StatefulWidget { class AddTaskWidget extends StatefulWidget {
const AddTaskWidget({super.key, this.restorationId, required this.onSave}); const AddTaskWidget({super.key, this.restorationId, required this.onSave});
@ -15,11 +18,14 @@ class AddTaskWidget extends StatefulWidget {
//TODO: make _repeat changeable. //TODO: make _repeat changeable.
class _AddTaskWidgetState extends State<AddTaskWidget> with RestorationMixin { class _AddTaskWidgetState extends State<AddTaskWidget> {
final _formKey = GlobalKey<FormState>(); final _formKey = GlobalKey<FormState>();
final _titleController = TextEditingController(); final _titleController = TextEditingController();
final int _repeat = 1; static _emptyRepetition() {
final RestorableDateTime _beginDate = RestorableDateTime(DateTime.now()); return Tuple2(TextEditingController(text: "1"), ValueNotifier(DateInterval.daily));
}
final List<Tuple2<TextEditingController,ValueNotifier<DateInterval>>> _repeatEveryController = [_emptyRepetition()];
DateTime _beginDate = DateTime.now();
@override @override
void dispose() { void dispose() {
@ -27,53 +33,45 @@ class _AddTaskWidgetState extends State<AddTaskWidget> with RestorationMixin {
super.dispose(); super.dispose();
} }
@override String _prettyInterval(DateInterval d) {
String? get restorationId => widget.restorationId; switch (d) {
default:
late final RestorableRouteFuture<DateTime?> _restorableDatePickerRouteFuture = return d.toString();
RestorableRouteFuture<DateTime?>(
onComplete: _selectDate,
onPresent: (NavigatorState navigator, Object? arguments) {
return navigator.restorablePush(
_datePickerRoute,
arguments: _beginDate.value.millisecondsSinceEpoch,
);
},
);
static Route<DateTime> _datePickerRoute(
BuildContext context,
Object? arguments,
) {
return DialogRoute<DateTime>(
context: context,
builder: (BuildContext context) {
return DatePickerDialog(
restorationId: 'date_picker_dialog',
initialEntryMode: DatePickerEntryMode.calendarOnly,
initialDate: DateTime.fromMillisecondsSinceEpoch(arguments! as int),
firstDate: DateTime(2000),
lastDate: DateTime(2100),
);
},
);
}
@override
void restoreState(RestorationBucket? oldBucket, bool initialRestore) {
registerForRestoration(_beginDate, 'selected_date');
registerForRestoration(
_restorableDatePickerRouteFuture, 'date_picker_route_future');
}
void _selectDate(DateTime? newSelectedDate) {
if (newSelectedDate != null) {
setState(() {
_beginDate.value = newSelectedDate;
});
} }
} }
Widget _repeatBuilder(BuildContext context, Tuple2<TextEditingController,ValueNotifier<DateInterval>> data) {
return Row(
children: [
Text("Repeat every " ),
Expanded(
flex: 1,
child: TextFormField(
controller: data.item1,
decoration: const InputDecoration(
hintText: "1",
),
keyboardType: const TextInputType.numberWithOptions(signed: false, decimal: false),
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
),
),
Expanded(
flex: 3,
child: DropdownButton<DateInterval>(
items: DateInterval.values.map((v) => DropdownMenuItem(value: v, child: Text(_prettyInterval(v)))).toList(),
onChanged: (v) => setState(() {
data.item2.value = v ?? data.item2.value;
}),
value: data.item2.value,
),
),
IconButton(onPressed: () => setState(() {
_repeatEveryController.remove(data);
}), icon: Icon(Icons.remove, color: Theme.of(context).errorColor,)),
],
);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
@ -98,21 +96,21 @@ class _AddTaskWidgetState extends State<AddTaskWidget> with RestorationMixin {
labelText: "Taskname" labelText: "Taskname"
), ),
), ),
Container(
decoration: const BoxDecoration(), DateTimeField(
child: Row( onDateSelected: (v) => setState(() { _beginDate = v; }),
children: [ selectedDate: _beginDate,
Text("Begin: ", style: Theme.of(context).textTheme.labelLarge), decoration: const InputDecoration(
Expanded( suffixIcon: Icon(Icons.event_note),
child: Text(Task.formatDate(_beginDate.value)) labelText: "Begin"
), ),
IconButton( mode: DateTimeFieldPickerMode.date,
onPressed: () => _restorableDatePickerRouteFuture.present(),
icon: Icon(Icons.date_range,
color: Theme.of(context).focusColor))
]
)
), ),
] + _repeatEveryController.map((c) => _repeatBuilder(context,c)).toList()
+ [
ElevatedButton(onPressed: () => setState(() {
_repeatEveryController.add(_emptyRepetition());
}), child: const Text("add repetition")),
Padding( Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0), padding: const EdgeInsets.symmetric(vertical: 16.0),
child: ElevatedButton( child: ElevatedButton(
@ -124,8 +122,16 @@ class _AddTaskWidgetState extends State<AddTaskWidget> with RestorationMixin {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Task added.')), const SnackBar(content: Text('Task added.')),
); );
var repeats = _repeatEveryController.map((e) => RepeatInterval(interval: e.item2.value, every: int.parse(e.item1.text))).toList();
var meta = repeats.map((e) => e.toString()).join("/");
widget.onSave(RepeatingTask( widget.onSave(RepeatingTask(
task: TaskExtra(title: _titleController.text, begin: _beginDate.value, repeat: [RepeatInterval(interval: DateInterval.daily)],))); task: TaskExtra(
title: _titleController.text,
begin: _beginDate,
meta: {"repeat": meta},
repeat: repeats,
)
));
Navigator.pop(context); Navigator.pop(context);
} }
}, },

View File

@ -43,6 +43,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.5" version: "1.0.5"
date_field:
dependency: "direct main"
description:
name: date_field
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.2"
fake_async: fake_async:
dependency: transitive dependency: transitive
description: description:
@ -93,6 +100,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.2.2" version: "0.2.2"
intl:
dependency: transitive
description:
name: intl
url: "https://pub.dartlang.org"
source: hosted
version: "0.17.0"
js: js:
dependency: transitive dependency: transitive
description: description:

View File

@ -41,6 +41,7 @@ dependencies:
tuple: ^2.0.1 tuple: ^2.0.1
flutter_window_close: ^0.2.2 flutter_window_close: ^0.2.2
collection: ^1.16.0 collection: ^1.16.0
date_field: ^3.0.2
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: