From 85e557e713ed06060f8a8fb9d2b47f8e2e9fa6da Mon Sep 17 00:00:00 2001 From: Parth Baraiya Date: Thu, 23 Nov 2023 21:39:42 +0530 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Add=20option=20to=20view,=20delete?= =?UTF-8?q?=20and=20update=20the=20events=20in=20example.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Migrate example to flutter 3.3.10 --- example/.gitignore | 1 - example/lib/main.dart | 22 +-- example/lib/model/event.dart | 17 -- example/lib/pages/create_event_page.dart | 42 ++-- example/lib/pages/day_view_page.dart | 29 ++- example/lib/pages/event_details_page.dart | 42 +++- example/lib/pages/home_page.dart | 17 ++ example/lib/pages/month_view_page.dart | 30 ++- example/lib/pages/web/web_home_page.dart | 8 +- example/lib/pages/week_view_page.dart | 30 ++- ..._event_widget.dart => add_event_form.dart} | 187 ++++++++++-------- example/lib/widgets/calendar_configs.dart | 7 +- example/lib/widgets/date_time_selector.dart | 185 +++++++++++------ example/lib/widgets/day_view_widget.dart | 14 +- example/lib/widgets/month_view_widget.dart | 13 +- example/lib/widgets/week_view_widget.dart | 13 +- example/pubspec.yaml | 3 +- 17 files changed, 401 insertions(+), 259 deletions(-) delete mode 100644 example/lib/model/event.dart create mode 100644 example/lib/pages/home_page.dart rename example/lib/widgets/{add_event_widget.dart => add_event_form.dart} (62%) diff --git a/example/.gitignore b/example/.gitignore index 2b12cb7a..fc1f4cb6 100644 --- a/example/.gitignore +++ b/example/.gitignore @@ -32,7 +32,6 @@ /build/ # Web related -lib/generated_plugin_registrant.dart # Symbolication related app.*.symbols diff --git a/example/lib/main.dart b/example/lib/main.dart index 720a6a67..146e23af 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -3,10 +3,7 @@ import 'dart:ui'; import 'package:calendar_view/calendar_view.dart'; import 'package:flutter/material.dart'; -import 'model/event.dart'; -import 'pages/mobile/mobile_home_page.dart'; -import 'pages/web/web_home_page.dart'; -import 'widgets/responsive_widget.dart'; +import 'pages/home_page.dart'; DateTime get _now => DateTime.now(); @@ -18,8 +15,8 @@ class MyApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { - return CalendarControllerProvider( - controller: EventController()..addAll(_events), + return CalendarControllerProvider( + controller: EventController()..addAll(_events), child: MaterialApp( title: 'Flutter Calendar Page Demo', debugShowCheckedModeBanner: false, @@ -31,19 +28,15 @@ class MyApp extends StatelessWidget { PointerDeviceKind.touch, }, ), - home: ResponsiveWidget( - mobileWidget: MobileHomePage(), - webWidget: WebHomePage(), - ), + home: HomePage(), ), ); } } -List> _events = [ +List _events = [ CalendarEventData( date: _now, - event: Event(title: "Joe's Birthday"), title: "Project meeting", description: "Today is project meeting.", startTime: DateTime(_now.year, _now.month, _now.day, 18, 30), @@ -53,7 +46,6 @@ List> _events = [ date: _now.add(Duration(days: 1)), startTime: DateTime(_now.year, _now.month, _now.day, 18), endTime: DateTime(_now.year, _now.month, _now.day, 19), - event: Event(title: "Wedding anniversary"), title: "Wedding anniversary", description: "Attend uncle's wedding anniversary.", ), @@ -61,7 +53,6 @@ List> _events = [ date: _now, startTime: DateTime(_now.year, _now.month, _now.day, 14), endTime: DateTime(_now.year, _now.month, _now.day, 17), - event: Event(title: "Football Tournament"), title: "Football Tournament", description: "Go to football tournament.", ), @@ -71,7 +62,6 @@ List> _events = [ _now.add(Duration(days: 3)).month, _now.add(Duration(days: 3)).day, 10), endTime: DateTime(_now.add(Duration(days: 3)).year, _now.add(Duration(days: 3)).month, _now.add(Duration(days: 3)).day, 14), - event: Event(title: "Sprint Meeting."), title: "Sprint Meeting.", description: "Last day of project submission for last year.", ), @@ -87,7 +77,6 @@ List> _events = [ _now.subtract(Duration(days: 2)).month, _now.subtract(Duration(days: 2)).day, 16), - event: Event(title: "Team Meeting"), title: "Team Meeting", description: "Team Meeting", ), @@ -103,7 +92,6 @@ List> _events = [ _now.subtract(Duration(days: 2)).month, _now.subtract(Duration(days: 2)).day, 12), - event: Event(title: "Chemistry Viva"), title: "Chemistry Viva", description: "Today is Joe's birthday.", ), diff --git a/example/lib/model/event.dart b/example/lib/model/event.dart deleted file mode 100644 index faa565d2..00000000 --- a/example/lib/model/event.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'package:flutter/foundation.dart'; - -@immutable -class Event { - final String title; - - const Event({this.title = "Title"}); - - @override - bool operator ==(Object other) => other is Event && title == other.title; - - @override - int get hashCode => super.hashCode; - - @override - String toString() => title; -} diff --git a/example/lib/pages/create_event_page.dart b/example/lib/pages/create_event_page.dart index 6f9acf6c..85bfb42f 100644 --- a/example/lib/pages/create_event_page.dart +++ b/example/lib/pages/create_event_page.dart @@ -1,29 +1,14 @@ +import 'package:calendar_view/calendar_view.dart'; import 'package:flutter/material.dart'; import '../app_colors.dart'; import '../extension.dart'; -import '../widgets/add_event_widget.dart'; +import '../widgets/add_event_form.dart'; -class CreateEventPage extends StatefulWidget { - final bool withDuration; +class CreateEventPage extends StatelessWidget { + const CreateEventPage({super.key, this.event}); - const CreateEventPage({Key? key, this.withDuration = false}) - : super(key: key); - - @override - _CreateEventPageState createState() => _CreateEventPageState(); -} - -class _CreateEventPageState extends State { - @override - void initState() { - super.initState(); - } - - @override - void dispose() { - super.dispose(); - } + final CalendarEventData? event; @override Widget build(BuildContext context) { @@ -40,7 +25,7 @@ class _CreateEventPageState extends State { ), ), title: Text( - "Create New Event", + event == null ? "Create New Event" : "Update Event", style: TextStyle( color: AppColors.black, fontSize: 20.0, @@ -52,8 +37,19 @@ class _CreateEventPageState extends State { physics: ClampingScrollPhysics(), child: Padding( padding: EdgeInsets.all(20.0), - child: AddEventWidget( - onEventAdd: context.pop, + child: AddOrEditEventForm( + onEventAdd: (newEvent) { + if (this.event != null) { + CalendarControllerProvider.of(context) + .controller + .updateEvent(this.event!, newEvent); + } else { + CalendarControllerProvider.of(context).controller.add(newEvent); + } + + context.pop(true); + }, + event: event, ), ), ), diff --git a/example/lib/pages/day_view_page.dart b/example/lib/pages/day_view_page.dart index 2cb73a41..f58e9be1 100644 --- a/example/lib/pages/day_view_page.dart +++ b/example/lib/pages/day_view_page.dart @@ -1,10 +1,11 @@ -import 'package:calendar_view/calendar_view.dart'; +import 'package:example/enumerations.dart'; import 'package:flutter/material.dart'; import '../extension.dart'; -import '../model/event.dart'; import '../widgets/day_view_widget.dart'; +import '../widgets/responsive_widget.dart'; import 'create_event_page.dart'; +import 'web/web_home_page.dart'; class DayViewPageDemo extends StatefulWidget { const DayViewPageDemo({Key? key}) : super(key: key); @@ -16,20 +17,18 @@ class DayViewPageDemo extends StatefulWidget { class _DayViewPageDemoState extends State { @override Widget build(BuildContext context) { - return Scaffold( - floatingActionButton: FloatingActionButton( - child: Icon(Icons.add), - elevation: 8, - onPressed: () async { - final event = - await context.pushRoute>(CreateEventPage( - withDuration: true, - )); - if (event == null) return; - CalendarControllerProvider.of(context).controller.add(event); - }, + return ResponsiveWidget( + webWidget: WebHomePage( + selectedView: CalendarView.day, + ), + mobileWidget: Scaffold( + floatingActionButton: FloatingActionButton( + child: Icon(Icons.add), + elevation: 8, + onPressed: () => context.pushRoute(CreateEventPage()), + ), + body: DayViewWidget(), ), - body: DayViewWidget(), ); } } diff --git a/example/lib/pages/event_details_page.dart b/example/lib/pages/event_details_page.dart index 150f764c..b8b0052e 100644 --- a/example/lib/pages/event_details_page.dart +++ b/example/lib/pages/event_details_page.dart @@ -1,4 +1,5 @@ import 'package:calendar_view/calendar_view.dart'; +import 'package:example/pages/create_event_page.dart'; import 'package:flutter/material.dart'; import '../extension.dart'; @@ -76,14 +77,49 @@ class DetailsPage extends StatelessWidget { height: 30.0, ), ], - if (event.description != "") ...[ + if (event.description?.isNotEmpty ?? false) ...[ Divider(), Text("Description"), SizedBox( height: 10.0, ), - Text(event.description), - ] + Text(event.description!), + ], + const SizedBox(height: 50), + Row( + children: [ + Expanded( + child: ElevatedButton( + onPressed: () { + CalendarControllerProvider.of(context) + .controller + .remove(event); + Navigator.of(context).pop(); + }, + child: Text('Delete Event'), + ), + ), + SizedBox(width: 30), + Expanded( + child: ElevatedButton( + onPressed: () async { + final result = await Navigator.of(context).push( + MaterialPageRoute( + builder: (_) => CreateEventPage( + event: event, + ), + ), + ); + + if (result) { + Navigator.of(context).pop(); + } + }, + child: Text('Edit Event'), + ), + ), + ], + ), ], ), ); diff --git a/example/lib/pages/home_page.dart b/example/lib/pages/home_page.dart new file mode 100644 index 00000000..894d1f76 --- /dev/null +++ b/example/lib/pages/home_page.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; + +import '../widgets/responsive_widget.dart'; +import 'mobile/mobile_home_page.dart'; +import 'web/web_home_page.dart'; + +class HomePage extends StatelessWidget { + const HomePage({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return ResponsiveWidget( + mobileWidget: MobileHomePage(), + webWidget: WebHomePage(), + ); + } +} diff --git a/example/lib/pages/month_view_page.dart b/example/lib/pages/month_view_page.dart index 7da6d0c3..9cc77e88 100644 --- a/example/lib/pages/month_view_page.dart +++ b/example/lib/pages/month_view_page.dart @@ -1,10 +1,11 @@ -import 'package:calendar_view/calendar_view.dart'; import 'package:flutter/material.dart'; +import '../enumerations.dart'; import '../extension.dart'; -import '../model/event.dart'; import '../widgets/month_view_widget.dart'; +import '../widgets/responsive_widget.dart'; import 'create_event_page.dart'; +import 'web/web_home_page.dart'; class MonthViewPageDemo extends StatefulWidget { const MonthViewPageDemo({ @@ -18,23 +19,18 @@ class MonthViewPageDemo extends StatefulWidget { class _MonthViewPageDemoState extends State { @override Widget build(BuildContext context) { - return Scaffold( - floatingActionButton: FloatingActionButton( - child: Icon(Icons.add), - elevation: 8, - onPressed: _addEvent, + return ResponsiveWidget( + webWidget: WebHomePage( + selectedView: CalendarView.month, ), - body: MonthViewWidget(), - ); - } - - Future _addEvent() async { - final event = await context.pushRoute>( - CreateEventPage( - withDuration: true, + mobileWidget: Scaffold( + floatingActionButton: FloatingActionButton( + child: Icon(Icons.add), + elevation: 8, + onPressed: () => context.pushRoute(CreateEventPage()), + ), + body: MonthViewWidget(), ), ); - if (event == null) return; - CalendarControllerProvider.of(context).controller.add(event); } } diff --git a/example/lib/pages/web/web_home_page.dart b/example/lib/pages/web/web_home_page.dart index 6c1ebfc4..3fe2b836 100644 --- a/example/lib/pages/web/web_home_page.dart +++ b/example/lib/pages/web/web_home_page.dart @@ -5,12 +5,18 @@ import '../../widgets/calendar_configs.dart'; import '../../widgets/calendar_views.dart'; class WebHomePage extends StatefulWidget { + WebHomePage({ + this.selectedView = CalendarView.month, + }); + + final CalendarView selectedView; + @override _WebHomePageState createState() => _WebHomePageState(); } class _WebHomePageState extends State { - CalendarView _selectedView = CalendarView.month; + late var _selectedView = widget.selectedView; void _setView(CalendarView view) { if (view != _selectedView && mounted) { diff --git a/example/lib/pages/week_view_page.dart b/example/lib/pages/week_view_page.dart index 0de511d7..d177cdbd 100644 --- a/example/lib/pages/week_view_page.dart +++ b/example/lib/pages/week_view_page.dart @@ -1,10 +1,12 @@ import 'package:calendar_view/calendar_view.dart'; +import 'package:example/enumerations.dart'; +import 'package:example/widgets/responsive_widget.dart'; import 'package:flutter/material.dart'; import '../extension.dart'; -import '../model/event.dart'; import '../widgets/week_view_widget.dart'; import 'create_event_page.dart'; +import 'web/web_home_page.dart'; class WeekViewDemo extends StatefulWidget { const WeekViewDemo({Key? key}) : super(key: key); @@ -16,22 +18,18 @@ class WeekViewDemo extends StatefulWidget { class _WeekViewDemoState extends State { @override Widget build(BuildContext context) { - return Scaffold( - floatingActionButton: FloatingActionButton( - child: Icon(Icons.add), - elevation: 8, - onPressed: _addEvent, + return ResponsiveWidget( + webWidget: WebHomePage( + selectedView: CalendarView.week, + ), + mobileWidget: Scaffold( + floatingActionButton: FloatingActionButton( + child: Icon(Icons.add), + elevation: 8, + onPressed: () => context.pushRoute(CreateEventPage()), + ), + body: WeekViewWidget(), ), - body: WeekViewWidget(), ); } - - Future _addEvent() async { - final event = - await context.pushRoute>(CreateEventPage( - withDuration: true, - )); - if (event == null) return; - CalendarControllerProvider.of(context).controller.add(event); - } } diff --git a/example/lib/widgets/add_event_widget.dart b/example/lib/widgets/add_event_form.dart similarity index 62% rename from example/lib/widgets/add_event_widget.dart rename to example/lib/widgets/add_event_form.dart index 52115696..1dcba9d4 100644 --- a/example/lib/widgets/add_event_widget.dart +++ b/example/lib/widgets/add_event_form.dart @@ -5,73 +5,53 @@ import 'package:flutter_colorpicker/flutter_colorpicker.dart'; import '../app_colors.dart'; import '../constants.dart'; import '../extension.dart'; -import '../model/event.dart'; import 'custom_button.dart'; import 'date_time_selector.dart'; -class AddEventWidget extends StatefulWidget { - final void Function(CalendarEventData)? onEventAdd; +class AddOrEditEventForm extends StatefulWidget { + final void Function(CalendarEventData)? onEventAdd; + final CalendarEventData? event; - const AddEventWidget({ - Key? key, + const AddOrEditEventForm({ + super.key, this.onEventAdd, - }) : super(key: key); + this.event, + }); @override - _AddEventWidgetState createState() => _AddEventWidgetState(); + _AddOrEditEventFormState createState() => _AddOrEditEventFormState(); } -class _AddEventWidgetState extends State { - late DateTime _startDate; - late DateTime _endDate; +class _AddOrEditEventFormState extends State { + late DateTime _startDate = DateTime.now().withoutTime; + late DateTime _endDate = DateTime.now().withoutTime; DateTime? _startTime; - DateTime? _endTime; - String _title = ""; - - String _description = ""; - Color _color = Colors.blue; - late FocusNode _titleNode; - - late FocusNode _descriptionNode; + final _form = GlobalKey(); - late FocusNode _dateNode; - - final GlobalKey _form = GlobalKey(); - - late TextEditingController _startDateController; - late TextEditingController _startTimeController; - late TextEditingController _endTimeController; - late TextEditingController _endDateController; + late final _descriptionController = TextEditingController(); + late final _titleController = TextEditingController(); + late final _titleNode = FocusNode(); + late final _descriptionNode = FocusNode(); @override void initState() { super.initState(); - _titleNode = FocusNode(); - _descriptionNode = FocusNode(); - _dateNode = FocusNode(); - - _startDateController = TextEditingController(); - _endDateController = TextEditingController(); - _startTimeController = TextEditingController(); - _endTimeController = TextEditingController(); + _setDefaults(); } @override void dispose() { _titleNode.dispose(); _descriptionNode.dispose(); - _dateNode.dispose(); - _startDateController.dispose(); - _endDateController.dispose(); - _startTimeController.dispose(); - _endTimeController.dispose(); + _descriptionController.dispose(); + _titleController.dispose(); super.dispose(); } @@ -84,6 +64,7 @@ class _AddEventWidgetState extends State { mainAxisSize: MainAxisSize.min, children: [ TextFormField( + controller: _titleController, decoration: AppConstants.inputDecoration.copyWith( labelText: "Event Title", ), @@ -91,9 +72,10 @@ class _AddEventWidgetState extends State { color: AppColors.black, fontSize: 17.0, ), - onSaved: (value) => _title = value?.trim() ?? "", validator: (value) { - if (value == null || value == "") + final title = value?.trim(); + + if (title == null || title == "") return "Please enter event title."; return null; @@ -108,13 +90,25 @@ class _AddEventWidgetState extends State { children: [ Expanded( child: DateTimeSelectorFormField( - controller: _startDateController, decoration: AppConstants.inputDecoration.copyWith( labelText: "Start Date", ), + initialDateTime: _startDate, + onSelect: (date) { + if (date.withoutTime.withoutTime + .isAfter(_endDate.withoutTime)) { + _endDate = date.withoutTime; + } + + _startDate = date.withoutTime; + + if (mounted) { + setState(() {}); + } + }, validator: (value) { if (value == null || value == "") - return "Please select date."; + return "Please select start date."; return null; }, @@ -122,20 +116,34 @@ class _AddEventWidgetState extends State { color: AppColors.black, fontSize: 17.0, ), - onSave: (date) => _startDate = date, + onSave: (date) => _startDate = date ?? _startDate, type: DateTimeSelectionType.date, ), ), SizedBox(width: 20.0), Expanded( child: DateTimeSelectorFormField( - controller: _endDateController, + initialDateTime: _endDate, decoration: AppConstants.inputDecoration.copyWith( labelText: "End Date", ), + onSelect: (date) { + if (date.withoutTime.withoutTime + .isBefore(_startDate.withoutTime)) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text('End date occurs before start date.'), + )); + } else { + _endDate = date.withoutTime; + } + + if (mounted) { + setState(() {}); + } + }, validator: (value) { if (value == null || value == "") - return "Please select date."; + return "Please select end date."; return null; }, @@ -143,28 +151,32 @@ class _AddEventWidgetState extends State { color: AppColors.black, fontSize: 17.0, ), - onSave: (date) => _endDate = date, + onSave: (date) => _endDate = date ?? _endDate, type: DateTimeSelectionType.date, ), ), ], ), - SizedBox( - height: 15, - ), + SizedBox(height: 15), Row( children: [ Expanded( child: DateTimeSelectorFormField( - controller: _startTimeController, decoration: AppConstants.inputDecoration.copyWith( labelText: "Start Time", ), - validator: (value) { - if (value == null || value == "") - return "Please select start time."; - - return null; + initialDateTime: _startTime, + minimumDateTime: CalendarConstants.epochDate, + onSelect: (date) { + if (_endTime != null && + date.getTotalMinutes > _endTime!.getTotalMinutes) { + _endTime = date.add(Duration(minutes: 1)); + } + _startTime = date; + + if (mounted) { + setState(() {}); + } }, onSave: (date) => _startTime = date, textStyle: TextStyle( @@ -177,15 +189,23 @@ class _AddEventWidgetState extends State { SizedBox(width: 20.0), Expanded( child: DateTimeSelectorFormField( - controller: _endTimeController, decoration: AppConstants.inputDecoration.copyWith( labelText: "End Time", ), - validator: (value) { - if (value == null || value == "") - return "Please select end time."; - - return null; + initialDateTime: _endTime, + onSelect: (date) { + if (_startTime != null && + date.getTotalMinutes < _startTime!.getTotalMinutes) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text('End time is less then start time.'), + )); + } else { + _endTime = date; + } + + if (mounted) { + setState(() {}); + } }, onSave: (date) => _endTime = date, textStyle: TextStyle( @@ -201,6 +221,7 @@ class _AddEventWidgetState extends State { height: 15, ), TextFormField( + controller: _descriptionController, focusNode: _descriptionNode, style: TextStyle( color: AppColors.black, @@ -218,7 +239,6 @@ class _AddEventWidgetState extends State { return null; }, - onSaved: (value) => _description = value?.trim() ?? "", decoration: AppConstants.inputDecoration.copyWith( hintText: "Event Description", ), @@ -249,7 +269,7 @@ class _AddEventWidgetState extends State { ), CustomButton( onTap: _createEvent, - title: "Add Event", + title: widget.event == null ? "Add Event" : "Update Event", ), ], ), @@ -261,28 +281,39 @@ class _AddEventWidgetState extends State { _form.currentState?.save(); - final event = CalendarEventData( + final event = CalendarEventData( date: _startDate, - color: _color, endTime: _endTime, startTime: _startTime, - description: _description, endDate: _endDate, - title: _title, - event: Event( - title: _title, - ), + color: _color, + title: _titleController.text.trim(), + description: _descriptionController.text.trim(), ); widget.onEventAdd?.call(event); _resetForm(); } + void _setDefaults() { + if (widget.event == null) return; + + final event = widget.event!; + + _startDate = event.date; + _endDate = event.endDate; + _startTime = event.startTime ?? _startTime; + _endTime = event.endTime ?? _endTime; + _titleController.text = event.title; + _descriptionController.text = event.description ?? ''; + } + void _resetForm() { _form.currentState?.reset(); - _startDateController.text = ""; - _endTimeController.text = ""; - _startTimeController.text = ""; + _startDate = DateTime.now().withoutTime; + _endDate = DateTime.now().withoutTime; + _startTime = null; + _endTime = null; } void _displayColorPicker() { @@ -294,16 +325,12 @@ class _AddEventWidgetState extends State { builder: (_) => SimpleDialog( clipBehavior: Clip.hardEdge, shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(30.0), - side: BorderSide( - color: AppColors.bluishGrey, - width: 2, - ), + borderRadius: BorderRadius.circular(20.0), ), contentPadding: EdgeInsets.all(20.0), children: [ Text( - "Event Color", + "Select event color", style: TextStyle( color: AppColors.black, fontSize: 25.0, diff --git a/example/lib/widgets/calendar_configs.dart b/example/lib/widgets/calendar_configs.dart index b429f1fe..31b0c327 100644 --- a/example/lib/widgets/calendar_configs.dart +++ b/example/lib/widgets/calendar_configs.dart @@ -4,8 +4,7 @@ import 'package:flutter/material.dart'; import '../app_colors.dart'; import '../enumerations.dart'; import '../extension.dart'; -import '../model/event.dart'; -import 'add_event_widget.dart'; +import 'add_event_form.dart'; class CalendarConfig extends StatelessWidget { final void Function(CalendarView view) onViewChange; @@ -99,9 +98,9 @@ class CalendarConfig extends StatelessWidget { SizedBox( height: 20, ), - AddEventWidget( + AddOrEditEventForm( onEventAdd: (event) { - CalendarControllerProvider.of(context) + CalendarControllerProvider.of(context) .controller .add(event); }, diff --git a/example/lib/widgets/date_time_selector.dart b/example/lib/widgets/date_time_selector.dart index 77a2fcf3..a6ee1800 100644 --- a/example/lib/widgets/date_time_selector.dart +++ b/example/lib/widgets/date_time_selector.dart @@ -8,28 +8,36 @@ typedef Validator = String? Function(String? value); enum DateTimeSelectionType { date, time } class DateTimeSelectorFormField extends StatefulWidget { - final Function(DateTime?)? onSelect; - final DateTimeSelectionType? type; + /// Called when date is selected. + final Function(DateTime)? onSelect; + + /// Selection time, date or time. + final DateTimeSelectionType type; + final FocusNode? focusNode; + + /// Minimum date that can be selected. final DateTime? minimumDateTime; + final Validator? validator; - final bool displayDefault; + final TextStyle? textStyle; - final void Function(DateTime date)? onSave; + final void Function(DateTime? date)? onSave; final InputDecoration? decoration; - final TextEditingController controller; + final TextEditingController? controller; + final DateTime? initialDateTime; const DateTimeSelectorFormField({ this.onSelect, - this.type, + this.type = DateTimeSelectionType.time, this.onSave, this.decoration, this.focusNode, this.minimumDateTime, this.validator, - this.displayDefault = false, this.textStyle, - required this.controller, + this.controller, + this.initialDateTime, }); @override @@ -38,84 +46,128 @@ class DateTimeSelectorFormField extends StatefulWidget { } class _DateTimeSelectorFormFieldState extends State { - late TextEditingController _textEditingController; - late FocusNode _focusNode; + bool _isSelectorOpen = false; + + late var _minimumDate = CalendarConstants.minDate.withoutTime; - late DateTime _selectedDate; + late var _textEditingController = + widget.controller ?? TextEditingController(); + late var _focusNode = _getFocusNode(); + + late DateTime? _selectedDate; @override void initState() { super.initState(); - _textEditingController = widget.controller; - _focusNode = FocusNode(); - - _selectedDate = widget.minimumDateTime ?? DateTime.now(); - - if (widget.displayDefault && widget.minimumDateTime != null) { - if (widget.type == DateTimeSelectionType.date) { - _textEditingController.text = widget.minimumDateTime - ?.dateToStringWithFormat(format: "dd/MM/yyyy") ?? - ""; - } else { - _textEditingController.text = - widget.minimumDateTime?.getTimeInFormat(TimeStampFormat.parse_12) ?? - ""; - } + _setDates(); + } + + @override + void didUpdateWidget(covariant DateTimeSelectorFormField oldWidget) { + super.didUpdateWidget(oldWidget); + + if (widget.focusNode != oldWidget.focusNode) { + _focusNode.dispose(); + _focusNode = _getFocusNode(); + } + + if (widget.controller != oldWidget.controller) { + _textEditingController.dispose(); + _textEditingController = widget.controller ?? TextEditingController(); + } + + if (_selectedDate != oldWidget.initialDateTime || + widget.minimumDateTime != oldWidget.minimumDateTime) { + _setDates(); } } + FocusNode _getFocusNode() { + final node = widget.focusNode ?? FocusNode(); + + // node.addListener(() { + // if (node.hasFocus) { + // _showSelector(); + // } + // }); + + return node; + } + @override void dispose() { - _focusNode.dispose(); + if (widget.controller == null) _textEditingController.dispose(); + if (widget.focusNode == null) _focusNode.dispose(); + super.dispose(); } + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: _showSelector, + child: TextFormField( + focusNode: _focusNode, + style: widget.textStyle, + controller: _textEditingController, + validator: widget.validator, + minLines: 1, + onSaved: (value) => widget.onSave?.call(_selectedDate), + enabled: false, + decoration: widget.decoration, + ), + ); + } + Future _showSelector() async { DateTime? date; if (widget.type == DateTimeSelectionType.date) { date = await _showDateSelector(); - _textEditingController.text = - (date ?? _selectedDate).dateToStringWithFormat(format: "dd/MM/yyyy"); + + _textEditingController.text = (date ?? _selectedDate) + ?.dateToStringWithFormat(format: "dd/MM/yyyy") ?? + ''; } else { date = await _showTimeSelector(); + _textEditingController.text = - (date ?? _selectedDate).getTimeInFormat(TimeStampFormat.parse_12); + (date ?? _selectedDate)?.getTimeInFormat(TimeStampFormat.parse_12) ?? + ''; } - _selectedDate = date ?? DateTime.now(); + _selectedDate = date ?? _selectedDate; if (mounted) { setState(() {}); } - widget.onSelect?.call(date); + if (date != null) { + widget.onSelect?.call(date); + } } Future _showDateSelector() async { - final now = widget.minimumDateTime ?? DateTime.now(); - final date = await showDatePicker( context: context, - initialDate: now, - firstDate: widget.minimumDateTime ?? now, + initialDate: _selectedDate ?? DateTime.now(), + firstDate: widget.minimumDateTime ?? CalendarConstants.minDate, lastDate: CalendarConstants.maxDate, ); - if (date == null) return null; - return date; } Future _showTimeSelector() async { - final now = widget.minimumDateTime ?? DateTime.now(); + final now = _selectedDate ?? DateTime.now(); + final time = await showTimePicker( context: context, builder: (context, widget) { - return widget ?? Container(); + return widget ?? SizedBox.shrink(); }, - initialTime: TimeOfDay(hour: now.hour, minute: now.minute), + initialTime: TimeOfDay.fromDateTime(now), ); if (time == null) return null; @@ -125,24 +177,43 @@ class _DateTimeSelectorFormFieldState extends State { minute: time.minute, ); - if (widget.minimumDateTime == null) return date; - return date; } - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: _showSelector, - child: TextFormField( - style: widget.textStyle, - controller: _textEditingController, - validator: widget.validator, - minLines: 1, - onSaved: (value) => widget.onSave?.call(_selectedDate), - enabled: false, - decoration: widget.decoration, - ), - ); + void _setDates() { + _minimumDate = widget.minimumDateTime ?? CalendarConstants.minDate; + _selectedDate = widget.initialDateTime; + + switch (widget.type) { + case DateTimeSelectionType.date: + if (_selectedDate?.withoutTime.isBefore(_minimumDate.withoutTime) ?? + false) { + throw 'InitialDate is smaller than Minimum date'; + } + + // We are adding this to avoid internal error while + // rebuilding the widget. + WidgetsBinding.instance.addPostFrameCallback((_) { + _textEditingController.text = + _selectedDate?.dateToStringWithFormat(format: "dd/MM/yyyy") ?? ''; + }); + + break; + + case DateTimeSelectionType.time: + if (_selectedDate != null && + _selectedDate!.getTotalMinutes < _minimumDate.getTotalMinutes) { + throw 'InitialDate is smaller than Minimum date'; + } + + // We are adding this to avoid internal error while + // rebuilding the widget. + WidgetsBinding.instance.addPostFrameCallback((_) { + _textEditingController.text = + _selectedDate?.getTimeInFormat(TimeStampFormat.parse_12) ?? ''; + }); + + break; + } } } diff --git a/example/lib/widgets/day_view_widget.dart b/example/lib/widgets/day_view_widget.dart index 452ebea7..819e212e 100644 --- a/example/lib/widgets/day_view_widget.dart +++ b/example/lib/widgets/day_view_widget.dart @@ -1,8 +1,7 @@ import 'package:calendar_view/calendar_view.dart'; +import 'package:example/pages/event_details_page.dart'; import 'package:flutter/material.dart'; -import '../model/event.dart'; - class DayViewWidget extends StatelessWidget { final GlobalKey? state; final double? width; @@ -15,7 +14,7 @@ class DayViewWidget extends StatelessWidget { @override Widget build(BuildContext context) { - return DayView( + return DayView( key: state, width: width, startDuration: Duration(hours: 8), @@ -25,6 +24,15 @@ class DayViewWidget extends StatelessWidget { hourIndicatorSettings: HourIndicatorSettings( color: Theme.of(context).dividerColor, ), + onEventTap: (events, date) { + Navigator.of(context).push( + MaterialPageRoute( + builder: (_) => DetailsPage( + event: events.first, + ), + ), + ); + }, halfHourIndicatorSettings: HourIndicatorSettings( color: Theme.of(context).dividerColor, lineStyle: LineStyle.dashed, diff --git a/example/lib/widgets/month_view_widget.dart b/example/lib/widgets/month_view_widget.dart index be6ee093..fc41a432 100644 --- a/example/lib/widgets/month_view_widget.dart +++ b/example/lib/widgets/month_view_widget.dart @@ -1,7 +1,7 @@ import 'package:calendar_view/calendar_view.dart'; import 'package:flutter/material.dart'; -import '../model/event.dart'; +import '../pages/event_details_page.dart'; class MonthViewWidget extends StatelessWidget { final GlobalKey? state; @@ -15,9 +15,18 @@ class MonthViewWidget extends StatelessWidget { @override Widget build(BuildContext context) { - return MonthView( + return MonthView( key: state, width: width, + onEventTap: (event, date) { + Navigator.of(context).push( + MaterialPageRoute( + builder: (_) => DetailsPage( + event: event, + ), + ), + ); + }, ); } } diff --git a/example/lib/widgets/week_view_widget.dart b/example/lib/widgets/week_view_widget.dart index b54623dd..3dcc3435 100644 --- a/example/lib/widgets/week_view_widget.dart +++ b/example/lib/widgets/week_view_widget.dart @@ -1,7 +1,7 @@ import 'package:calendar_view/calendar_view.dart'; import 'package:flutter/material.dart'; -import '../model/event.dart'; +import '../pages/event_details_page.dart'; class WeekViewWidget extends StatelessWidget { final GlobalKey? state; @@ -11,9 +11,18 @@ class WeekViewWidget extends StatelessWidget { @override Widget build(BuildContext context) { - return WeekView( + return WeekView( key: state, width: width, + onEventTap: (events, date) { + Navigator.of(context).push( + MaterialPageRoute( + builder: (_) => DetailsPage( + event: events.first, + ), + ), + ); + }, ); } } diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 80e5991f..e4b34e86 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -18,7 +18,8 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 environment: - sdk: ">=2.12.0 <3.0.0" + sdk: ">=2.18.6 <3.0.0" + flutter: ^3.3.10 dependencies: flutter: