Improved add links
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: 182 (91 per locale)
|
/// Strings: 188 (94 per locale)
|
||||||
///
|
///
|
||||||
/// Built on 2024-02-23 at 18:23 UTC
|
/// Built on 2024-02-23 at 21:28 UTC
|
||||||
|
|
||||||
// coverage:ignore-file
|
// coverage:ignore-file
|
||||||
// ignore_for_file: type=lint
|
// ignore_for_file: type=lint
|
||||||
@@ -313,6 +313,9 @@ class _StringsLinksAddLinkEn {
|
|||||||
String get notes => 'Notes';
|
String get notes => 'Notes';
|
||||||
String get addNotes => 'Add notes';
|
String get addNotes => 'Add notes';
|
||||||
String get tags => 'Tags';
|
String get tags => 'Tags';
|
||||||
|
String get noTagsAdded => 'No tags added';
|
||||||
|
String get tagNoWhitespaces => 'Tag cannot contain whitespaces';
|
||||||
|
String get addTag => 'Add tag';
|
||||||
String get others => 'Others';
|
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.';
|
||||||
@@ -550,6 +553,9 @@ class _StringsLinksAddLinkEs implements _StringsLinksAddLinkEn {
|
|||||||
@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 tags => 'Etiquetas';
|
||||||
|
@override String get noTagsAdded => 'No hay etiquetas añadidas';
|
||||||
|
@override String get tagNoWhitespaces => 'La etiqueta no puede contener espacios';
|
||||||
|
@override String get addTag => 'Añadir etiqueta';
|
||||||
@override String get others => 'Otros';
|
@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.';
|
||||||
@@ -651,6 +657,9 @@ extension on Translations {
|
|||||||
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.tags': return 'Tags';
|
||||||
|
case 'links.addLink.noTagsAdded': return 'No tags added';
|
||||||
|
case 'links.addLink.tagNoWhitespaces': return 'Tag cannot contain whitespaces';
|
||||||
|
case 'links.addLink.addTag': return 'Add tag';
|
||||||
case 'links.addLink.others': return 'Others';
|
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.';
|
||||||
@@ -750,6 +759,9 @@ extension on _StringsEs {
|
|||||||
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.tags': return 'Etiquetas';
|
||||||
|
case 'links.addLink.noTagsAdded': return 'No hay etiquetas añadidas';
|
||||||
|
case 'links.addLink.tagNoWhitespaces': return 'La etiqueta no puede contener espacios';
|
||||||
|
case 'links.addLink.addTag': return 'Añadir etiqueta';
|
||||||
case 'links.addLink.others': return 'Otros';
|
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.';
|
||||||
|
|||||||
@@ -54,6 +54,9 @@
|
|||||||
"notes": "Notes",
|
"notes": "Notes",
|
||||||
"addNotes": "Add notes",
|
"addNotes": "Add notes",
|
||||||
"tags": "Tags",
|
"tags": "Tags",
|
||||||
|
"noTagsAdded": "No tags added",
|
||||||
|
"tagNoWhitespaces": "Tag cannot contain whitespaces",
|
||||||
|
"addTag": "Add tag",
|
||||||
"others": "Others",
|
"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.",
|
||||||
|
|||||||
@@ -54,6 +54,9 @@
|
|||||||
"notes": "Notas",
|
"notes": "Notas",
|
||||||
"addNotes": "Añadir notas",
|
"addNotes": "Añadir notas",
|
||||||
"tags": "Etiquetas",
|
"tags": "Etiquetas",
|
||||||
|
"noTagsAdded": "No hay etiquetas añadidas",
|
||||||
|
"tagNoWhitespaces": "La etiqueta no puede contener espacios",
|
||||||
|
"addTag": "Añadir etiqueta",
|
||||||
"others": "Otros",
|
"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.",
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ class PostBookmark {
|
|||||||
final bool isArchived;
|
final bool isArchived;
|
||||||
final bool unread;
|
final bool unread;
|
||||||
final bool shared;
|
final bool shared;
|
||||||
final List<String> tagNames;
|
final String tagNames;
|
||||||
|
|
||||||
const PostBookmark({
|
const PostBookmark({
|
||||||
required this.url,
|
required this.url,
|
||||||
@@ -21,9 +21,9 @@ class PostBookmark {
|
|||||||
"url": url,
|
"url": url,
|
||||||
"title": title,
|
"title": title,
|
||||||
"description": description,
|
"description": description,
|
||||||
"isArchived": isArchived,
|
"is_archived": isArchived,
|
||||||
"unread": unread,
|
"unread": unread,
|
||||||
"shared": shared,
|
"shared": shared,
|
||||||
"tagNames": tagNames,
|
"tag_names": tagNames,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
51
lib/models/data/tags.dart
Normal file
51
lib/models/data/tags.dart
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
class Tags {
|
||||||
|
final int? count;
|
||||||
|
final int? next;
|
||||||
|
final int? previous;
|
||||||
|
final List<Result>? results;
|
||||||
|
|
||||||
|
Tags({
|
||||||
|
this.count,
|
||||||
|
this.next,
|
||||||
|
this.previous,
|
||||||
|
this.results,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory Tags.fromJson(Map<String, dynamic> json) => Tags(
|
||||||
|
count: json["count"],
|
||||||
|
next: json["next"],
|
||||||
|
previous: json["previous"],
|
||||||
|
results: json["results"] == null ? [] : List<Result>.from(json["results"]!.map((x) => Result.fromJson(x))),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => {
|
||||||
|
"count": count,
|
||||||
|
"next": next,
|
||||||
|
"previous": previous,
|
||||||
|
"results": results == null ? [] : List<dynamic>.from(results!.map((x) => x.toJson())),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class Result {
|
||||||
|
final int? id;
|
||||||
|
final String? name;
|
||||||
|
final DateTime? dateAdded;
|
||||||
|
|
||||||
|
Result({
|
||||||
|
this.id,
|
||||||
|
this.name,
|
||||||
|
this.dateAdded,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory Result.fromJson(Map<String, dynamic> json) => Result(
|
||||||
|
id: json["id"],
|
||||||
|
name: json["name"],
|
||||||
|
dateAdded: json["date_added"] == null ? null : DateTime.parse(json["date_added"]),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => {
|
||||||
|
"id": id,
|
||||||
|
"name": name,
|
||||||
|
"date_added": dateAdded?.toIso8601String(),
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -11,7 +11,9 @@ class AddLinkModel {
|
|||||||
final TextEditingController titleController;
|
final TextEditingController titleController;
|
||||||
final TextEditingController descriptionController;
|
final TextEditingController descriptionController;
|
||||||
bool markAsUnread;
|
bool markAsUnread;
|
||||||
final List<String> tags;
|
final TextEditingController tagsController;
|
||||||
|
String? tagsError;
|
||||||
|
List<String> tags;
|
||||||
final TextEditingController notesController;
|
final TextEditingController notesController;
|
||||||
|
|
||||||
AddLinkModel({
|
AddLinkModel({
|
||||||
@@ -22,6 +24,8 @@ class AddLinkModel {
|
|||||||
required this.titleController,
|
required this.titleController,
|
||||||
required this.descriptionController,
|
required this.descriptionController,
|
||||||
this.markAsUnread = false,
|
this.markAsUnread = false,
|
||||||
|
required this.tagsController,
|
||||||
|
this.tagsError,
|
||||||
required this.tags,
|
required this.tags,
|
||||||
required this.notesController,
|
required this.notesController,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
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/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/models/data/tags.dart';
|
||||||
|
import 'package:linkdy/utils/snackbar.dart';
|
||||||
import 'package:linkdy/i18n/strings.g.dart';
|
import 'package:linkdy/i18n/strings.g.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';
|
||||||
@@ -30,6 +31,12 @@ FutureOr<ApiResponse<Bookmark>> addBookmark(AddBookmarkRef ref, PostBookmark new
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@riverpod
|
||||||
|
FutureOr<ApiResponse<Tags>> getTags(GetTagsRef ref) async {
|
||||||
|
final result = await ref.watch(apiClientProviderProvider)!.fetchTags();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
@riverpod
|
@riverpod
|
||||||
class AddLink extends _$AddLink {
|
class AddLink extends _$AddLink {
|
||||||
@override
|
@override
|
||||||
@@ -38,6 +45,7 @@ class AddLink extends _$AddLink {
|
|||||||
urlController: TextEditingController(),
|
urlController: TextEditingController(),
|
||||||
titleController: TextEditingController(),
|
titleController: TextEditingController(),
|
||||||
descriptionController: TextEditingController(),
|
descriptionController: TextEditingController(),
|
||||||
|
tagsController: TextEditingController(),
|
||||||
tags: [],
|
tags: [],
|
||||||
notesController: TextEditingController(),
|
notesController: TextEditingController(),
|
||||||
);
|
);
|
||||||
@@ -86,7 +94,7 @@ class AddLink extends _$AddLink {
|
|||||||
isArchived: false,
|
isArchived: false,
|
||||||
unread: state.markAsUnread,
|
unread: state.markAsUnread,
|
||||||
shared: false,
|
shared: false,
|
||||||
tagNames: [],
|
tagNames: state.tags.join(","),
|
||||||
);
|
);
|
||||||
|
|
||||||
final processModal = ProcessModal();
|
final processModal = ProcessModal();
|
||||||
@@ -106,4 +114,23 @@ class AddLink extends _$AddLink {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void validateTagInput(String value) {
|
||||||
|
if (value.contains(" ")) {
|
||||||
|
state.tagsError = t.links.addLink.tagNoWhitespaces;
|
||||||
|
} else {
|
||||||
|
state.tagsError = null;
|
||||||
|
}
|
||||||
|
ref.notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setTags(List<String> tags) {
|
||||||
|
state.tags = tags;
|
||||||
|
ref.notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearTagsController() {
|
||||||
|
state.tagsController.clear();
|
||||||
|
ref.notifyListeners();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -290,7 +290,21 @@ class _AddBookmarkProviderElement
|
|||||||
PostBookmark get newBookmark => (origin as AddBookmarkProvider).newBookmark;
|
PostBookmark get newBookmark => (origin as AddBookmarkProvider).newBookmark;
|
||||||
}
|
}
|
||||||
|
|
||||||
String _$addLinkHash() => r'0e9fcd2cca9fa5e195072163a11ae09b7bbe903a';
|
String _$getTagsHash() => r'feecc7515f9492838c4a9bccc7031f49713e6ebb';
|
||||||
|
|
||||||
|
/// See also [getTags].
|
||||||
|
@ProviderFor(getTags)
|
||||||
|
final getTagsProvider = AutoDisposeFutureProvider<ApiResponse<Tags>>.internal(
|
||||||
|
getTags,
|
||||||
|
name: r'getTagsProvider',
|
||||||
|
debugGetCreateSourceHash:
|
||||||
|
const bool.fromEnvironment('dart.vm.product') ? null : _$getTagsHash,
|
||||||
|
dependencies: null,
|
||||||
|
allTransitiveDependencies: null,
|
||||||
|
);
|
||||||
|
|
||||||
|
typedef GetTagsRef = AutoDisposeFutureProviderRef<ApiResponse<Tags>>;
|
||||||
|
String _$addLinkHash() => r'fd6e2629386254ace7ac7abeff0e566dda69575a';
|
||||||
|
|
||||||
/// See also [AddLink].
|
/// See also [AddLink].
|
||||||
@ProviderFor(AddLink)
|
@ProviderFor(AddLink)
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
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:easy_autocomplete/easy_autocomplete.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/widgets/section_label.dart';
|
||||||
|
|
||||||
import 'package:linkdy/i18n/strings.g.dart';
|
import 'package:linkdy/i18n/strings.g.dart';
|
||||||
@@ -49,6 +49,8 @@ class _ModalContent extends ConsumerWidget {
|
|||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final provider = ref.watch(addLinkProvider);
|
final provider = ref.watch(addLinkProvider);
|
||||||
|
|
||||||
|
final tags = ref.watch(getTagsProvider);
|
||||||
|
|
||||||
return ListView(
|
return ListView(
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
@@ -185,6 +187,86 @@ class _ModalContent extends ConsumerWidget {
|
|||||||
vertical: 24,
|
vertical: 24,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
if (tags.isLoading == false)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
if (provider.checkBookmark != null)
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: EasyAutocomplete(
|
||||||
|
controller: provider.tagsController,
|
||||||
|
onChanged: ref.read(addLinkProvider.notifier).validateTagInput,
|
||||||
|
suggestions: tags.value?.content?.results?.map((t) => t.name!).toList() ?? [],
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: const OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.all(
|
||||||
|
Radius.circular(10),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
labelText: t.links.addLink.tags,
|
||||||
|
errorText: provider.tagsError,
|
||||||
|
),
|
||||||
|
suggestionBuilder: (data) => Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||||
|
child: Text(
|
||||||
|
data,
|
||||||
|
style: TextStyle(fontSize: 16, color: Theme.of(context).colorScheme.onSurfaceVariant),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onSubmitted: provider.tagsController.text != "" && provider.tagsError == null
|
||||||
|
? (v) {
|
||||||
|
ref.read(addLinkProvider.notifier).setTags([...provider.tags, v]);
|
||||||
|
ref.read(addLinkProvider.notifier).clearTagsController();
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 16),
|
||||||
|
IconButton(
|
||||||
|
onPressed: provider.tagsController.text != "" && provider.tagsError == null
|
||||||
|
? () {
|
||||||
|
ref
|
||||||
|
.read(addLinkProvider.notifier)
|
||||||
|
.setTags([...provider.tags, provider.tagsController.text]);
|
||||||
|
ref.read(addLinkProvider.notifier).clearTagsController();
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
icon: const Icon(Icons.check_rounded),
|
||||||
|
tooltip: t.links.addLink.addTag,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
if (provider.checkBookmark != null) const SizedBox(height: 16),
|
||||||
|
SizedBox(
|
||||||
|
height: 40,
|
||||||
|
child: provider.tags.isNotEmpty
|
||||||
|
? ListView.separated(
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
itemCount: provider.tags.length,
|
||||||
|
separatorBuilder: (context, index) => const SizedBox(width: 8),
|
||||||
|
itemBuilder: (context, index) => InputChip(
|
||||||
|
label: Text(provider.tags[index]),
|
||||||
|
onDeleted: () => ref
|
||||||
|
.read(addLinkProvider.notifier)
|
||||||
|
.setTags(provider.tags.where((tag) => tag != provider.tags[index]).toList()),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Center(
|
||||||
|
child: Text(
|
||||||
|
t.links.addLink.noTagsAdded,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
color: Theme.of(context).colorScheme.secondary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
SectionLabel(
|
SectionLabel(
|
||||||
label: t.links.addLink.others,
|
label: t.links.addLink.others,
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import 'package:linkdy/models/api_response.dart';
|
|||||||
import 'package:linkdy/models/data/bookmarks.dart';
|
import 'package:linkdy/models/data/bookmarks.dart';
|
||||||
import 'package:linkdy/models/data/check_bookmark.dart';
|
import 'package:linkdy/models/data/check_bookmark.dart';
|
||||||
import 'package:linkdy/models/data/post_bookmark.dart';
|
import 'package:linkdy/models/data/post_bookmark.dart';
|
||||||
|
import 'package:linkdy/models/data/tags.dart';
|
||||||
import 'package:linkdy/models/server_instance.dart';
|
import 'package:linkdy/models/server_instance.dart';
|
||||||
|
|
||||||
class ApiClient {
|
class ApiClient {
|
||||||
@@ -74,4 +75,16 @@ class ApiClient {
|
|||||||
return const ApiResponse(successful: false);
|
return const ApiResponse(successful: false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<ApiResponse<Tags>> fetchTags() async {
|
||||||
|
try {
|
||||||
|
final response = await dioInstance.get("/tags/");
|
||||||
|
return ApiResponse(
|
||||||
|
successful: true,
|
||||||
|
content: Tags.fromJson(response.data),
|
||||||
|
);
|
||||||
|
} catch (e, stackTrace) {
|
||||||
|
return const ApiResponse(successful: false);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,62 +0,0 @@
|
|||||||
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,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -289,6 +289,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.6.9"
|
version: "1.6.9"
|
||||||
|
easy_autocomplete:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: easy_autocomplete
|
||||||
|
sha256: e7ded95deb010daec41caf25f553b450fbdaa6769e7a6fdb7b8dce6764318406
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.6.0"
|
||||||
expandable:
|
expandable:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ dependencies:
|
|||||||
share_plus: ^7.2.2
|
share_plus: ^7.2.2
|
||||||
package_info_plus: ^5.0.1
|
package_info_plus: ^5.0.1
|
||||||
expandable: ^5.0.1
|
expandable: ^5.0.1
|
||||||
|
easy_autocomplete: ^1.6.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
build_runner: ^2.4.8
|
build_runner: ^2.4.8
|
||||||
|
|||||||
Reference in New Issue
Block a user