From e569fc4a6ff0dab1ecb0d1ff9bfbd6f6afe7993a Mon Sep 17 00:00:00 2001 From: Giorgio Azzinnaro Date: Mon, 9 Dec 2024 16:55:03 +0100 Subject: [PATCH] feat: new interactive placeholder texts --- lib/app.dart | 3 ++ lib/l10n/app_de.arb | 5 ++- lib/l10n/app_en.arb | 5 ++- lib/l10n/app_es.arb | 5 ++- lib/l10n/app_fr.arb | 5 ++- lib/l10n/app_it.arb | 5 ++- .../containers/schedules_list.dart | 4 +++ lib/presentation/screens/auth.dart | 2 +- lib/presentation/screens/group_details.dart | 2 +- lib/presentation/screens/home.dart | 34 +++++++++++++++---- .../widgets/default_reply_action_sheet.dart | 3 +- lib/presentation/widgets/empty_state.dart | 16 +++------ lib/presentation/widgets/group_events.dart | 17 +++++++++- lib/presentation/widgets/group_members.dart | 20 ++++++++++- lib/presentation/widgets/schedules_list.dart | 26 +++++++++++++- pubspec.lock | 16 +++++++++ pubspec.yaml | 1 + 17 files changed, 140 insertions(+), 29 deletions(-) diff --git a/lib/app.dart b/lib/app.dart index d12b566a..6e59fa45 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -5,6 +5,7 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_redux/flutter_redux.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:google_fonts/google_fonts.dart'; import 'package:parousia/actions/actions.dart'; import 'package:parousia/router.dart'; import 'package:parousia/selectors/selectors.dart'; @@ -61,10 +62,12 @@ class ParApp extends StatelessWidget { locale: vm.locale, darkTheme: ThemeData( colorScheme: darkColorScheme, + fontFamily: GoogleFonts.cabin().fontFamily, ), theme: ThemeData( // TODO(borgoat): dynamic color scheme using dynamic_color package colorScheme: lightColorScheme, + fontFamily: GoogleFonts.cabin().fontFamily, ), routerConfig: router, ); diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index 6fe3577a..a74d7949 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -44,9 +44,11 @@ "eventDatetime": "Datum und Uhrzeit des Events", "eventName": "Name des Events", "events": "Events", + "eventsEmpty": "Es ist Zeit, deiner Gruppe Leben einzuhauchen!
Tippe auf die Schaltfläche , um deine erste Aktivität zu planen.
Wähle ein Datum, setze eine Uhrzeit und füge Details hinzu, um alle begeistert zu machen, zusammenzukommen.", "gallery": "Galerie", "groupDismissAdmin": "Admin entfernen", "groupDismissAdminConfirmation": "Bist du sicher, dass du dieses Mitglied als Admin entfernen möchtest?", + "groupEmptyEvents": "Dein Eventkalender ist leer.
Bereit, etwas Aufregendes zu planen? Tippe auf die Schaltfläche , um ein neues Event zu erstellen und die Gruppenmitglieder einzuladen!", "groupLeave": "Gruppe verlassen", "groupLeaveConfirmation": "Bist du sicher, dass du diese Gruppe verlassen möchtest? Du wirst den Zugriff auf alle Gruppen-Events und Mitglieder verlieren.", "groupMakeAdmin": "Zum Gruppenadmin machen", @@ -61,6 +63,7 @@ "inviteCodeExplanation": "Teile diesen Code mit {user}, um ihn in die Gruppe einzuladen", "inviteMessage": "Hey, tritt meiner Gruppe auf Grup bei! Benutze diesen Code {code} oder klicke hier: {link}", "invites": "Einladungen", + "invitesEmpty": "Es ist Zeit, deine Freunde einzuladen, deinen Gruppen beizutreten!
Tippe auf die Schaltfläche , um deine Freunde einzuladen, deinen Gruppen beizutreten und gemeinsam Events zu organisieren.", "inviteFromContacts": "Aus Kontakten einladen", "inviteManual": "Manuell einladen", "inviteMembersCTA": "Lad' ein paar Freunde ein!", @@ -71,7 +74,7 @@ "members": "Mitglieder", "newGroup": "Neue Gruppe", "no": "Nee", - "onboardingMessage": "Fühlst du dich wie ein einsamer Wolf? Kein Problem! Du kannst entweder eine neue Gruppe erstellen oder einer bestehenden Gruppe beitreten, indem du einen Einladungscode verwendest.", + "onboardingMessage": "Willkommen bei GRUP!
Du hast noch keine Gruppen erstellt. Beginne, indem du auf die Schaltfläche tippst, um deine erste Gruppe zu erstellen und mit Freunden, Familie oder Kollegen Events zu organisieren.", "profile": "Profil", "recurrenceRule": "Wiederholung", "save": "Speichern", diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 086088e8..9cc29dd0 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -44,9 +44,11 @@ "eventDatetime": "Event date and time", "eventName": "Event name", "events": "Events", + "eventsEmpty": "Time to bring your group to life!
Tap the button to schedule your first activity.
Choose a date, set a time, and add details to get everyone excited about coming together.", "gallery": "Gallery", "groupDismissAdmin": "Dismiss as admin", "groupDismissAdminConfirmation": "Are you sure you want to dismiss this member as an admin?", + "groupEmptyEvents": "Your event calendar is empty.
Ready to plan something exciting? Tap the button to create a new event and invite your group members!", "groupLeave": "Leave group", "groupLeaveConfirmation": "Are you sure you want to leave this group? You will lose access to all group events and members.", "groupMakeAdmin": "Make group admin", @@ -91,6 +93,7 @@ } }, "invites": "Invites", + "invitesEmpty": "Your group is waiting for its first member!
Tap the button to add friends, family, or colleagues.", "inviteFromContacts": "Invite from contacts", "inviteManual": "Invite manually", "inviteMembersCTA": "Invite some friends!", @@ -101,7 +104,7 @@ "members": "Members", "newGroup": "New group", "no": "No", - "onboardingMessage": "Feeling like a lone wolf? No problem! You can either create a new group, or join an existing one using an invite code.", + "onboardingMessage": "Welcome to GRUP!
You haven’t created any groups yet. Start by tapping the button to create your first group and begin organizing events with friends, family, or colleagues.", "profile": "Profile", "recurrenceRule": "Recurrence", "save": "Save", diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb index 0400fbf1..489b2661 100644 --- a/lib/l10n/app_es.arb +++ b/lib/l10n/app_es.arb @@ -44,9 +44,11 @@ "eventDatetime": "Fecha y hora del evento", "eventName": "Nombre del evento", "events": "Eventos", + "eventsEmpty": "¡Es hora de dar vida a tu grupo!
Toca el botón para programar tu primera actividad.
Elige una fecha, establece una hora y añade detalles para que todos estén emocionados de reunirse.", "gallery": "Galería", "groupDismissAdmin": "Descarta como admin", "groupDismissAdminConfirmation": "¿Seguro que quieres descartar a este miembro como admin?", + "groupEmptyEvents": "Tu calendario de eventos está vacío.
¿Listo para planear algo emocionante? Toca el botón para crear un nuevo evento e invitar a los miembros del grupo.", "groupLeave": "Abandona el grupo", "groupLeaveConfirmation": "¿Seguro que quieres abandonar este grupo? Perderás el acceso a todos los eventos y miembros del grupo.", "groupMakeAdmin": "Haz admin del grupo", @@ -61,6 +63,7 @@ "inviteCodeExplanation": "Comparte este código con {user} para invitarlo al grupo", "inviteMessage": "¡Ey, únete a mi grupo en Grup! Usa este código {code} o haz clic aquí: {link}", "invites": "Invitaciones", + "invitesEmpty": "¡Es hora de invitar a tus amigos a unirse a tus grupos!
Toca el botón para invitar a tus amigos a unirse a tus grupos y empezar a organizar eventos juntos.", "inviteFromContacts": "Invita desde tus contactos", "inviteManual": "Invita manualmente", "inviteMembersCTA": "¡Invita a algunos amigos!", @@ -71,7 +74,7 @@ "members": "Miembros", "newGroup": "Grupo nuevo", "no": "No", - "onboardingMessage": "¿Te sientes como un lobo solitario? ¡No hay problema! Puedes crear un nuevo grupo o unirte a un grupo existente con un código de invitación.", + "onboardingMessage": "¡Bienvenido a GRUP!
Aún no has creado grupos. Empieza tocando el botón para crear tu primer grupo y empieza a organizar eventos con amigos, familiares o compañeros.", "profile": "Perfil", "recurrenceRule": "Recurrencia", "save": "Guarda", diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index bdd344e1..05054f2b 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -44,9 +44,11 @@ "eventDatetime": "Date et heure de l'événement", "eventName": "Nom de l'événement", "events": "Événements", + "eventsEmpty": "Il est temps de donner vie à ton groupe!
Appuie sur le bouton pour planifier ta première activité.
Choisis une date, définis une heure et ajoute des détails pour que tout le monde soit excité de se retrouver.", "gallery": "Galerie", "groupDismissAdmin": "Révoque l'admin", "groupDismissAdminConfirmation": "Es-tu sûr de vouloir révoquer ce membre comme admin?", + "groupEmptyEvents": "Ton calendrier d'événements est vide.
Prêt à planifier quelque chose d'excitant? Appuie sur le bouton pour créer un nouvel événement et inviter les membres du groupe!", "groupLeave": "Quitte le groupe", "groupLeaveConfirmation": "Es-tu sûr de vouloir quitter ce groupe? Tu perdras l'accès à tous les événements et membres du groupe.", "groupMakeAdmin": "Rend admin du groupe", @@ -61,6 +63,7 @@ "inviteCodeExplanation": "Partage ce code avec {user} pour l'inviter dans le groupe", "inviteMessage": "Hey, rejoins mon groupe sur Grup! Utilise ce code {code} ou clique ici: {link}", "invites": "Invitations", + "invitesEmpty": "Il est temps d'inviter tes amis à rejoindre tes groupes!
Appuie sur le bouton pour inviter tes amis à rejoindre tes groupes et commencer à organiser des événements ensemble.", "inviteFromContacts": "Invite depuis tes contacts", "inviteManual": "Invite manuellement", "inviteMembersCTA": "Invite quelques amis!", @@ -71,7 +74,7 @@ "members": "Membres", "newGroup": "Nouveau groupe", "no": "Non", - "onboardingMessage": "Tu te sens comme un loup solitaire? Pas de problème! Tu peux créer un nouveau groupe ou rejoindre un groupe existant avec un code d'invitation.", + "onboardingMessage": "Bienvenue sur GRUP!
Tu n'as pas encore créé de groupes. Commence en appuyant sur le bouton pour créer ton premier groupe et commence à organiser des événements avec des amis, de la famille ou des collègues.", "profile": "Profil", "recurrenceRule": "Récurrence", "save": "Sauvegarde", diff --git a/lib/l10n/app_it.arb b/lib/l10n/app_it.arb index a02b7943..2ee19925 100644 --- a/lib/l10n/app_it.arb +++ b/lib/l10n/app_it.arb @@ -44,9 +44,11 @@ "eventDatetime": "Data e ora dell'evento", "eventName": "Nome dell'evento", "events": "Eventi", + "eventsEmpty": "È il momento di dare vita al tuo gruppo!
Tocca il pulsante per pianificare la tua prima attività.
Scegli una data, imposta un orario e aggiungi dettagli per far sì che tutti siano entusiasti di riunirsi.", "gallery": "Galleria", "groupDismissAdmin": "Rimuovi come amministratore", "groupDismissAdminConfirmation": "Sei sicuro di voler rimuovere questo membro come amministratore?", + "groupEmptyEvents": "Il tuo calendario eventi è vuoto.
Pronto a pianificare qualcosa di emozionante? Tocca il pulsante per creare un nuovo evento e invitare i membri del gruppo!", "groupLeave": "Lascia il gruppo", "groupLeaveConfirmation": "Sei sicuro di voler lasciare questo gruppo? Perderai l'accesso a tutti gli eventi e ai membri del gruppo.", "groupMakeAdmin": "Rendi amministratore del gruppo", @@ -61,6 +63,7 @@ "inviteCodeExplanation": "Condividi questo codice con {user} per invitarlo nel gruppo", "inviteMessage": "Ehi, unisciti al mio gruppo su Grup! Usa questo codice {code} o clicca qui: {link}", "invites": "Inviti", + "invitesEmpty": "Non hai ancora inviti.
Tocca il pulsante per iniziare a invitare i tuoi amici a unirsi al gruppo.", "inviteFromContacts": "Invita dai tuoi contatti", "inviteManual": "Invita manualmente", "inviteMembersCTA": "Invita qualche amico!", @@ -71,7 +74,7 @@ "members": "Membri", "newGroup": "Nuovo gruppo", "no": "No", - "onboardingMessage": "Ti senti un po' come un lupo solitario? Nessun problema! Puoi creare un nuovo gruppo, o unirti a uno esistente usando un codice di invito.", + "onboardingMessage": "Benvenuto in GRUP!
Non hai ancora creato gruppi. Inizia toccando il pulsante per creare il tuo primo gruppo e inizia a organizzare eventi con amici, familiari o colleghi.", "profile": "Profilo", "recurrenceRule": "Ricorrenza", "save": "Salva", diff --git a/lib/presentation/containers/schedules_list.dart b/lib/presentation/containers/schedules_list.dart index d03637f2..b71e8c8b 100644 --- a/lib/presentation/containers/schedules_list.dart +++ b/lib/presentation/containers/schedules_list.dart @@ -13,8 +13,11 @@ import 'package:redux_entity/redux_entity.dart'; part 'schedules_list.freezed.dart'; class SchedulesListContainer extends StatefulWidget { + final String? groupId; + const SchedulesListContainer({ super.key, + this.groupId, }); @override @@ -46,6 +49,7 @@ class SchedulesListContainerState extends State { }, itemBuilder: (context, index) { return SchedulesList( + groupId: widget.groupId, schedules: vm.schedules, onReplyChanged: vm.onReplyChanged, ); diff --git a/lib/presentation/screens/auth.dart b/lib/presentation/screens/auth.dart index bcd8e379..722f5982 100644 --- a/lib/presentation/screens/auth.dart +++ b/lib/presentation/screens/auth.dart @@ -20,7 +20,7 @@ class AuthScreen extends StatelessWidget { appBar: AppBar( title: Text( l10n.appName.toUpperCase(), - style: GoogleFonts.ranchers( + style: GoogleFonts.sniglet( color: theme.colorScheme.primary, textStyle: theme.textTheme.headlineLarge, ), diff --git a/lib/presentation/screens/group_details.dart b/lib/presentation/screens/group_details.dart index cfca4a0b..7af97d61 100644 --- a/lib/presentation/screens/group_details.dart +++ b/lib/presentation/screens/group_details.dart @@ -35,7 +35,7 @@ class GroupDetailsScreen extends StatelessWidget { padding: const EdgeInsets.only(left: 16.0, right: 16.0), child: Text(groupDescription), ), - Expanded(child: SchedulesListContainer()), + Expanded(child: SchedulesListContainer(groupId: groupIdStr)), ], ), floatingActionButton: isAdmin && groupIdStr != null diff --git a/lib/presentation/screens/home.dart b/lib/presentation/screens/home.dart index 275b8f9a..6e8ac709 100644 --- a/lib/presentation/screens/home.dart +++ b/lib/presentation/screens/home.dart @@ -5,6 +5,7 @@ import 'package:google_fonts/google_fonts.dart'; import 'package:parousia/go_router_builder.dart'; import 'package:parousia/models/models.dart'; import 'package:parousia/presentation/presentation.dart'; +import 'package:styled_text/styled_text.dart'; class HomeScreen extends StatelessWidget { final Profile? profile; @@ -37,8 +38,24 @@ class HomeScreen extends StatelessWidget { final nothingToShow = groups == null || groups!.isEmpty; final innerBody = nothingToShow ? EmptyState( - image: 'assets/images/home.webp', - text: l10n.onboardingMessage, + image: 'home.webp', + text: StyledText( + text: l10n.onboardingMessage, + textAlign: TextAlign.center, + style: theme.textTheme.bodyLarge, + tags: { + 'title': StyledTextTag( + style: GoogleFonts.sniglet(color: theme.colorScheme.primary), + ), + 'plus': StyledTextWidgetTag( + TextButton.icon( + icon: Icon(Icons.group_add_outlined), + label: Text(l10n.createOrJoinGroup), + onPressed: () => _onGroupCreate(context), + ), + ) + }, + ), ) : GroupsList(groups: groups); @@ -46,9 +63,10 @@ class HomeScreen extends StatelessWidget { appBar: AppBar( title: Text( l10n.appName.toUpperCase(), - style: GoogleFonts.ranchers( + style: GoogleFonts.sniglet( color: theme.colorScheme.primary, textStyle: theme.textTheme.headlineLarge, + fontWeight: FontWeight.bold, ), ), bottom: loading @@ -69,12 +87,16 @@ class HomeScreen extends StatelessWidget { child: innerBody, ), floatingActionButton: FloatingActionButton.extended( - onPressed: () => GroupCreateRoute() - .push(context) - .then((value) => value != null ? onGroupCreate?.call(value) : null), + onPressed: () => _onGroupCreate(context), label: Text(l10n.createOrJoinGroup), icon: const Icon(Icons.group_add_outlined), ), ); } + + _onGroupCreate(BuildContext context) { + return GroupCreateRoute() + .push(context) + .then((value) => value != null ? onGroupCreate?.call(value) : null); + } } diff --git a/lib/presentation/widgets/default_reply_action_sheet.dart b/lib/presentation/widgets/default_reply_action_sheet.dart index d1129bf3..4e976625 100644 --- a/lib/presentation/widgets/default_reply_action_sheet.dart +++ b/lib/presentation/widgets/default_reply_action_sheet.dart @@ -1,10 +1,9 @@ -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:parousia/models/models.dart'; import 'package:parousia/presentation/presentation.dart'; import 'package:parousia/util/util.dart'; import 'package:rrule/rrule.dart'; -import 'package:parousia/models/models.dart'; class DefaultReplyActionSheet extends StatelessWidget { final RecurrenceRule? recurrenceRule; diff --git a/lib/presentation/widgets/empty_state.dart b/lib/presentation/widgets/empty_state.dart index 8d4b29ee..d8491977 100644 --- a/lib/presentation/widgets/empty_state.dart +++ b/lib/presentation/widgets/empty_state.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; class EmptyState extends StatelessWidget { - final String? text; + final Widget? text; final String? image; const EmptyState({super.key, this.text, this.image}); @@ -10,21 +10,19 @@ class EmptyState extends StatelessWidget { Widget build(BuildContext context) { final theme = Theme.of(context); + final imageUri = image != null ? 'assets/images/$image' : null; + return Padding( padding: const EdgeInsets.all(16.0), child: Column( children: [ - if (text != null) - Text( - text!, - style: theme.textTheme.bodyLarge, - ), + if (text != null) text!, if (image != null) Expanded( child: Container( decoration: BoxDecoration( image: DecorationImage( - image: AssetImage(image!), + image: AssetImage(imageUri!), invertColors: theme.brightness == Brightness.dark, ), ), @@ -34,8 +32,4 @@ class EmptyState extends StatelessWidget { ), ); } - - // Add a factory constructor to create an empty state with an image. - factory EmptyState.withImage(String image) => - EmptyState(image: 'assets/images/$image'); } diff --git a/lib/presentation/widgets/group_events.dart b/lib/presentation/widgets/group_events.dart index 6bdf4c07..bad224cb 100644 --- a/lib/presentation/widgets/group_events.dart +++ b/lib/presentation/widgets/group_events.dart @@ -4,6 +4,7 @@ import 'package:parousia/go_router_builder.dart'; import 'package:parousia/models/models.dart'; import 'package:parousia/presentation/presentation.dart'; import 'package:rrule/rrule.dart'; +import 'package:styled_text/styled_text.dart'; class GroupEvents extends StatelessWidget { final Group? group; // TODO is this needed? @@ -24,6 +25,7 @@ class GroupEvents extends StatelessWidget { @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context)!; + final theme = Theme.of(context); final topWidget = schedules?.isNotEmpty ?? false ? ListView.builder( @@ -49,7 +51,20 @@ class GroupEvents extends StatelessWidget { ); }, ) - : EmptyState.withImage('events.webp'); + : EmptyState( + image: 'events.webp', + text: StyledText( + text: l10n.eventsEmpty, + textAlign: TextAlign.center, + style: theme.textTheme.bodyLarge, + tags: { + 'newevent': StyledTextWidgetTag(TextButton( + onPressed: () => _createNewEvent(context), + child: Text(l10n.createNewEvent), + )) + }, + ), + ); return Padding( padding: const EdgeInsets.symmetric(vertical: 16.0), diff --git a/lib/presentation/widgets/group_members.dart b/lib/presentation/widgets/group_members.dart index 86d4bf7d..a6ff8c38 100644 --- a/lib/presentation/widgets/group_members.dart +++ b/lib/presentation/widgets/group_members.dart @@ -6,6 +6,7 @@ import 'package:parousia/go_router_builder.dart'; import 'package:parousia/models/models.dart'; import 'package:parousia/presentation/presentation.dart'; import 'package:parousia/util/util.dart'; +import 'package:styled_text/styled_text.dart'; import 'contact_form.dart'; @@ -24,6 +25,7 @@ class GroupMembers extends StatelessWidget { @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context)!; + final theme = Theme.of(context); final topWidget = members?.isNotEmpty ?? false ? ListView.builder( @@ -52,7 +54,23 @@ class GroupMembers extends StatelessWidget { }); }, ) - : EmptyState.withImage('add-members.webp'); + // TODO This is actually never shown, because the members list has at least the current user... + // Refactor this so that the current user is shown differently, + // and the empty state is shown when there are no members other than the current user. + : EmptyState( + image: 'add-members.webp', + text: StyledText( + text: l10n.invitesEmpty, + textAlign: TextAlign.center, + style: theme.textTheme.bodyLarge, + tags: { + 'invite': StyledTextWidgetTag(TextButton( + onPressed: () => _showInviteModal(context), + child: Text(l10n.inviteMembersCTA), + )), + }, + ), + ); return Padding( padding: const EdgeInsets.symmetric(vertical: 16.0), diff --git a/lib/presentation/widgets/schedules_list.dart b/lib/presentation/widgets/schedules_list.dart index 1077d711..0051580b 100644 --- a/lib/presentation/widgets/schedules_list.dart +++ b/lib/presentation/widgets/schedules_list.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:parousia/go_router_builder.dart'; import 'package:parousia/models/models.dart'; import 'package:parousia/presentation/presentation.dart'; +import 'package:styled_text/styled_text.dart'; typedef OnReplyChangedCallback = void Function( ScheduleInstanceSummary, ReplyOptions?); @@ -9,15 +11,20 @@ typedef OnReplyChangedCallback = void Function( class SchedulesList extends StatelessWidget { final Iterable? schedules; final OnReplyChangedCallback? onReplyChanged; + final String? groupId; const SchedulesList({ super.key, this.schedules, this.onReplyChanged, + this.groupId, }); @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final theme = Theme.of(context); + if (schedules == null) { return const Center(child: CircularProgressIndicator.adaptive()); } @@ -35,6 +42,23 @@ class SchedulesList extends StatelessWidget { .push(context), ), ) - : EmptyState.withImage('add-events.webp'); + : EmptyState( + image: 'add-events.webp', + text: StyledText( + text: l10n.groupEmptyEvents, + textAlign: TextAlign.center, + style: theme.textTheme.bodyLarge, + tags: { + 'manage': StyledTextWidgetTag( + TextButton.icon( + icon: const Icon(Icons.edit), + label: Text(l10n.groupManage), + onPressed: () => + GroupManageRoute(groupId: groupId!).push(context), + ), + ) + }, + ), + ); } } diff --git a/pubspec.lock b/pubspec.lock index 04819d8a..fbb1bf75 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1438,6 +1438,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" + styled_text: + dependency: "direct main" + description: + name: styled_text + sha256: fd624172cf629751b4f171dd0ecf9acf02a06df3f8a81bb56c0caa4f1df706c3 + url: "https://pub.dev" + source: hosted + version: "8.1.0" supabase: dependency: "direct main" description: @@ -1759,6 +1767,14 @@ packages: url: "https://pub.dev" source: hosted version: "6.5.0" + xmlstream: + dependency: transitive + description: + name: xmlstream + sha256: cfc14e3f256997897df9481ae630d94c2d85ada5187ebeb868bb1aabc2c977b4 + url: "https://pub.dev" + source: hosted + version: "1.1.1" yaml: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index c66b4961..b098dda9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -94,6 +94,7 @@ dependencies: universal_html: ^2.2.4 uuid: ^4.1.0 share_plus: ^10.1.2 + styled_text: ^8.1.0 dev_dependencies: