diff --git a/lib/addReminder.dart b/lib/addReminder.dart new file mode 100644 index 0000000..a98907d --- /dev/null +++ b/lib/addReminder.dart @@ -0,0 +1,141 @@ +import 'package:flutter/material.dart'; +import 'package:nextcloud_reminder/repeating_task.dart'; + +class AddTaskWidget extends StatefulWidget { + const AddTaskWidget({super.key, this.restorationId, required this.onSave}); + + final ValueChanged onSave; + final String? restorationId; + + @override + State createState() => _AddTaskWidgetState(); +} + +class _AddTaskWidgetState extends State with RestorationMixin { + final _formKey = GlobalKey(); + final _titleController = TextEditingController(); + final int _repeat = 1; + final RestorableDateTime _beginDate = RestorableDateTime(DateTime.now()); + final _dateTextField = const TextField(); + + @override + void dispose() { + _titleController.dispose(); + super.dispose(); + } + + @override + String? get restorationId => widget.restorationId; + + late final RestorableRouteFuture _restorableDatePickerRouteFuture = + RestorableRouteFuture( + onComplete: _selectDate, + onPresent: (NavigatorState navigator, Object? arguments) { + return navigator.restorablePush( + _datePickerRoute, + arguments: _beginDate.value.millisecondsSinceEpoch, + ); + }, + ); + + static Route _datePickerRoute( + BuildContext context, + Object? arguments, + ) { + return DialogRoute( + 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; + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text( + 'Selected: ${_beginDate.value.day}/${_beginDate.value.month}/${_beginDate.value.year}'), + )); + }); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Add new repeating Task') + ), + body: Form( + key: _formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TextFormField( + // The validator receives the text that the user has entered. + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please enter a name'; + } + return null; + }, + controller: _titleController, + decoration: const InputDecoration( + labelText: "Taskname" + ), + ), + Container( + decoration: const BoxDecoration(), + child: Row( + children: [ + Text("Begin: ", style: Theme.of(context).textTheme.labelLarge), + Expanded( + child: Text("${_beginDate.value.year}-${_beginDate.value.month.toString().padLeft(2,'0')}-${_beginDate.value.day.toString().padLeft(2,'0')}") + ), + IconButton( + onPressed: () => _restorableDatePickerRouteFuture.present(), + icon: Icon(Icons.date_range, + color: Theme.of(context).focusColor)) + ] + ) + ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 16.0), + child: ElevatedButton( + onPressed: () { + // Validate returns true if the form is valid, or false otherwise. + if (_formKey.currentState!.validate()) { + // If the form is valid, display a snackbar. In the real world, + // you'd often call a server or save the information in a database. + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Task added.')), + ); + widget.onSave(RepeatingTask( + title: _titleController.text, begin: _beginDate.value)); + Navigator.pop(context); + } + }, + child: const Text('Submit'), + ), + ), + ], + ), + ) + ); + } +} diff --git a/lib/homescreen.dart b/lib/homescreen.dart new file mode 100644 index 0000000..fd00b40 --- /dev/null +++ b/lib/homescreen.dart @@ -0,0 +1,74 @@ +import 'package:flutter/material.dart'; +import 'package:nextcloud_reminder/addReminder.dart'; +import 'package:nextcloud_reminder/repeating_task.dart'; +import 'package:nextcloud_reminder/table.dart'; + +class HomeWidget extends StatefulWidget { + const HomeWidget({super.key, required this.title}); + + // This widget is the home page of your application. It is stateful, meaning + // that it has a State object (defined below) that contains fields that affect + // how it looks. + + // This class is the configuration for the state. It holds the values (in this + // case the title) provided by the parent (in this case the App widget) and + // used by the build method of the State. Fields in a Widget subclass are + // always marked "final". + + final String title; + + @override + State createState() => _HomeWidgetState(); +} + +class _HomeWidgetState extends State { + final ScrollTable _checkboxes = ScrollTable(title: "TODOs"); + int _counter = 1; + + void _addDummyTask() { + setState(() { + // This call to setState tells the Flutter framework that something has + // changed in this State, which causes it to rerun the build method below + // so that the display can reflect the updated values. If we changed + // stuff without calling setState(), then the build method would not be + // called again, and so nothing would appear to happen. + _checkboxes.addTask(RepeatingTask(title: "Dummy Task #$_counter", begin: DateTime.now(), repeat: _counter++)); + }); + } + void _addTask(RepeatingTask? t) { + if (t != null) { + setState(() { + _checkboxes.addTask(t!); + }); + } + } + + @override + Widget build(BuildContext context) { + // This method is rerun every time setState is called, for instance as done + // by the _incrementCounter method above. + // + // The Flutter framework has been optimized to make rerunning build methods + // fast, so that you can just rebuild anything that needs updating rather + // than having to individually change instances of widgets. + return Scaffold( + appBar: AppBar( + // Here we take the value from the MyHomePage object that was created by + // the App.build method, and use it to set our appbar title. + title: Text(widget.title), + ), + body: Center( + // Center is a layout widget. It takes a single child and positions it + // in the middle of the parent. + child: _checkboxes, + ), + floatingActionButton: FloatingActionButton( + onPressed: () => Navigator.of(context).push( + MaterialPageRoute(builder: (context) => AddTaskWidget(onSave: _addTask)) + ), + tooltip: 'add Task', + child: const Icon(Icons.add), + ), // This trailing comma makes auto-formatting nicer for build methods. + ); + } +} diff --git a/lib/main.dart b/lib/main.dart index c7db930..52cf98c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,93 +1,24 @@ import 'package:flutter/material.dart'; -import 'package:nextcloud_reminder/repeating_task.dart'; -import 'package:nextcloud_reminder/table.dart'; +import 'package:nextcloud_reminder/homescreen.dart'; + void main() { - runApp(const MyApp()); + runApp(const TodoTxtReminderApp()); } -class MyApp extends StatelessWidget { - const MyApp({super.key}); +class TodoTxtReminderApp extends StatelessWidget { + const TodoTxtReminderApp({super.key}); // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( - title: 'Flutter Demo', + title: 'Nextcloud Reminder', theme: ThemeData( // This is the theme of your application. - // - // Try running your application with "flutter run". You'll see the - // application has a blue toolbar. Then, without quitting the app, try - // changing the primarySwatch below to Colors.green and then invoke - // "hot reload" (press "r" in the console where you ran "flutter run", - // or simply save your changes to "hot reload" in a Flutter IDE). - // Notice that the counter didn't reset back to zero; the application - // is not restarted. primarySwatch: Colors.blue, ), - home: const MyHomePage(title: 'todo.txt reminder'), - ); - } -} - -class MyHomePage extends StatefulWidget { - const MyHomePage({super.key, required this.title}); - - // This widget is the home page of your application. It is stateful, meaning - // that it has a State object (defined below) that contains fields that affect - // how it looks. - - // This class is the configuration for the state. It holds the values (in this - // case the title) provided by the parent (in this case the App widget) and - // used by the build method of the State. Fields in a Widget subclass are - // always marked "final". - - final String title; - - @override - State createState() => _MyHomePageState(); -} - -class _MyHomePageState extends State { - final ScrollTable _checkboxes = ScrollTable(title: "TODOs"); - int _counter = 1; - - void _addDummyTask() { - setState(() { - // This call to setState tells the Flutter framework that something has - // changed in this State, which causes it to rerun the build method below - // so that the display can reflect the updated values. If we changed - // stuff without calling setState(), then the build method would not be - // called again, and so nothing would appear to happen. - _checkboxes.addTask(RepeatingTask(title: "Dummy Task #$_counter", begin: DateTime.now(), repeat: _counter++)); - }); - } - - @override - Widget build(BuildContext context) { - // This method is rerun every time setState is called, for instance as done - // by the _incrementCounter method above. - // - // The Flutter framework has been optimized to make rerunning build methods - // fast, so that you can just rebuild anything that needs updating rather - // than having to individually change instances of widgets. - return Scaffold( - appBar: AppBar( - // Here we take the value from the MyHomePage object that was created by - // the App.build method, and use it to set our appbar title. - title: Text(widget.title), - ), - body: Center( - // Center is a layout widget. It takes a single child and positions it - // in the middle of the parent. - child: _checkboxes, - ), - floatingActionButton: FloatingActionButton( - onPressed: _addDummyTask, - tooltip: 'add dummy Task', - child: const Icon(Icons.add), - ), // This trailing comma makes auto-formatting nicer for build methods. + home: const HomeWidget(title: 'todo.txt reminder'), ); } } diff --git a/test/widget_test.dart b/test/widget_test.dart index b708dcd..df3c719 100644 --- a/test/widget_test.dart +++ b/test/widget_test.dart @@ -13,7 +13,7 @@ import 'package:nextcloud_reminder/main.dart'; void main() { testWidgets('Counter increments smoke test', (WidgetTester tester) async { // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); + await tester.pumpWidget(const TodoTxtReminderApp()); // Verify that our counter starts at 0. expect(find.text('0'), findsOneWidget);