diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 45c99ed..e9f7909 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -53,6 +53,7 @@ "goBack": "Go back", "closePositiveSheetAction": "Done", "closeNeutralAction": "Ok", + "closeSaveAction": "Save", "continueLabel": "Continue", "deleteLabel": "Delete", "dismissLabel": "Dismiss", @@ -170,6 +171,8 @@ "taskAction_generateLink_process_creatingURI": "Creating link...", "taskAction_generateLink_shareTextSubject": "Here's the link to see my location", "taskAction_showDetails": "Show Details", + "taskAction_changeName": "Change the Name for your Share", + "taskAction_changeName_field_label": "Name", "tasks_action_stopAll": "Stop tasks", "tasks_action_startAll": "Start tasks", "tasks_examples_weekend": "Weekend Getaway", diff --git a/lib/screens/locations_overview_screen_widgets/TaskChangeNameDialog.dart b/lib/screens/locations_overview_screen_widgets/TaskChangeNameDialog.dart new file mode 100644 index 0000000..31f74df --- /dev/null +++ b/lib/screens/locations_overview_screen_widgets/TaskChangeNameDialog.dart @@ -0,0 +1,117 @@ +import 'package:basic_utils/basic_utils.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:flutter_platform_widgets/flutter_platform_widgets.dart'; +import 'package:locus/utils/theme.dart'; + +class TaskChangeNameDialog extends StatefulWidget { + final String initialName; + final Function(String) onNameChanged; + + const TaskChangeNameDialog({ + required this.initialName, + required this.onNameChanged, + super.key, + }); + + @override + State createState() => _TaskChangeNameDialogState(); +} + +class _TaskChangeNameDialogState extends State { + final formKey = GlobalKey(); + final nameController = TextEditingController(); + final nameFocusNode = FocusNode(); + + late bool showClearButton; + + @override + void initState() { + super.initState(); + + nameController.text = widget.initialName; + nameController.addListener(() { + setState(() { + showClearButton = nameController.text.isNotEmpty; + }); + }); + showClearButton = nameController.text.isNotEmpty; + } + + @override + void dispose() { + nameController.dispose(); + + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context); + + return PlatformAlertDialog( + material: (_, __) => MaterialAlertDialogData( + icon: const Icon(Icons.edit_rounded), + ), + title: Text(l10n.taskAction_changeName), + actions: createCancellableDialogActions( + context, + [ + PlatformDialogAction( + child: Text(l10n.closeSaveAction), + cupertino: (_, __) => CupertinoDialogActionData( + isDefaultAction: true, + ), + material: (_, __) => MaterialDialogActionData( + icon: const Icon(Icons.check_rounded), + ), + onPressed: () { + if (formKey.currentState!.validate()) { + widget.onNameChanged(nameController.text); + } + }, + ) + ], + ), + content: Form( + key: formKey, + child: PlatformTextFormField( + autofillHints: const [AutofillHints.name], + controller: nameController, + autofocus: true, + focusNode: nameFocusNode, + validator: (value) { + if (value == null || value.isEmpty) { + return l10n.fields_errors_isEmpty; + } + + if (!StringUtils.isAscii(value)) { + return l10n.fields_errors_invalidCharacters; + } + + return null; + }, + textInputAction: TextInputAction.done, + keyboardType: TextInputType.name, + maxLines: 1, + material: (_, __) => MaterialTextFormFieldData( + decoration: InputDecoration( + filled: false, + labelText: l10n.taskAction_changeName_field_label, + suffixIcon: showClearButton + ? IconButton( + visualDensity: VisualDensity.compact, + icon: const Icon(Icons.clear), + onPressed: () { + nameController.clear(); + nameFocusNode.requestFocus(); + }, + ) + : null, + ), + ), + ), + ), + ); + } +} diff --git a/lib/screens/locations_overview_screen_widgets/TaskTile.dart b/lib/screens/locations_overview_screen_widgets/TaskTile.dart index 6b674cb..1d74483 100644 --- a/lib/screens/locations_overview_screen_widgets/TaskTile.dart +++ b/lib/screens/locations_overview_screen_widgets/TaskTile.dart @@ -1,10 +1,11 @@ import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_platform_widgets/flutter_platform_widgets.dart' hide PlatformListTile; import 'package:intl/intl.dart'; import 'package:locus/screens/TaskDetailScreen.dart'; +import 'package:locus/screens/locations_overview_screen_widgets/TaskChangeNameDialog.dart'; import 'package:locus/services/task_service/index.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:locus/services/timers_service.dart'; import 'package:locus/utils/date.dart'; import 'package:locus/utils/navigation.dart'; @@ -70,6 +71,23 @@ class _TaskTileState extends State with TaskLinkGenerationMixin { setState(() {}); } + void _showChangeNameDialog() async => showPlatformDialog( + context: context, + builder: (context) => TaskChangeNameDialog( + initialName: widget.task.name, + onNameChanged: (newName) { + final taskService = context.read(); + + widget.task.name = newName; + + taskService.save(); + taskService.update(widget.task); + + Navigator.of(context).pop(); + }, + ), + ); + @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context); @@ -131,6 +149,7 @@ class _TaskTileState extends State with TaskLinkGenerationMixin { : null, ), ), + onTap: _showChangeNameDialog, ); } }