From 1a796abe772e6d2de69b6c2d7f0944cd0c19289d Mon Sep 17 00:00:00 2001 From: Iván Ávalos Date: Thu, 30 Mar 2023 00:05:43 -0600 Subject: Progress for 4th practice, event creation and reading --- lib/database/helper.dart | 3 + lib/main.dart | 2 + lib/providers/events_provider.dart | 28 ++++++++ lib/routes.dart | 2 + lib/screens/events_screen.dart | 69 ++++++++++++------ lib/screens/new_event_screen.dart | 144 +++++++++++++++++++++++++++++++++++++ lib/widgets/event_item.dart | 42 +++++++++++ lib/widgets/event_list.dart | 35 +++++++++ 8 files changed, 304 insertions(+), 21 deletions(-) create mode 100644 lib/providers/events_provider.dart create mode 100644 lib/screens/new_event_screen.dart create mode 100644 lib/widgets/event_item.dart create mode 100644 lib/widgets/event_list.dart diff --git a/lib/database/helper.dart b/lib/database/helper.dart index 5992e7f..da2d199 100644 --- a/lib/database/helper.dart +++ b/lib/database/helper.dart @@ -25,6 +25,9 @@ class DatabaseHelper { onUpgrade: ((db, oldVersion, newVersion) { _onCreate(db, newVersion); }), + onOpen: (db) { + _onCreate(db, 0); + }, ); return _database!; } diff --git a/lib/main.dart b/lib/main.dart index 0b10f9b..a174aaa 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,5 +1,6 @@ import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/material.dart'; +import 'package:pmsna1/providers/events_provider.dart'; import 'package:pmsna1/providers/flags_provider.dart'; import 'package:pmsna1/providers/theme_provider.dart'; import 'package:pmsna1/routes.dart'; @@ -34,6 +35,7 @@ class _MainContentState extends State { providers: [ ChangeNotifierProvider(create: (_) => ThemeProvider()), ChangeNotifierProvider(create: (_) => FlagsProvider()), + ChangeNotifierProvider(create: (_) => EventsProvider()), ], child: const PMSNA1(), ); diff --git a/lib/providers/events_provider.dart b/lib/providers/events_provider.dart new file mode 100644 index 0000000..eee9853 --- /dev/null +++ b/lib/providers/events_provider.dart @@ -0,0 +1,28 @@ +import 'package:flutter/foundation.dart'; +import 'package:pmsna1/database/helper.dart'; +import 'package:pmsna1/models/event.dart'; + +class EventsProvider with ChangeNotifier { + DatabaseHelper helper = DatabaseHelper(); + List _events = []; + + EventsProvider() { + fetchDB(); + } + + List get events { + return _events; + } + + set events(List events) { + _events = events; + notifyListeners(); + } + + void fetchDB() { + print('fetching events from DB'); + helper.getAllEvent().then((e) { + events = e; + }); + } +} diff --git a/lib/routes.dart b/lib/routes.dart index 1d4284b..766273b 100644 --- a/lib/routes.dart +++ b/lib/routes.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:pmsna1/screens/dashboard_screen.dart'; import 'package:pmsna1/screens/events_screen.dart'; +import 'package:pmsna1/screens/new_event_screen.dart'; import 'package:pmsna1/screens/new_post_screen.dart'; import 'package:pmsna1/screens/onboarding_screen.dart'; import 'package:pmsna1/screens/popular_screen.dart'; @@ -17,5 +18,6 @@ Map getApplicationRoutes() { '/new': (BuildContext context) => const NewPostScreen(), '/events': (BuildContext context) => const EventsScreen(), '/popular': (BuildContext context) => const PopularScreen(), + '/newevent': (BuildContext context) => const NewEventScreen(), }; } diff --git a/lib/screens/events_screen.dart b/lib/screens/events_screen.dart index 60d1b26..0b78fbe 100644 --- a/lib/screens/events_screen.dart +++ b/lib/screens/events_screen.dart @@ -1,6 +1,10 @@ import 'package:flutter/material.dart'; import 'package:table_calendar/table_calendar.dart'; +import '../widgets/event_list.dart'; + +enum Vista { calendario, lista } + class EventsScreen extends StatefulWidget { const EventsScreen({super.key}); @@ -9,6 +13,7 @@ class EventsScreen extends StatefulWidget { } class _EventsScreenState extends State { + Vista vista = Vista.calendario; DateTime _selectedDay = DateTime.now(); DateTime _focusedDay = DateTime.now(); CalendarFormat _calendarFormat = CalendarFormat.month; @@ -18,29 +23,51 @@ class _EventsScreenState extends State { return Scaffold( appBar: AppBar( title: const Text('Eventos'), - actions: const [], + actions: [ + SegmentedButton( + showSelectedIcon: false, + segments: const [ + ButtonSegment( + value: Vista.calendario, + icon: Icon(Icons.calendar_month), + ), + ButtonSegment( + value: Vista.lista, + icon: Icon(Icons.list), + ), + ], + selected: {vista}, + onSelectionChanged: (p0) { + setState(() { + vista = p0.first; + }); + }, + ) + ], ), body: Container( - child: TableCalendar( - firstDay: DateTime.fromMicrosecondsSinceEpoch(0), - lastDay: DateTime.utc(9000, 12, 31), - focusedDay: _focusedDay, - selectedDayPredicate: (day) { - return isSameDay(_selectedDay, day); - }, - onDaySelected: (selectedDay, focusedDay) { - setState(() { - _selectedDay = selectedDay; - _focusedDay = focusedDay; - }); - }, - calendarFormat: _calendarFormat, - onFormatChanged: (format) { - setState(() { - _calendarFormat = format; - }); - }, - )), + child: vista == Vista.calendario + ? TableCalendar( + firstDay: DateTime.fromMicrosecondsSinceEpoch(0), + lastDay: DateTime.utc(9000, 12, 31), + focusedDay: _focusedDay, + selectedDayPredicate: (day) { + return isSameDay(_selectedDay, day); + }, + onDaySelected: (selectedDay, focusedDay) { + setState(() { + _selectedDay = selectedDay; + _focusedDay = focusedDay; + }); + }, + calendarFormat: _calendarFormat, + onFormatChanged: (format) { + setState(() { + _calendarFormat = format; + }); + }, + ) + : const EventList()), floatingActionButton: FloatingActionButton.extended( label: const Text('Nuevo'), icon: const Icon(Icons.add), diff --git a/lib/screens/new_event_screen.dart b/lib/screens/new_event_screen.dart new file mode 100644 index 0000000..3c49ee6 --- /dev/null +++ b/lib/screens/new_event_screen.dart @@ -0,0 +1,144 @@ +import 'package:flutter/material.dart'; +import 'package:pmsna1/providers/events_provider.dart'; +import 'package:provider/provider.dart'; + +import '../database/helper.dart'; +import '../models/event.dart'; + +class NewEventScreen extends StatefulWidget { + const NewEventScreen({super.key}); + + @override + State createState() => _NewEventScreenState(); +} + +class _NewEventScreenState extends State { + Event? event; + DateTime? selectedDate; + DatabaseHelper helper = DatabaseHelper(); + + final _descController = TextEditingController(); + final _formKey = GlobalKey(); + + final padding = 12.0; + Widget get spacer => SizedBox(height: padding, width: padding); + + @override + Widget build(BuildContext context) { + event = ModalRoute.of(context)?.settings.arguments as Event?; + if (event != null) { + _descController.text = event!.description; + setState(() { + selectedDate = event!.date; + }); + } + + EventsProvider provider = Provider.of(context); + + return Scaffold( + appBar: AppBar( + title: Text(event == null ? 'Nuevo evento' : 'Editar evento'), + actions: [ + IconButton( + icon: const Icon(Icons.save), + tooltip: event == null ? 'Crear' : 'Editar', + onPressed: () { + if (_formKey.currentState?.validate() == false) { + return; + } + if (selectedDate == null) { + return; + } + + if (event == null) { + helper.insert('events', { + 'description': _descController.text, + 'date': selectedDate!.toIso8601String(), + 'completed': false, // TODO: implement checkbox + }).then((value) { + SnackBar bar = SnackBar( + content: Text( + value > 0 ? 'Evento insertado' : 'Ocurrió un error', + ), + ); + + ScaffoldMessenger.of(context).showSnackBar(bar); + Navigator.of(context).pop(); + provider.fetchDB(); + }); + } else { + helper + .update( + 'events', + { + 'description': _descController.text, + 'date': selectedDate!.toIso8601String(), + 'completed': false, + }, + 'id', + event!.id, + ) + .then((value) { + SnackBar bar = SnackBar( + content: Text( + value > 0 ? 'Evento modificado' : 'Ocurrió un error', + ), + ); + + ScaffoldMessenger.of(context).showSnackBar(bar); + Navigator.of(context).pop(); + provider.fetchDB(); + }); + } + }, + ) + ], + ), + body: Padding( + padding: const EdgeInsets.all(12.0), + child: Form( + key: _formKey, + child: Column( + children: [ + TextFormField( + controller: _descController, + textAlignVertical: TextAlignVertical.top, + decoration: const InputDecoration( + border: OutlineInputBorder(), + labelText: 'Descripción', + ), + validator: (value) { + if (value != null && value.isNotEmpty) { + return null; + } + return 'Este campo no debe estar vacío'; + }, + ), + spacer, + OutlinedButton( + child: Text(selectedDate != null + ? selectedDate!.toIso8601String() + : 'Seleccionar fecha'), + onPressed: () { + showDatePicker( + initialDate: DateTime.now(), + firstDate: DateTime.now(), + lastDate: DateTime.utc(9000, 12, 31), + currentDate: DateTime.now(), + initialEntryMode: DatePickerEntryMode.calendar, + context: context, + ).then((DateTime? date) { + if (date == null) return; + setState(() { + selectedDate = date; + }); + }); + }, + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/widgets/event_item.dart b/lib/widgets/event_item.dart new file mode 100644 index 0000000..e00d328 --- /dev/null +++ b/lib/widgets/event_item.dart @@ -0,0 +1,42 @@ +import 'package:flutter/material.dart'; + +import '../models/event.dart'; + +class EventItem extends StatelessWidget { + final Event event; + + const EventItem({super.key, required this.event}); + + final padding = 16.0; + final spacer = const SizedBox( + height: 12.0, + width: 12.0, + ); + + @override + Widget build(BuildContext context) { + final txtDate = Text( + event.date.toIso8601String(), + style: Theme.of(context).typography.englishLike.labelMedium, + ); + final txtDesc = Text( + event.description, + style: Theme.of(context).typography.englishLike.bodyLarge, + ); + + return Card( + child: Padding( + padding: EdgeInsets.all(padding), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + txtDesc, + spacer, + txtDate, + ], + ), + ), + ); + } +} diff --git a/lib/widgets/event_list.dart b/lib/widgets/event_list.dart new file mode 100644 index 0000000..437525c --- /dev/null +++ b/lib/widgets/event_list.dart @@ -0,0 +1,35 @@ +import 'package:flutter/material.dart'; +import 'package:pmsna1/database/helper.dart'; +import 'package:pmsna1/providers/events_provider.dart'; +import 'package:provider/provider.dart'; + +import '../models/event.dart'; +import 'event_item.dart'; + +class EventList extends StatefulWidget { + const EventList({super.key}); + + @override + State createState() => _EventListState(); +} + +class _EventListState extends State { + late DatabaseHelper helper; + + @override + void initState() { + super.initState(); + helper = DatabaseHelper(); + } + + @override + Widget build(BuildContext context) { + EventsProvider provider = context.watch(); + List events = provider.events; + return ListView.builder( + itemCount: events.length, + itemBuilder: (BuildContext context, int index) => + EventItem(event: events[index]), + ); + } +} -- cgit v1.2.3