From 6e611268f1dd8937d577031d143e6544621a02ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20D=C3=A1vila?= Date: Tue, 12 Mar 2024 21:06:13 -0500 Subject: [PATCH 1/2] Hosted by section added and powered from Firebase, also event detail screen refactored and separated by widgets --- lib/src/models/event.dart | 6 +- lib/src/ui/screens/event_detail_screen.dart | 337 +----------------- .../ui/widgets/event_detail_body_widget.dart | 224 ++++++++++++ .../ui/widgets/event_detail_item_widget.dart | 74 ++++ lib/src/ui/widgets/host_by_widget.dart | 34 ++ lib/src/ui/widgets/navbar_event_widget.dart | 55 +++ 6 files changed, 400 insertions(+), 330 deletions(-) create mode 100644 lib/src/ui/widgets/event_detail_body_widget.dart create mode 100644 lib/src/ui/widgets/event_detail_item_widget.dart create mode 100644 lib/src/ui/widgets/host_by_widget.dart create mode 100644 lib/src/ui/widgets/navbar_event_widget.dart diff --git a/lib/src/models/event.dart b/lib/src/models/event.dart index 4bc9848..3e1dcdd 100644 --- a/lib/src/models/event.dart +++ b/lib/src/models/event.dart @@ -13,6 +13,7 @@ class Event { final String calendarUrl; final String locationUrl; final List attendees; + final List speakers; int get attendeesCount => attendees.length; @@ -29,6 +30,7 @@ class Event { required this.calendarUrl, required this.locationUrl, required this.attendees, + required this.speakers, }); factory Event.fromJson({ @@ -37,8 +39,7 @@ class Event { }) { final dateTimeTimeStamp = json['dateTime'] as Timestamp; - final recommendations = - (json['recommendations'] as String ?? '').replaceAll("\\n", "\n"); + final recommendations = (json['recommendations']).replaceAll("\\n", "\n"); final attendees = (json['attendees'] as List? ?? []) .map((uid) => '$uid') @@ -57,6 +58,7 @@ class Event { calendarUrl: json['calendar_url'] ?? '', recommendations: recommendations, attendees: attendees, + speakers: json['speakers'] ?? {}, ); } } diff --git a/lib/src/ui/screens/event_detail_screen.dart b/lib/src/ui/screens/event_detail_screen.dart index f5ea373..fcefc7b 100644 --- a/lib/src/ui/screens/event_detail_screen.dart +++ b/lib/src/ui/screens/event_detail_screen.dart @@ -1,13 +1,7 @@ -import 'package:cached_network_image/cached_network_image.dart'; -import 'package:firebase_ui_auth/firebase_ui_auth.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_community_ibague/src/config/app_assets.dart'; import 'package:flutter_community_ibague/src/config/app_colors.dart'; -import 'package:flutter_community_ibague/src/notifiers/auth_notifier.dart'; -import 'package:flutter_community_ibague/src/notifiers/event_notifier.dart'; -import 'package:intl/intl.dart'; -import 'package:provider/provider.dart'; -import 'package:url_launcher/url_launcher.dart'; +import 'package:flutter_community_ibague/src/ui/widgets/event_detail_body_widget.dart'; +import 'package:flutter_community_ibague/src/ui/widgets/navbar_event_widget.dart'; class EventDetailScreen extends StatelessWidget { const EventDetailScreen({ @@ -16,18 +10,9 @@ class EventDetailScreen extends StatelessWidget { @override Widget build(BuildContext context) { - final notifier = context.watch(); final bigScreen = MediaQuery.sizeOf(context).width > 800; final widthScreen = MediaQuery.sizeOf(context).width; - final event = notifier.event; - // Convertir la fecha al formato deseado - DateTime fecha = DateTime.parse(event.dateTime.toString()); - String fechaFormateada = DateFormat('d MMMM, y', 'es').format(fecha); - // Extraer la hora - String horaFormateada = DateFormat('HH:mm').format(fecha); - // Determinar el día de la semana - String diaSemana = DateFormat('EEEE', 'es').format(fecha); return Scaffold( appBar: AppBar( title: const Text( @@ -48,246 +33,14 @@ class EventDetailScreen extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ - ConstrainedBox( - constraints: BoxConstraints(maxWidth: widthScreen), - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Text( - event.title, - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: bigScreen ? 36 : 28, - ), - ), - ListTile( - leading: const CircleAvatar(), - title: Text( - 'Hosted By', - style: TextStyle( - fontWeight: FontWeight.w300, - fontSize: bigScreen ? 18 : 12, - ), - ), - subtitle: Text( - 'Camilo Cubillos y Jorge Lopez', - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: bigScreen ? 18 : 12, - ), - ), - ), - const SizedBox(height: 25), - ], - ), - ), + NavbarEventWidget( + widthScreen: widthScreen, + bigScreen: bigScreen, + ), + EventDetailBodyWidget( + bigScreen: bigScreen, + widthScreen: widthScreen, ), - Wrap( - children: [ - ConstrainedBox( - constraints: BoxConstraints( - maxWidth: bigScreen ? widthScreen * 0.5 : widthScreen, - ), - child: Column( - children: [ - Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox( - child: CachedNetworkImage( - imageUrl: event.banner, - fit: BoxFit.cover, - width: bigScreen - ? widthScreen * 0.6 - : widthScreen, - ), - ), - const SizedBox(height: 20), - Text( - 'Acerca del evento', - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: bigScreen ? 22 : 16, - ), - ), - Text( - event.description, - textAlign: TextAlign.justify, - style: - TextStyle(fontSize: bigScreen ? 20 : 14), - ), - const SizedBox(height: 20), - if (event.recommendations != '') - Text( - 'Recomendaciones', - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: bigScreen ? 22 : 16, - ), - ), - Text( - event.recommendations, - style: - TextStyle(fontSize: bigScreen ? 20 : 14), - ), - ], - ), - const SizedBox( - height: 20, - ), - ], - ), - ), - SizedBox(width: widthScreen * 0.1), - ConstrainedBox( - constraints: BoxConstraints( - maxWidth: bigScreen ? widthScreen * 0.3 : widthScreen, - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Card( - child: Column( - children: [ - Padding( - padding: const EdgeInsets.symmetric( - horizontal: 15.0, - vertical: 12.0, - ), - child: Column( - children: [ - EventDetailItem( - iconAsset: AppAssets.calendar, - title: fechaFormateada, - subTitle: - '$diaSemana, $horaFormateada', - isBigScreen: bigScreen, - onTap: () { - final Uri uri = - Uri.parse(event.calendarUrl); - launchUrl(uri); - }, - isIcon: false, - ), - EventDetailItem( - iconAsset: AppAssets.location, - title: event.locationTitle, - subTitle: event.locationDetails, - isBigScreen: bigScreen, - onTap: () { - final Uri uri = - Uri.parse(event.locationUrl); - launchUrl(uri); - }, - isIcon: false, - ), - EventDetailItem( - iconAsset: AppAssets.location, - title: - '+${event.attendeesCount} Asistirán', - subTitle: '', - isBigScreen: bigScreen, - onTap: () {}, - isIcon: true, - ), - ], - ), - ), - ], - ), - ), - const SizedBox(height: 20), - ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: notifier.attending - ? AppColors.cancel - : AppColors.primary, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(15), - ), - padding: EdgeInsets.symmetric( - vertical: bigScreen ? 20 : 15, - horizontal: 100, - ), - ), - onPressed: () async { - final authNotifier = - context.read(); - if (notifier.attending) { - notifier.notAttendEvent(); - return; - } - if (authNotifier.user == null) { - await showDialog( - context: context, - builder: (ctx) => AlertDialog( - title: const Text('Inicia sesión'), - content: const Text( - 'Debes iniciar sesión para poder registrarte al evento', - style: TextStyle( - fontSize: 16, - )), - actions: [ - TextButton( - onPressed: () { - Navigator.pop(ctx); - }, - child: const Text('Ok')) - ], - )); - final logged = - await showModalBottomSheet( - context: context, - builder: (ctx) { - late final Widget child; - - child = SignInScreen( - actions: [ - AuthStateChangeAction< - SignedIn>( - (context, state) { - Navigator.pop(ctx, true); - }), - AuthStateChangeAction< - UserCreated>( - (context, state) { - Navigator.pop(ctx, true); - }), - ], - ); - - return Scaffold(body: child); - }) ?? - false; - - if (logged) { - notifier.attendEvent(); - } - } else { - notifier.attendEvent(); - } - }, - child: Text( - notifier.attending - ? 'Cancelar Asistencia' - : 'Asistir', - style: TextStyle( - color: Colors.white, - fontSize: bigScreen ? 26 : 18, - fontWeight: FontWeight.w300, - ), - ), - ), - if (!bigScreen) const SizedBox(height: 20), - ], - ), - ), - ], - ) ], ), ), @@ -297,75 +50,3 @@ class EventDetailScreen extends StatelessWidget { ); } } - -class EventDetailItem extends StatelessWidget { - final String iconAsset; - final String title; - final String subTitle; - final bool isBigScreen; - final VoidCallback onTap; - final bool isIcon; // todo(davila): do not use this - - const EventDetailItem({ - super.key, - required this.iconAsset, - required this.title, - required this.subTitle, - required this.isBigScreen, - required this.onTap, - required this.isIcon, - }); - - @override - Widget build(BuildContext context) { - return MouseRegion( - cursor: SystemMouseCursors.click, - child: GestureDetector( - onTap: onTap, - child: Row( - children: [ - Container( - padding: const EdgeInsets.all(10), - margin: const EdgeInsets.only( - right: 10, - top: 10, - bottom: 10, - ), - child: isIcon - ? const Icon( - Icons - .people_outline_rounded, // todo(davila): change this logic - size: 30.0, - color: Color.fromRGBO(86, 105, 255, 1), - ) - : Image.asset( - iconAsset, - height: 30, - width: 30, - ), - ), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - title, - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: isBigScreen ? 22 : 14, - overflow: TextOverflow.ellipsis, - ), - ), - Text( - subTitle, - style: TextStyle( - fontSize: isBigScreen ? 16 : 10, - overflow: TextOverflow.ellipsis, - ), - ), - ], - ), - ], - ), - )); - } -} diff --git a/lib/src/ui/widgets/event_detail_body_widget.dart b/lib/src/ui/widgets/event_detail_body_widget.dart new file mode 100644 index 0000000..a87c347 --- /dev/null +++ b/lib/src/ui/widgets/event_detail_body_widget.dart @@ -0,0 +1,224 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:firebase_ui_auth/firebase_ui_auth.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_community_ibague/src/config/app_assets.dart'; +import 'package:flutter_community_ibague/src/config/app_colors.dart'; +import 'package:flutter_community_ibague/src/notifiers/auth_notifier.dart'; +import 'package:flutter_community_ibague/src/notifiers/event_notifier.dart'; +import 'package:flutter_community_ibague/src/ui/widgets/event_detail_item_widget.dart'; +import 'package:intl/intl.dart'; +import 'package:provider/provider.dart'; +import 'package:url_launcher/url_launcher.dart'; + +class EventDetailBodyWidget extends StatelessWidget { + final bool bigScreen; + final double widthScreen; + + const EventDetailBodyWidget({ + super.key, + required this.bigScreen, + required this.widthScreen, + }); + + @override + Widget build(BuildContext context) { + final notifier = context.watch(); + final event = notifier.event; + + // Convertir la fecha al formato deseado + DateTime fecha = DateTime.parse(event.dateTime.toString()); + String fechaFormateada = DateFormat('d MMMM, y', 'es').format(fecha); + // Extraer la hora + String horaFormateada = DateFormat('HH:mm').format(fecha); + // Determinar el día de la semana + String diaSemana = DateFormat('EEEE', 'es').format(fecha); + return Wrap( + children: [ + ConstrainedBox( + constraints: BoxConstraints( + maxWidth: bigScreen ? widthScreen * 0.5 : widthScreen, + ), + child: Column( + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + child: CachedNetworkImage( + imageUrl: event.banner, + fit: BoxFit.cover, + width: bigScreen ? widthScreen * 0.6 : widthScreen, + ), + ), + const SizedBox(height: 20), + Text( + 'Acerca del evento', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: bigScreen ? 22 : 16, + ), + ), + Text( + event.description, + textAlign: TextAlign.justify, + style: TextStyle(fontSize: bigScreen ? 20 : 14), + ), + const SizedBox(height: 20), + if (event.recommendations != '') + Text( + 'Recomendaciones', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: bigScreen ? 22 : 16, + ), + ), + Text( + event.recommendations, + style: TextStyle(fontSize: bigScreen ? 20 : 14), + ), + ], + ), + const SizedBox( + height: 20, + ), + ], + ), + ), + SizedBox(width: widthScreen * 0.1), + ConstrainedBox( + constraints: BoxConstraints( + maxWidth: bigScreen ? widthScreen * 0.3 : widthScreen, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Card( + child: Column( + children: [ + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 15.0, + vertical: 12.0, + ), + child: Column( + children: [ + EventDetailItemWidget( + iconAsset: AppAssets.calendar, + title: fechaFormateada, + subTitle: '$diaSemana, $horaFormateada', + isBigScreen: bigScreen, + onTap: () { + final Uri uri = Uri.parse(event.calendarUrl); + launchUrl(uri); + }, + isIcon: false, + ), + EventDetailItemWidget( + iconAsset: AppAssets.location, + title: event.locationTitle, + subTitle: event.locationDetails, + isBigScreen: bigScreen, + onTap: () { + final Uri uri = Uri.parse(event.locationUrl); + launchUrl(uri); + }, + isIcon: false, + ), + EventDetailItemWidget( + iconAsset: AppAssets.location, + title: '+${event.attendeesCount} Asistirán', + subTitle: '', + isBigScreen: bigScreen, + onTap: () {}, + isIcon: true, + ), + ], + ), + ), + ], + ), + ), + const SizedBox(height: 20), + ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: + notifier.attending ? AppColors.cancel : AppColors.primary, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(15), + ), + padding: EdgeInsets.symmetric( + vertical: bigScreen ? 20 : 15, + horizontal: 100, + ), + ), + onPressed: () async { + final authNotifier = context.read(); + if (notifier.attending) { + notifier.notAttendEvent(); + return; + } + if (authNotifier.user == null) { + await showDialog( + context: context, + builder: (ctx) => AlertDialog( + title: const Text('Inicia sesión'), + content: const Text( + 'Debes iniciar sesión para poder registrarte al evento', + style: TextStyle( + fontSize: 16, + )), + actions: [ + TextButton( + onPressed: () { + Navigator.pop(ctx); + }, + child: const Text('Ok')) + ], + )); + final logged = await showModalBottomSheet( + context: context, + builder: (ctx) { + late final Widget child; + + child = SignInScreen( + actions: [ + AuthStateChangeAction( + (context, state) { + Navigator.pop(ctx, true); + }), + AuthStateChangeAction( + (context, state) { + Navigator.pop(ctx, true); + }), + ], + ); + + return Scaffold(body: child); + }) ?? + false; + + if (logged) { + notifier.attendEvent(); + } + } else { + notifier.attendEvent(); + } + }, + child: Text( + notifier.attending ? 'Cancelar Asistencia' : 'Asistir', + style: TextStyle( + color: Colors.white, + fontSize: bigScreen ? 26 : 18, + fontWeight: FontWeight.w300, + ), + ), + ), + if (!bigScreen) const SizedBox(height: 20), + ], + ), + ), + ], + ); + } +} diff --git a/lib/src/ui/widgets/event_detail_item_widget.dart b/lib/src/ui/widgets/event_detail_item_widget.dart new file mode 100644 index 0000000..b3917c6 --- /dev/null +++ b/lib/src/ui/widgets/event_detail_item_widget.dart @@ -0,0 +1,74 @@ +import 'package:flutter/material.dart'; + +class EventDetailItemWidget extends StatelessWidget { + final String iconAsset; + final String title; + final String subTitle; + final bool isBigScreen; + final VoidCallback onTap; + final bool isIcon; // todo(davila): do not use this + + const EventDetailItemWidget({ + super.key, + required this.iconAsset, + required this.title, + required this.subTitle, + required this.isBigScreen, + required this.onTap, + required this.isIcon, + }); + + @override + Widget build(BuildContext context) { + return MouseRegion( + cursor: SystemMouseCursors.click, + child: GestureDetector( + onTap: onTap, + child: Row( + children: [ + Container( + padding: const EdgeInsets.all(10), + margin: const EdgeInsets.only( + right: 10, + top: 10, + bottom: 10, + ), + child: isIcon + ? const Icon( + Icons + .people_outline_rounded, // todo(davila): change this logic + size: 30.0, + color: Color.fromRGBO(86, 105, 255, 1), + ) + : Image.asset( + iconAsset, + height: 30, + width: 30, + ), + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: isBigScreen ? 22 : 14, + overflow: TextOverflow.ellipsis, + ), + ), + Text( + subTitle, + style: TextStyle( + fontSize: isBigScreen ? 16 : 10, + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + ], + ), + ), + ); + } +} diff --git a/lib/src/ui/widgets/host_by_widget.dart b/lib/src/ui/widgets/host_by_widget.dart new file mode 100644 index 0000000..1cf8b5f --- /dev/null +++ b/lib/src/ui/widgets/host_by_widget.dart @@ -0,0 +1,34 @@ +import 'package:flutter/material.dart'; + +class HostByWidget extends StatelessWidget { + final List speakers; + final bool bigScreen; + + const HostByWidget({ + super.key, + required this.speakers, + required this.bigScreen, + }); + @override + Widget build(BuildContext context) { + return Text( + speakerName(), + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: bigScreen ? 18 : 12, + ), + ); + } + + String speakerName() { + String speakerName = ''; + for (var speaker in speakers) { + if (speakerName == '') { + speakerName = speaker; + } else { + speakerName = '$speakerName y $speaker'; + } + } + return speakerName; + } +} diff --git a/lib/src/ui/widgets/navbar_event_widget.dart b/lib/src/ui/widgets/navbar_event_widget.dart new file mode 100644 index 0000000..b0c7bd5 --- /dev/null +++ b/lib/src/ui/widgets/navbar_event_widget.dart @@ -0,0 +1,55 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_community_ibague/src/notifiers/event_notifier.dart'; +import 'package:flutter_community_ibague/src/ui/widgets/host_by_widget.dart'; +import 'package:provider/provider.dart'; + +class NavbarEventWidget extends StatelessWidget { + final double widthScreen; + final bool bigScreen; + + const NavbarEventWidget({ + super.key, + required this.widthScreen, + required this.bigScreen, + }); + + @override + Widget build(BuildContext context) { + final notifier = context.watch(); + final event = notifier.event; + return ConstrainedBox( + constraints: BoxConstraints(maxWidth: widthScreen), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + event.title, + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: bigScreen ? 36 : 28, + ), + ), + ListTile( + leading: const CircleAvatar(), + title: Text( + 'Hosted By', + style: TextStyle( + fontWeight: FontWeight.w300, + fontSize: bigScreen ? 18 : 12, + ), + ), + subtitle: HostByWidget( + speakers: event.speakers, + bigScreen: bigScreen, + ), + ), + const SizedBox(height: 25), + ], + ), + ), + ); + } +} From 3560df2da5bd5d0139597d4c6b61a6dfb171f377 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20D=C3=A1vila?= Date: Tue, 12 Mar 2024 21:16:47 -0500 Subject: [PATCH 2/2] Change in list dynamic to list string in event model --- lib/src/models/event.dart | 8 ++++++-- lib/src/ui/widgets/host_by_widget.dart | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/src/models/event.dart b/lib/src/models/event.dart index 3e1dcdd..b7d6dd9 100644 --- a/lib/src/models/event.dart +++ b/lib/src/models/event.dart @@ -13,7 +13,7 @@ class Event { final String calendarUrl; final String locationUrl; final List attendees; - final List speakers; + final List speakers; int get attendeesCount => attendees.length; @@ -45,6 +45,10 @@ class Event { .map((uid) => '$uid') .toList(); + final speakers = (json['speakers'] as List? ?? []) + .map((name) => '$name') + .toList(); + return Event( id: id, title: json['title'] ?? '', @@ -58,7 +62,7 @@ class Event { calendarUrl: json['calendar_url'] ?? '', recommendations: recommendations, attendees: attendees, - speakers: json['speakers'] ?? {}, + speakers: speakers, ); } } diff --git a/lib/src/ui/widgets/host_by_widget.dart b/lib/src/ui/widgets/host_by_widget.dart index 1cf8b5f..2326d95 100644 --- a/lib/src/ui/widgets/host_by_widget.dart +++ b/lib/src/ui/widgets/host_by_widget.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; class HostByWidget extends StatelessWidget { - final List speakers; + final List speakers; final bool bigScreen; const HostByWidget({