Changed add link design
This commit is contained in:
@@ -4,9 +4,9 @@
|
|||||||
/// To regenerate, run: `dart run slang`
|
/// To regenerate, run: `dart run slang`
|
||||||
///
|
///
|
||||||
/// Locales: 2
|
/// Locales: 2
|
||||||
/// Strings: 162 (81 per locale)
|
/// Strings: 182 (91 per locale)
|
||||||
///
|
///
|
||||||
/// Built on 2024-02-23 at 17:01 UTC
|
/// Built on 2024-02-23 at 18:23 UTC
|
||||||
|
|
||||||
// coverage:ignore-file
|
// coverage:ignore-file
|
||||||
// ignore_for_file: type=lint
|
// ignore_for_file: type=lint
|
||||||
@@ -170,6 +170,7 @@ class _StringsGenericEn {
|
|||||||
String get save => 'Save';
|
String get save => 'Save';
|
||||||
String get close => 'Close';
|
String get close => 'Close';
|
||||||
String get error => 'Error';
|
String get error => 'Error';
|
||||||
|
String get optional => 'Optional';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Path: onboarding
|
// Path: onboarding
|
||||||
@@ -297,6 +298,13 @@ class _StringsLinksAddLinkEn {
|
|||||||
|
|
||||||
// Translations
|
// Translations
|
||||||
String get addLink => 'Add new link';
|
String get addLink => 'Add new link';
|
||||||
|
String get bookmarkUrl => 'Bookmark URL';
|
||||||
|
String get bookmarkDetails => 'Bookmark details';
|
||||||
|
String get validateUrl => 'Validate URL';
|
||||||
|
String get urlValid => 'The URL is valid';
|
||||||
|
String get urlNotValid => 'The URL is not valid';
|
||||||
|
String get cannotCheckUrl => 'The URL couldn\'t be checked';
|
||||||
|
String get invalidUrl => 'Invalid URL';
|
||||||
String get url => 'URL';
|
String get url => 'URL';
|
||||||
String get checkingUrl => 'Checking URL...';
|
String get checkingUrl => 'Checking URL...';
|
||||||
String get title => 'Title';
|
String get title => 'Title';
|
||||||
@@ -304,6 +312,8 @@ class _StringsLinksAddLinkEn {
|
|||||||
String get markAsUnread => 'Mark as unread';
|
String get markAsUnread => 'Mark as unread';
|
||||||
String get notes => 'Notes';
|
String get notes => 'Notes';
|
||||||
String get addNotes => 'Add notes';
|
String get addNotes => 'Add notes';
|
||||||
|
String get tags => 'Tags';
|
||||||
|
String get others => 'Others';
|
||||||
String get savingLink => 'Saving link...';
|
String get savingLink => 'Saving link...';
|
||||||
String get errorSavingLink => 'An error occured when saving the new link.';
|
String get errorSavingLink => 'An error occured when saving the new link.';
|
||||||
String get leaveEmptyUseWebsiteTitle => 'Leave empty to use the website\'s title.';
|
String get leaveEmptyUseWebsiteTitle => 'Leave empty to use the website\'s title.';
|
||||||
@@ -397,6 +407,7 @@ class _StringsGenericEs implements _StringsGenericEn {
|
|||||||
@override String get save => 'Guardar';
|
@override String get save => 'Guardar';
|
||||||
@override String get close => 'Cerrar';
|
@override String get close => 'Cerrar';
|
||||||
@override String get error => 'Error';
|
@override String get error => 'Error';
|
||||||
|
@override String get optional => 'Opcional';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Path: onboarding
|
// Path: onboarding
|
||||||
@@ -525,12 +536,21 @@ class _StringsLinksAddLinkEs implements _StringsLinksAddLinkEn {
|
|||||||
// Translations
|
// Translations
|
||||||
@override String get addLink => 'Añadir nuevo enlace';
|
@override String get addLink => 'Añadir nuevo enlace';
|
||||||
@override String get url => 'URL';
|
@override String get url => 'URL';
|
||||||
|
@override String get bookmarkUrl => 'URL del marcador';
|
||||||
|
@override String get bookmarkDetails => 'Detalles del marcador';
|
||||||
|
@override String get validateUrl => 'Validar URL';
|
||||||
|
@override String get urlValid => 'La URL es válida';
|
||||||
|
@override String get urlNotValid => 'La URL no es válida';
|
||||||
|
@override String get cannotCheckUrl => 'No se pudo comprobar la URL';
|
||||||
|
@override String get invalidUrl => 'URL no válida';
|
||||||
@override String get checkingUrl => 'Comprobando URL...';
|
@override String get checkingUrl => 'Comprobando URL...';
|
||||||
@override String get title => 'Title';
|
@override String get title => 'Title';
|
||||||
@override String get description => 'Description';
|
@override String get description => 'Description';
|
||||||
@override String get markAsUnread => 'Marcar como no leído';
|
@override String get markAsUnread => 'Marcar como no leído';
|
||||||
@override String get notes => 'Notas';
|
@override String get notes => 'Notas';
|
||||||
@override String get addNotes => 'Añadir notas';
|
@override String get addNotes => 'Añadir notas';
|
||||||
|
@override String get tags => 'Etiquetas';
|
||||||
|
@override String get others => 'Otros';
|
||||||
@override String get savingLink => 'Guardando enlace...';
|
@override String get savingLink => 'Guardando enlace...';
|
||||||
@override String get errorSavingLink => 'Ocurrió un error al guardar el nuevo enlace.';
|
@override String get errorSavingLink => 'Ocurrió un error al guardar el nuevo enlace.';
|
||||||
@override String get leaveEmptyUseWebsiteTitle => 'Dejar vacío para usar el título del sitio web.';
|
@override String get leaveEmptyUseWebsiteTitle => 'Dejar vacío para usar el título del sitio web.';
|
||||||
@@ -590,6 +610,7 @@ extension on Translations {
|
|||||||
case 'generic.save': return 'Save';
|
case 'generic.save': return 'Save';
|
||||||
case 'generic.close': return 'Close';
|
case 'generic.close': return 'Close';
|
||||||
case 'generic.error': return 'Error';
|
case 'generic.error': return 'Error';
|
||||||
|
case 'generic.optional': return 'Optional';
|
||||||
case 'onboarding.title': return 'Welcome to Linkdy';
|
case 'onboarding.title': return 'Welcome to Linkdy';
|
||||||
case 'onboarding.subtitle': return 'An application to manage your bookmarks.';
|
case 'onboarding.subtitle': return 'An application to manage your bookmarks.';
|
||||||
case 'onboarding.start': return 'Start';
|
case 'onboarding.start': return 'Start';
|
||||||
@@ -615,6 +636,13 @@ extension on Translations {
|
|||||||
case 'links.dates.todayAt': return ({required Object time}) => 'Today, ${time}';
|
case 'links.dates.todayAt': return ({required Object time}) => 'Today, ${time}';
|
||||||
case 'links.dates.yesterdayAt': return ({required Object time}) => 'Yesterday, ${time}';
|
case 'links.dates.yesterdayAt': return ({required Object time}) => 'Yesterday, ${time}';
|
||||||
case 'links.addLink.addLink': return 'Add new link';
|
case 'links.addLink.addLink': return 'Add new link';
|
||||||
|
case 'links.addLink.bookmarkUrl': return 'Bookmark URL';
|
||||||
|
case 'links.addLink.bookmarkDetails': return 'Bookmark details';
|
||||||
|
case 'links.addLink.validateUrl': return 'Validate URL';
|
||||||
|
case 'links.addLink.urlValid': return 'The URL is valid';
|
||||||
|
case 'links.addLink.urlNotValid': return 'The URL is not valid';
|
||||||
|
case 'links.addLink.cannotCheckUrl': return 'The URL couldn\'t be checked';
|
||||||
|
case 'links.addLink.invalidUrl': return 'Invalid URL';
|
||||||
case 'links.addLink.url': return 'URL';
|
case 'links.addLink.url': return 'URL';
|
||||||
case 'links.addLink.checkingUrl': return 'Checking URL...';
|
case 'links.addLink.checkingUrl': return 'Checking URL...';
|
||||||
case 'links.addLink.title': return 'Title';
|
case 'links.addLink.title': return 'Title';
|
||||||
@@ -622,6 +650,8 @@ extension on Translations {
|
|||||||
case 'links.addLink.markAsUnread': return 'Mark as unread';
|
case 'links.addLink.markAsUnread': return 'Mark as unread';
|
||||||
case 'links.addLink.notes': return 'Notes';
|
case 'links.addLink.notes': return 'Notes';
|
||||||
case 'links.addLink.addNotes': return 'Add notes';
|
case 'links.addLink.addNotes': return 'Add notes';
|
||||||
|
case 'links.addLink.tags': return 'Tags';
|
||||||
|
case 'links.addLink.others': return 'Others';
|
||||||
case 'links.addLink.savingLink': return 'Saving link...';
|
case 'links.addLink.savingLink': return 'Saving link...';
|
||||||
case 'links.addLink.errorSavingLink': return 'An error occured when saving the new link.';
|
case 'links.addLink.errorSavingLink': return 'An error occured when saving the new link.';
|
||||||
case 'links.addLink.leaveEmptyUseWebsiteTitle': return 'Leave empty to use the website\'s title.';
|
case 'links.addLink.leaveEmptyUseWebsiteTitle': return 'Leave empty to use the website\'s title.';
|
||||||
@@ -679,6 +709,7 @@ extension on _StringsEs {
|
|||||||
case 'generic.save': return 'Guardar';
|
case 'generic.save': return 'Guardar';
|
||||||
case 'generic.close': return 'Cerrar';
|
case 'generic.close': return 'Cerrar';
|
||||||
case 'generic.error': return 'Error';
|
case 'generic.error': return 'Error';
|
||||||
|
case 'generic.optional': return 'Opcional';
|
||||||
case 'onboarding.title': return 'Bienvenido a Linkdy';
|
case 'onboarding.title': return 'Bienvenido a Linkdy';
|
||||||
case 'onboarding.subtitle': return 'Una aplicación para gestionar tus enlaces favoritos.';
|
case 'onboarding.subtitle': return 'Una aplicación para gestionar tus enlaces favoritos.';
|
||||||
case 'onboarding.start': return 'Comenzar';
|
case 'onboarding.start': return 'Comenzar';
|
||||||
@@ -705,12 +736,21 @@ extension on _StringsEs {
|
|||||||
case 'links.dates.yesterdayAt': return ({required Object time}) => 'Ayer, ${time}';
|
case 'links.dates.yesterdayAt': return ({required Object time}) => 'Ayer, ${time}';
|
||||||
case 'links.addLink.addLink': return 'Añadir nuevo enlace';
|
case 'links.addLink.addLink': return 'Añadir nuevo enlace';
|
||||||
case 'links.addLink.url': return 'URL';
|
case 'links.addLink.url': return 'URL';
|
||||||
|
case 'links.addLink.bookmarkUrl': return 'URL del marcador';
|
||||||
|
case 'links.addLink.bookmarkDetails': return 'Detalles del marcador';
|
||||||
|
case 'links.addLink.validateUrl': return 'Validar URL';
|
||||||
|
case 'links.addLink.urlValid': return 'La URL es válida';
|
||||||
|
case 'links.addLink.urlNotValid': return 'La URL no es válida';
|
||||||
|
case 'links.addLink.cannotCheckUrl': return 'No se pudo comprobar la URL';
|
||||||
|
case 'links.addLink.invalidUrl': return 'URL no válida';
|
||||||
case 'links.addLink.checkingUrl': return 'Comprobando URL...';
|
case 'links.addLink.checkingUrl': return 'Comprobando URL...';
|
||||||
case 'links.addLink.title': return 'Title';
|
case 'links.addLink.title': return 'Title';
|
||||||
case 'links.addLink.description': return 'Description';
|
case 'links.addLink.description': return 'Description';
|
||||||
case 'links.addLink.markAsUnread': return 'Marcar como no leído';
|
case 'links.addLink.markAsUnread': return 'Marcar como no leído';
|
||||||
case 'links.addLink.notes': return 'Notas';
|
case 'links.addLink.notes': return 'Notas';
|
||||||
case 'links.addLink.addNotes': return 'Añadir notas';
|
case 'links.addLink.addNotes': return 'Añadir notas';
|
||||||
|
case 'links.addLink.tags': return 'Etiquetas';
|
||||||
|
case 'links.addLink.others': return 'Otros';
|
||||||
case 'links.addLink.savingLink': return 'Guardando enlace...';
|
case 'links.addLink.savingLink': return 'Guardando enlace...';
|
||||||
case 'links.addLink.errorSavingLink': return 'Ocurrió un error al guardar el nuevo enlace.';
|
case 'links.addLink.errorSavingLink': return 'Ocurrió un error al guardar el nuevo enlace.';
|
||||||
case 'links.addLink.leaveEmptyUseWebsiteTitle': return 'Dejar vacío para usar el título del sitio web.';
|
case 'links.addLink.leaveEmptyUseWebsiteTitle': return 'Dejar vacío para usar el título del sitio web.';
|
||||||
|
|||||||
@@ -5,7 +5,8 @@
|
|||||||
"next": "Next",
|
"next": "Next",
|
||||||
"save": "Save",
|
"save": "Save",
|
||||||
"close": "Close",
|
"close": "Close",
|
||||||
"error": "Error"
|
"error": "Error",
|
||||||
|
"optional": "Optional"
|
||||||
},
|
},
|
||||||
"onboarding": {
|
"onboarding": {
|
||||||
"title": "Welcome to Linkdy",
|
"title": "Welcome to Linkdy",
|
||||||
@@ -38,6 +39,13 @@
|
|||||||
},
|
},
|
||||||
"addLink": {
|
"addLink": {
|
||||||
"addLink": "Add new link",
|
"addLink": "Add new link",
|
||||||
|
"bookmarkUrl": "Bookmark URL",
|
||||||
|
"bookmarkDetails": "Bookmark details",
|
||||||
|
"validateUrl": "Validate URL",
|
||||||
|
"urlValid": "The URL is valid",
|
||||||
|
"urlNotValid": "The URL is not valid",
|
||||||
|
"cannotCheckUrl": "The URL couldn't be checked",
|
||||||
|
"invalidUrl": "Invalid URL",
|
||||||
"url": "URL",
|
"url": "URL",
|
||||||
"checkingUrl": "Checking URL...",
|
"checkingUrl": "Checking URL...",
|
||||||
"title": "Title",
|
"title": "Title",
|
||||||
@@ -45,6 +53,8 @@
|
|||||||
"markAsUnread": "Mark as unread",
|
"markAsUnread": "Mark as unread",
|
||||||
"notes": "Notes",
|
"notes": "Notes",
|
||||||
"addNotes": "Add notes",
|
"addNotes": "Add notes",
|
||||||
|
"tags": "Tags",
|
||||||
|
"others": "Others",
|
||||||
"savingLink": "Saving link...",
|
"savingLink": "Saving link...",
|
||||||
"errorSavingLink": "An error occured when saving the new link.",
|
"errorSavingLink": "An error occured when saving the new link.",
|
||||||
"leaveEmptyUseWebsiteTitle": "Leave empty to use the website's title.",
|
"leaveEmptyUseWebsiteTitle": "Leave empty to use the website's title.",
|
||||||
|
|||||||
@@ -5,7 +5,8 @@
|
|||||||
"next": "Siguiente",
|
"next": "Siguiente",
|
||||||
"save": "Guardar",
|
"save": "Guardar",
|
||||||
"close": "Cerrar",
|
"close": "Cerrar",
|
||||||
"error": "Error"
|
"error": "Error",
|
||||||
|
"optional": "Opcional"
|
||||||
},
|
},
|
||||||
"onboarding": {
|
"onboarding": {
|
||||||
"title": "Bienvenido a Linkdy",
|
"title": "Bienvenido a Linkdy",
|
||||||
@@ -39,12 +40,21 @@
|
|||||||
"addLink": {
|
"addLink": {
|
||||||
"addLink": "Añadir nuevo enlace",
|
"addLink": "Añadir nuevo enlace",
|
||||||
"url": "URL",
|
"url": "URL",
|
||||||
|
"bookmarkUrl": "URL del marcador",
|
||||||
|
"bookmarkDetails": "Detalles del marcador",
|
||||||
|
"validateUrl": "Validar URL",
|
||||||
|
"urlValid": "La URL es válida",
|
||||||
|
"urlNotValid": "La URL no es válida",
|
||||||
|
"cannotCheckUrl": "No se pudo comprobar la URL",
|
||||||
|
"invalidUrl": "URL no válida",
|
||||||
"checkingUrl": "Comprobando URL...",
|
"checkingUrl": "Comprobando URL...",
|
||||||
"title": "Title",
|
"title": "Title",
|
||||||
"description": "Description",
|
"description": "Description",
|
||||||
"markAsUnread": "Marcar como no leído",
|
"markAsUnread": "Marcar como no leído",
|
||||||
"notes": "Notas",
|
"notes": "Notas",
|
||||||
"addNotes": "Añadir notas",
|
"addNotes": "Añadir notas",
|
||||||
|
"tags": "Etiquetas",
|
||||||
|
"others": "Otros",
|
||||||
"savingLink": "Guardando enlace...",
|
"savingLink": "Guardando enlace...",
|
||||||
"errorSavingLink": "Ocurrió un error al guardar el nuevo enlace.",
|
"errorSavingLink": "Ocurrió un error al guardar el nuevo enlace.",
|
||||||
"leaveEmptyUseWebsiteTitle": "Dejar vacío para usar el título del sitio web.",
|
"leaveEmptyUseWebsiteTitle": "Dejar vacío para usar el título del sitio web.",
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import 'package:expandable/expandable.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:linkdy/constants/enums.dart';
|
import 'package:linkdy/constants/enums.dart';
|
||||||
@@ -12,8 +11,8 @@ class AddLinkModel {
|
|||||||
final TextEditingController titleController;
|
final TextEditingController titleController;
|
||||||
final TextEditingController descriptionController;
|
final TextEditingController descriptionController;
|
||||||
bool markAsUnread;
|
bool markAsUnread;
|
||||||
|
final List<String> tags;
|
||||||
final TextEditingController notesController;
|
final TextEditingController notesController;
|
||||||
final ExpandableController expandableController;
|
|
||||||
|
|
||||||
AddLinkModel({
|
AddLinkModel({
|
||||||
required this.urlController,
|
required this.urlController,
|
||||||
@@ -23,7 +22,7 @@ class AddLinkModel {
|
|||||||
required this.titleController,
|
required this.titleController,
|
||||||
required this.descriptionController,
|
required this.descriptionController,
|
||||||
this.markAsUnread = false,
|
this.markAsUnread = false,
|
||||||
|
required this.tags,
|
||||||
required this.notesController,
|
required this.notesController,
|
||||||
required this.expandableController,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
import 'package:expandable/expandable.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:linkdy/utils/snackbar.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
|
|
||||||
import 'package:linkdy/screens/links/ui/error_save_link.dart';
|
|
||||||
import 'package:linkdy/screens/links/provider/links.provider.dart';
|
import 'package:linkdy/screens/links/provider/links.provider.dart';
|
||||||
import 'package:linkdy/screens/links/model/add_link.model.dart';
|
import 'package:linkdy/screens/links/model/add_link.model.dart';
|
||||||
|
|
||||||
import 'package:linkdy/i18n/strings.g.dart';
|
import 'package:linkdy/i18n/strings.g.dart';
|
||||||
import 'package:linkdy/router/routes.dart';
|
|
||||||
import 'package:linkdy/utils/process_modal.dart';
|
import 'package:linkdy/utils/process_modal.dart';
|
||||||
import 'package:linkdy/models/data/post_bookmark.dart';
|
import 'package:linkdy/models/data/post_bookmark.dart';
|
||||||
import 'package:linkdy/providers/router_provider.dart';
|
import 'package:linkdy/providers/router_provider.dart';
|
||||||
@@ -40,8 +38,8 @@ class AddLink extends _$AddLink {
|
|||||||
urlController: TextEditingController(),
|
urlController: TextEditingController(),
|
||||||
titleController: TextEditingController(),
|
titleController: TextEditingController(),
|
||||||
descriptionController: TextEditingController(),
|
descriptionController: TextEditingController(),
|
||||||
|
tags: [],
|
||||||
notesController: TextEditingController(),
|
notesController: TextEditingController(),
|
||||||
expandableController: ExpandableController(),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,7 +50,7 @@ class AddLink extends _$AddLink {
|
|||||||
state.urlError = null;
|
state.urlError = null;
|
||||||
ref.notifyListeners();
|
ref.notifyListeners();
|
||||||
} else {
|
} else {
|
||||||
state.urlError = "Invalid URL";
|
state.urlError = t.links.addLink.invalidUrl;
|
||||||
ref.notifyListeners();
|
ref.notifyListeners();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -102,9 +100,9 @@ class AddLink extends _$AddLink {
|
|||||||
ref.invalidate(linksRequestProvider);
|
ref.invalidate(linksRequestProvider);
|
||||||
ref.watch(routerProvider).pop();
|
ref.watch(routerProvider).pop();
|
||||||
} else {
|
} else {
|
||||||
showDialog(
|
showSnacbkar(
|
||||||
context: rootNavigatorKey.currentContext!,
|
label: t.links.addLink.errorSavingLink,
|
||||||
builder: (context) => const ErrorSaveLink(),
|
color: Colors.red,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,77 +1,67 @@
|
|||||||
import 'package:expandable/expandable.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
import 'package:linkdy/screens/links/provider/add_link.provider.dart';
|
import 'package:linkdy/screens/links/provider/add_link.provider.dart';
|
||||||
|
import 'package:linkdy/widgets/autocomplete_text_field.dart';
|
||||||
|
import 'package:linkdy/widgets/section_label.dart';
|
||||||
|
|
||||||
import 'package:linkdy/constants/enums.dart';
|
|
||||||
import 'package:linkdy/i18n/strings.g.dart';
|
import 'package:linkdy/i18n/strings.g.dart';
|
||||||
|
import 'package:linkdy/constants/enums.dart';
|
||||||
|
|
||||||
class AddLinkModal extends ConsumerStatefulWidget {
|
class AddLinkModal extends ConsumerWidget {
|
||||||
const AddLinkModal({Key? key}) : super(key: key);
|
final bool fullscreen;
|
||||||
|
|
||||||
|
const AddLinkModal({
|
||||||
|
Key? key,
|
||||||
|
required this.fullscreen,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
AddLinkModalState createState() => AddLinkModalState();
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
}
|
return Dialog.fullscreen(
|
||||||
|
child: Scaffold(
|
||||||
class AddLinkModalState extends ConsumerState<AddLinkModal> with SingleTickerProviderStateMixin {
|
appBar: AppBar(
|
||||||
late AnimationController _animationController;
|
leading: CloseButton(
|
||||||
late Animation<double> _animation;
|
onPressed: () => Navigator.pop(context),
|
||||||
|
),
|
||||||
@override
|
title: Text(t.links.addLink.addLink),
|
||||||
void initState() {
|
actions: [
|
||||||
_animationController = AnimationController(
|
IconButton(
|
||||||
duration: const Duration(milliseconds: 200),
|
onPressed: ref.watch(addLinkProvider).checkBookmark != null
|
||||||
vsync: this,
|
? () => ref.read(addLinkProvider.notifier).addBookmark()
|
||||||
);
|
: null,
|
||||||
_animation = Tween(
|
icon: const Icon(Icons.save_rounded),
|
||||||
begin: 0.0,
|
tooltip: t.generic.save,
|
||||||
end: 0.5,
|
),
|
||||||
).animate(
|
const SizedBox(width: 8),
|
||||||
CurvedAnimation(
|
],
|
||||||
parent: _animationController,
|
),
|
||||||
curve: Curves.easeInOut,
|
body: const _ModalContent(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
}
|
||||||
super.initState();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _ModalContent extends ConsumerWidget {
|
||||||
|
const _ModalContent({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final provider = ref.watch(addLinkProvider);
|
final provider = ref.watch(addLinkProvider);
|
||||||
|
|
||||||
return Padding(
|
return ListView(
|
||||||
padding: MediaQuery.of(context).viewInsets,
|
|
||||||
child: Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
borderRadius: const BorderRadius.only(topLeft: Radius.circular(28), topRight: Radius.circular(28)),
|
|
||||||
color: Theme.of(context).dialogBackgroundColor,
|
|
||||||
),
|
|
||||||
child: SafeArea(
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
children: [
|
||||||
Flexible(
|
|
||||||
child: SingleChildScrollView(
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
const SizedBox(height: 24),
|
|
||||||
Icon(
|
|
||||||
Icons.add_rounded,
|
|
||||||
color: Theme.of(context).colorScheme.secondary,
|
|
||||||
size: 24,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Text(
|
SectionLabel(
|
||||||
t.links.addLink.addLink,
|
label: t.links.addLink.bookmarkUrl,
|
||||||
textAlign: TextAlign.center,
|
padding: const EdgeInsets.symmetric(
|
||||||
style: const TextStyle(fontSize: 24),
|
horizontal: 16,
|
||||||
|
vertical: 0,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 24),
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
controller: provider.urlController,
|
controller: provider.urlController,
|
||||||
onChanged: ref.read(addLinkProvider.notifier).validateUrl,
|
onChanged: ref.read(addLinkProvider.notifier).validateUrl,
|
||||||
@@ -88,15 +78,36 @@ class AddLinkModalState extends ConsumerState<AddLinkModal> with SingleTickerPro
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (provider.checkBookmarkLoadStatus == LoadStatus.loading)
|
const SizedBox(height: 16),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(top: 16),
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
child: Center(
|
||||||
|
child: ElevatedButton(
|
||||||
|
onPressed: provider.checkBookmarkLoadStatus == null &&
|
||||||
|
provider.urlError == null &&
|
||||||
|
provider.urlController.text != ""
|
||||||
|
? () => ref.read(addLinkProvider.notifier).checkUrlDetails()
|
||||||
|
: null,
|
||||||
|
style: ButtonStyle(
|
||||||
|
foregroundColor: provider.checkBookmarkLoadStatus == LoadStatus.loaded
|
||||||
|
? const MaterialStatePropertyAll(Colors.green)
|
||||||
|
: provider.checkBookmarkLoadStatus == LoadStatus.loaded
|
||||||
|
? const MaterialStatePropertyAll(Colors.red)
|
||||||
|
: null,
|
||||||
|
backgroundColor: provider.checkBookmarkLoadStatus == LoadStatus.loaded
|
||||||
|
? MaterialStatePropertyAll(Colors.green.withOpacity(0.15))
|
||||||
|
: provider.checkBookmarkLoadStatus == LoadStatus.loaded
|
||||||
|
? MaterialStatePropertyAll(Colors.red.withOpacity(0.15))
|
||||||
|
: null,
|
||||||
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
|
if (provider.checkBookmarkLoadStatus == null) Text(t.links.addLink.validateUrl),
|
||||||
|
if (provider.checkBookmarkLoadStatus == LoadStatus.loading) ...[
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 12,
|
|
||||||
width: 12,
|
width: 12,
|
||||||
|
height: 12,
|
||||||
child: CircularProgressIndicator(
|
child: CircularProgressIndicator(
|
||||||
strokeWidth: 2,
|
strokeWidth: 2,
|
||||||
),
|
),
|
||||||
@@ -104,19 +115,32 @@ class AddLinkModalState extends ConsumerState<AddLinkModal> with SingleTickerPro
|
|||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Text(t.links.addLink.checkingUrl),
|
Text(t.links.addLink.checkingUrl),
|
||||||
],
|
],
|
||||||
|
if (provider.checkBookmarkLoadStatus == LoadStatus.loaded) ...[
|
||||||
|
const Icon(Icons.check_circle_rounded),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Text(t.links.addLink.urlValid),
|
||||||
|
],
|
||||||
|
if (provider.checkBookmarkLoadStatus == LoadStatus.error) ...[
|
||||||
|
const Icon(Icons.error_rounded),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Text(t.links.addLink.cannotCheckUrl),
|
||||||
|
],
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (provider.checkBookmark != null) ...[
|
),
|
||||||
const Padding(
|
),
|
||||||
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 16),
|
SectionLabel(
|
||||||
child: Divider(),
|
label: t.links.addLink.bookmarkDetails,
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 16,
|
||||||
|
vertical: 24,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 24),
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
controller: provider.titleController,
|
controller: provider.titleController,
|
||||||
// onChanged: ref.read(addLinkProvider.notifier).validateUrl,
|
|
||||||
// enabled: provider.checkBookmarkLoadStatus != LoadStatus.loading,
|
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
prefixIcon: const Icon(Icons.title_rounded),
|
prefixIcon: const Icon(Icons.title_rounded),
|
||||||
border: const OutlineInputBorder(
|
border: const OutlineInputBorder(
|
||||||
@@ -124,21 +148,20 @@ class AddLinkModalState extends ConsumerState<AddLinkModal> with SingleTickerPro
|
|||||||
Radius.circular(10),
|
Radius.circular(10),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
errorText: provider.urlError,
|
|
||||||
labelText: t.links.addLink.title,
|
labelText: t.links.addLink.title,
|
||||||
hintText: provider.checkBookmark?.metadata?.title,
|
hintText: provider.checkBookmark?.metadata?.title,
|
||||||
floatingLabelBehavior: FloatingLabelBehavior.always,
|
floatingLabelBehavior:
|
||||||
|
provider.checkBookmark != null ? FloatingLabelBehavior.always : FloatingLabelBehavior.auto,
|
||||||
helperText: t.links.addLink.leaveEmptyUseWebsiteTitle,
|
helperText: t.links.addLink.leaveEmptyUseWebsiteTitle,
|
||||||
|
enabled: provider.checkBookmark != null,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 24),
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
controller: provider.descriptionController,
|
controller: provider.descriptionController,
|
||||||
// onChanged: ref.read(addLinkProvider.notifier).validateUrl,
|
|
||||||
// enabled: provider.checkBookmarkLoadStatus != LoadStatus.loading,
|
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
prefixIcon: const Icon(Icons.description_rounded),
|
prefixIcon: const Icon(Icons.description_rounded),
|
||||||
border: const OutlineInputBorder(
|
border: const OutlineInputBorder(
|
||||||
@@ -146,58 +169,33 @@ class AddLinkModalState extends ConsumerState<AddLinkModal> with SingleTickerPro
|
|||||||
Radius.circular(10),
|
Radius.circular(10),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
errorText: provider.urlError,
|
|
||||||
labelText: t.links.addLink.description,
|
labelText: t.links.addLink.description,
|
||||||
hintText: provider.checkBookmark?.metadata?.description,
|
hintText: provider.checkBookmark?.metadata?.description,
|
||||||
floatingLabelBehavior: FloatingLabelBehavior.always,
|
floatingLabelBehavior:
|
||||||
|
provider.checkBookmark != null ? FloatingLabelBehavior.always : FloatingLabelBehavior.auto,
|
||||||
helperText: t.links.addLink.leaveEmptyUseWebsiteDescription,
|
helperText: t.links.addLink.leaveEmptyUseWebsiteDescription,
|
||||||
|
enabled: provider.checkBookmark != null,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
SectionLabel(
|
||||||
|
label: t.links.addLink.tags,
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 16,
|
||||||
|
vertical: 24,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SectionLabel(
|
||||||
|
label: t.links.addLink.others,
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 16,
|
||||||
|
vertical: 24,
|
||||||
|
),
|
||||||
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 24),
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
child: ExpandablePanel(
|
child: SizedBox(
|
||||||
controller: provider.expandableController,
|
height: 150,
|
||||||
collapsed: ListTile(
|
|
||||||
onTap: () {
|
|
||||||
_animationController.animateTo(provider.expandableController.expanded == true ? 0 : 1);
|
|
||||||
provider.expandableController.toggle();
|
|
||||||
},
|
|
||||||
title: Text(t.links.addLink.addNotes),
|
|
||||||
trailing: RotationTransition(
|
|
||||||
turns: _animation,
|
|
||||||
child: Icon(
|
|
||||||
Icons.expand_more_rounded,
|
|
||||||
size: 26,
|
|
||||||
color: Theme.of(context).colorScheme.secondary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
contentPadding: const EdgeInsets.all(0),
|
|
||||||
),
|
|
||||||
expanded: Column(
|
|
||||||
children: [
|
|
||||||
ListTile(
|
|
||||||
onTap: () {
|
|
||||||
_animationController.animateTo(
|
|
||||||
provider.expandableController.expanded == true ? 0 : 1,
|
|
||||||
);
|
|
||||||
provider.expandableController.toggle();
|
|
||||||
},
|
|
||||||
title: Text(t.links.addLink.addNotes),
|
|
||||||
trailing: RotationTransition(
|
|
||||||
turns: _animation,
|
|
||||||
child: Icon(
|
|
||||||
Icons.expand_more_rounded,
|
|
||||||
size: 26,
|
|
||||||
color: Theme.of(context).colorScheme.secondary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
contentPadding: const EdgeInsets.all(0),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
SizedBox(
|
|
||||||
height: 130,
|
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
controller: provider.notesController,
|
controller: provider.notesController,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
@@ -208,59 +206,26 @@ class AddLinkModalState extends ConsumerState<AddLinkModal> with SingleTickerPro
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
labelText: t.links.addLink.notes,
|
labelText: t.links.addLink.notes,
|
||||||
|
helperText: t.generic.optional,
|
||||||
),
|
),
|
||||||
autocorrect: false,
|
autocorrect: false,
|
||||||
expands: true,
|
expands: true,
|
||||||
minLines: null,
|
minLines: null,
|
||||||
maxLines: null,
|
maxLines: null,
|
||||||
textAlignVertical: TextAlignVertical.top,
|
textAlignVertical: TextAlignVertical.top,
|
||||||
),
|
enabled: provider.checkBookmark != null,
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
SwitchListTile(
|
SwitchListTile(
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24, vertical: 0),
|
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
||||||
title: Text(t.links.addLink.markAsUnread),
|
title: Text(t.links.addLink.markAsUnread),
|
||||||
value: provider.markAsUnread,
|
value: provider.markAsUnread,
|
||||||
onChanged: ref.read(addLinkProvider.notifier).updateMarkAsUnread,
|
onChanged:
|
||||||
|
provider.checkBookmark != null ? (v) => ref.read(addLinkProvider.notifier).updateMarkAsUnread(v) : null,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.all(24),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
|
||||||
children: [
|
|
||||||
TextButton(
|
|
||||||
onPressed: () => Navigator.pop(context),
|
|
||||||
child: Text(t.generic.cancel),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
if (provider.checkBookmark == null)
|
|
||||||
TextButton(
|
|
||||||
onPressed: provider.checkBookmarkLoadStatus != LoadStatus.loading
|
|
||||||
? ref.read(addLinkProvider.notifier).checkUrlDetails
|
|
||||||
: null,
|
|
||||||
child: Text(t.generic.next),
|
|
||||||
),
|
|
||||||
if (provider.checkBookmark != null)
|
|
||||||
TextButton(
|
|
||||||
onPressed: ref.read(addLinkProvider.notifier).addBookmark,
|
|
||||||
child: Text(t.generic.confirm),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
@@ -16,6 +18,8 @@ class Links extends ConsumerWidget {
|
|||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final bookmarks = ref.watch(linksRequestProvider);
|
final bookmarks = ref.watch(linksRequestProvider);
|
||||||
|
|
||||||
|
final width = MediaQuery.of(context).size.width;
|
||||||
|
|
||||||
String validateStrings(String? string1, String? string2) {
|
String validateStrings(String? string1, String? string2) {
|
||||||
if (string1 != null && string1.isNotEmpty) {
|
if (string1 != null && string1.isNotEmpty) {
|
||||||
return string1;
|
return string1;
|
||||||
@@ -44,12 +48,18 @@ class Links extends ConsumerWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void openAddModal() {
|
void openAddModal() {
|
||||||
showModalBottomSheet(
|
showGeneralDialog(
|
||||||
useRootNavigator: true,
|
|
||||||
context: context,
|
context: context,
|
||||||
isScrollControlled: true,
|
barrierColor: !(width > 700 || !(Platform.isAndroid || Platform.isIOS)) ? Colors.transparent : Colors.black54,
|
||||||
useSafeArea: true,
|
transitionBuilder: (context, anim1, anim2, child) {
|
||||||
builder: (context) => const AddLinkModal(),
|
return SlideTransition(
|
||||||
|
position: Tween(begin: const Offset(0, 1), end: const Offset(0, 0)).animate(
|
||||||
|
CurvedAnimation(parent: anim1, curve: Curves.easeInOutCubicEmphasized),
|
||||||
|
),
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
pageBuilder: (context, animation, secondaryAnimation) => AddLinkModal(fullscreen: width <= 700),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
62
lib/widgets/autocomplete_text_field.dart
Normal file
62
lib/widgets/autocomplete_text_field.dart
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class AutocompleteTextField extends StatefulWidget {
|
||||||
|
final IconData leadingIcon;
|
||||||
|
final String label;
|
||||||
|
|
||||||
|
const AutocompleteTextField({
|
||||||
|
Key? key,
|
||||||
|
required this.leadingIcon,
|
||||||
|
required this.label,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<AutocompleteTextField> createState() => _AutocompleteTextFieldState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AutocompleteTextFieldState extends State<AutocompleteTextField> {
|
||||||
|
final _controller = TextEditingController();
|
||||||
|
final _overlayController = OverlayPortalController();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Stack(
|
||||||
|
children: [
|
||||||
|
TextFormField(
|
||||||
|
controller: _controller,
|
||||||
|
onChanged: (value) {
|
||||||
|
if (value.isNotEmpty) {
|
||||||
|
_overlayController.show();
|
||||||
|
} else {
|
||||||
|
_overlayController.hide();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
decoration: InputDecoration(
|
||||||
|
prefixIcon: Icon(widget.leadingIcon),
|
||||||
|
border: const OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.all(
|
||||||
|
Radius.circular(10),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
labelText: widget.label,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
OverlayPortal(
|
||||||
|
controller: _overlayController,
|
||||||
|
overlayChildBuilder: (context) => Positioned(
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
child: Container(
|
||||||
|
width: 100,
|
||||||
|
height: 100,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user