From 168f690fb2e54b392fd592a8ed27155182519998 Mon Sep 17 00:00:00 2001 From: mahesh jamdade Date: Thu, 28 Nov 2024 23:16:03 -0500 Subject: [PATCH] prepare for release v1.2.0 --- CHANGELOG.md | 50 +++ README.md | 18 +- example/lib/{custom.dart => UserSelect.dart} | 0 example/lib/main.dart | 153 ++------- example/lib/sample.dart | 310 +++++++++++++++++++ pubspec.yaml | 2 +- 6 files changed, 397 insertions(+), 136 deletions(-) rename example/lib/{custom.dart => UserSelect.dart} (100%) create mode 100644 example/lib/sample.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index c4f1c99..d771cc5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,53 @@ +#### [1.2.0] - November 28, 2024 + +- [Breaking]: Handle selected Value on Client Side [Issue: #191](https://github.com/maheshj01/searchfield/issues/191) + +**Before** + +The package sets the selectedValue automatically inside the component, which leads to issues when navigating or selecting suggestions +```dart +SearchField( + hint: 'Basic SearchField', + dynamicHeight: true, + maxSuggestionBoxHeight: 300, + initialValue: SearchFieldListItem('ABC'), + suggestions: dynamicHeightSuggestion + .map(SearchFieldListItem.new) + .toList(), + suggestionState: Suggestion.expand, +), +``` + +**After** + +Now the client should handle te selectedValue explicitly in the client code (consumer code) by using the onSuggestionTap callback. This approach simplifies the API and provides more accurate control over the internal state of the package. + +```dart +var selectedValue = SearchFieldListItem('ABC'); + +SearchField( + hint: 'Basic SearchField', + dynamicHeight: true, + maxSuggestionBoxHeight: 300, + onSuggestionTap: (SearchFieldListItem item) { + setState(() { + selectedValue = item; + }); + }, + selectedValue: selectedValue, + suggestions: dynamicHeightSuggestion + .map(SearchFieldListItem.new) + .toList(), + suggestionState: Suggestion.expand, +), +``` +- Fixes: [Issue: #178](https://github.com/maheshj01/searchfield/issues/178), [Issue: #155](https://github.com/maheshj01/searchfield/issues/155) + +- Fixes: [Issue: #151](https://github.com/maheshj01/searchfield/issues/151) Clarifies use of SuggestionAction and now defaults to `SuggestionState.unfocus` without having to use `FocusNode` on client side. + +Huge thanks to all contributors and supporters. +Happy Thanksgiving! 🦃 + #### [1.1.9] - November 25, 2024 - [regression] Fix: Keyboard navigation does not work [Issue: 183](https://github.com/maheshj01/searchfield/issues/182) diff --git a/README.md b/README.md index eecc6ec..6a803ad 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# [searchfield: ^1.1.9](https://pub.dev/packages/searchfield) +# [searchfield: ^1.2.0](https://pub.dev/packages/searchfield) Flutter Platform Badge Pub @@ -48,6 +48,8 @@ Use the Widget #### Example1 ```dart + +var selectedValue = null; SearchField( suggestions: countries .map( @@ -70,6 +72,12 @@ SearchField( ], ), ), + selectedValue: selectedValue, + onSuggestionTap: (SearchFieldListItem x) { + setState(() { + selectedValue = x.item; + }); + }, ), ).toList(), ), @@ -126,7 +134,7 @@ SearchField( focusNode: focus, suggestionState: Suggestion.expand, onSuggestionTap: (SearchFieldListItem x) { - focus.unfocus(); + }, ), ``` @@ -219,11 +227,13 @@ Form( .toList(), focusNode: focus, suggestionState: Suggestion.expand, + selectedValue: selectedValue, onSuggestionTap: (SearchFieldListItem x) { - focus.unfocus(); + setState(() { + selectedValue = x.item; + }); }, ), -)); ``` diff --git a/example/lib/custom.dart b/example/lib/UserSelect.dart similarity index 100% rename from example/lib/custom.dart rename to example/lib/UserSelect.dart diff --git a/example/lib/main.dart b/example/lib/main.dart index 323bc78..7d2e4b9 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,8 +1,4 @@ // import 'package:example/pagination.dart'; -import 'package:example/custom.dart'; -import 'package:example/dynamic_height.dart'; -import 'package:example/network_sample.dart'; -import 'package:example/pagination.dart'; import 'package:flutter/material.dart'; import 'package:searchfield/searchfield.dart'; @@ -13,6 +9,7 @@ void main() { class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); + // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( @@ -22,7 +19,6 @@ class MyApp extends StatelessWidget { useMaterial3: true, brightness: Brightness.light, ), - themeMode: ThemeMode.system, darkTheme: ThemeData( colorSchemeSeed: Colors.blue, useMaterial3: true, @@ -67,42 +63,19 @@ class _SearchFieldSampleState extends State { 'Nigeria', 'Egypt', ]; - selected = suggestions[0]; super.initState(); } - int suggestionsCount = 12; - final focus = FocusNode(); - final dynamicHeightSuggestion = [ - 'ABC\nABC\nABC\nABC', - 'DEF\nABC', - 'GHI', - 'JKL\nABC', - 'ABC', - '123\n123', - '123\n123', - '123\n123', - '123\n123', - '123\n123', - '123\n123', - 'àkajsddddddddddddddddddddddddddddddddddddddddddddddddddđ', - 'àkajsddddddddddddddddddddddddddddddddddddddddddddddddddđ', - 'àkajsddddddddddddddddddddddddddddddddddddddddddddddddddđ', - 'àkajsddddddddddddddddddddddddddddddddddddddddddddddddddđ', - 'àkajsddddddddddddddddddddddddddddddddddddddddddddddddddđ', - ]; - final TextEditingController searchController = TextEditingController(); var suggestions = []; - int counter = 0; - var selected = ''; - var selectedValue = SearchFieldListItem('United States'); + var selectedValue = null; @override Widget build(BuildContext context) { Widget searchChild(x, {bool isSelected = false}) => Padding( padding: const EdgeInsets.symmetric(horizontal: 12), child: Text(x, style: TextStyle( - fontSize: 18, color: isSelected ? Colors.green : null)), + fontSize: 18, + color: isSelected ? Colors.green : Colors.black)), ); return Scaffold( appBar: AppBar(title: Text('Searchfield Demo')), @@ -110,83 +83,6 @@ class _SearchFieldSampleState extends State { padding: const EdgeInsets.all(8.0), child: ListView( children: [ - UserSelect(), - SizedBox( - height: 20, - ), - DynamicHeightExample(), - SizedBox( - height: 20, - ), - SearchField( - hint: 'Basic SearchField', - dynamicHeight: true, - maxSuggestionBoxHeight: 300, - onSuggestionTap: (SearchFieldListItem item) { - setState(() { - selectedValue = item; - }); - }, - selectedValue: selectedValue, - suggestions: - suggestions.map(SearchFieldListItem.new).toList(), - suggestionState: Suggestion.expand, - ), - SizedBox( - height: 20, - ), - TextFormField( - autovalidateMode: AutovalidateMode.onUserInteraction, - decoration: InputDecoration( - labelText: 'Flutter TextFormField', - ), - validator: (value) { - if (value == null || value.length < 4) { - return 'error'; - } - return null; - }), - SizedBox( - height: 50, - ), - Pagination(), - SizedBox( - height: 50, - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: SearchField( - maxSuggestionsInViewPort: 11, - suggestionAction: SuggestionAction.unfocus, - searchInputDecoration: SearchInputDecoration( - hintText: 'Search', - cursorColor: Colors.blue, - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - ), - ), - selectedValue: selectedValue, - onSuggestionTap: (SearchFieldListItem item) { - setState(() { - selectedValue = item; - }); - }, - suggestions: suggestions - .map( - (e) => SearchFieldListItem(e, - item: e, - child: searchChild(e, isSelected: e == selected)), - ) - .toList(), - ), - ), - SizedBox( - height: 50, - ), - NetworkSample(), - SizedBox( - height: 50, - ), SearchField( suggestionDirection: SuggestionDirection.flex, onSearchTextChanged: (query) { @@ -199,8 +95,7 @@ class _SearchFieldSampleState extends State { SearchFieldListItem(e, child: searchChild(e))) .toList(); }, - selectedValue: SearchFieldListItem('United States'), - controller: searchController, + selectedValue: selectedValue, autovalidateMode: AutovalidateMode.onUserInteraction, validator: (value) { if (value == null || !suggestions.contains(value.trim())) { @@ -213,9 +108,6 @@ class _SearchFieldSampleState extends State { key: const Key('searchfield'), hint: 'Search by country name', itemHeight: 50, - onTapOutside: (x) { - // focus.unfocus(); - }, scrollbarDecoration: ScrollbarDecoration( thickness: 12, radius: Radius.circular(6), @@ -280,29 +172,28 @@ class _SearchFieldSampleState extends State { .map((e) => SearchFieldListItem(e, child: searchChild(e))) .toList(), - focusNode: focus, suggestionState: Suggestion.expand, - onSuggestionTap: (SearchFieldListItem x) {}, - ), - SizedBox( - height: 50, + onSuggestionTap: (SearchFieldListItem x) { + setState(() { + selectedValue = x; + }); + }, ), + SizedBox(height: 20), SearchField( - enabled: false, - hint: 'Disabled SearchField', - suggestions: ['ABC', 'DEF', 'GHI', 'JKL'] - .map(SearchFieldListItem.new) - .toList(), + hint: 'Basic SearchField', + dynamicHeight: true, + maxSuggestionBoxHeight: 300, + onSuggestionTap: (SearchFieldListItem item) { + setState(() { + selectedValue = item; + }); + }, + selectedValue: selectedValue, + suggestions: + suggestions.map(SearchFieldListItem.new).toList(), suggestionState: Suggestion.expand, ), - SizedBox( - height: 50, - ), - NetworkSample(), - Text( - 'Counter: $counter', - style: Theme.of(context).textTheme.bodyLarge, - ), ], ), )); diff --git a/example/lib/sample.dart b/example/lib/sample.dart new file mode 100644 index 0000000..323bc78 --- /dev/null +++ b/example/lib/sample.dart @@ -0,0 +1,310 @@ +// import 'package:example/pagination.dart'; +import 'package:example/custom.dart'; +import 'package:example/dynamic_height.dart'; +import 'package:example/network_sample.dart'; +import 'package:example/pagination.dart'; +import 'package:flutter/material.dart'; +import 'package:searchfield/searchfield.dart'; + +void main() { + runApp(const MyApp()); +} + +class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Flutter App', + theme: ThemeData( + colorSchemeSeed: Colors.indigo, + useMaterial3: true, + brightness: Brightness.light, + ), + themeMode: ThemeMode.system, + darkTheme: ThemeData( + colorSchemeSeed: Colors.blue, + useMaterial3: true, + brightness: Brightness.dark, + ), + home: SearchFieldSample(), + debugShowCheckedModeBanner: false, + ); + } +} + +class SearchFieldSample extends StatefulWidget { + const SearchFieldSample({Key? key}) : super(key: key); + + @override + State createState() => _SearchFieldSampleState(); +} + +class _SearchFieldSampleState extends State { + @override + void initState() { + suggestions = [ + 'United States', + 'Germany', + 'Canada', + 'United Kingdom', + 'France', + 'Italy', + 'Spain', + 'Australia', + 'India', + 'China', + 'Japan', + 'Brazil', + 'South Africa', + 'Mexico', + 'Argentina', + 'Russia', + 'Indonesia', + 'Turkey', + 'Saudi Arabia', + 'Nigeria', + 'Egypt', + ]; + selected = suggestions[0]; + super.initState(); + } + + int suggestionsCount = 12; + final focus = FocusNode(); + final dynamicHeightSuggestion = [ + 'ABC\nABC\nABC\nABC', + 'DEF\nABC', + 'GHI', + 'JKL\nABC', + 'ABC', + '123\n123', + '123\n123', + '123\n123', + '123\n123', + '123\n123', + '123\n123', + 'àkajsddddddddddddddddddddddddddddddddddddddddddddddddddđ', + 'àkajsddddddddddddddddddddddddddddddddddddddddddddddddddđ', + 'àkajsddddddddddddddddddddddddddddddddddddddddddddddddddđ', + 'àkajsddddddddddddddddddddddddddddddddddddddddddddddddddđ', + 'àkajsddddddddddddddddddddddddddddddddddddddddddddddddddđ', + ]; + final TextEditingController searchController = TextEditingController(); + var suggestions = []; + int counter = 0; + var selected = ''; + var selectedValue = SearchFieldListItem('United States'); + @override + Widget build(BuildContext context) { + Widget searchChild(x, {bool isSelected = false}) => Padding( + padding: const EdgeInsets.symmetric(horizontal: 12), + child: Text(x, + style: TextStyle( + fontSize: 18, color: isSelected ? Colors.green : null)), + ); + return Scaffold( + appBar: AppBar(title: Text('Searchfield Demo')), + body: Padding( + padding: const EdgeInsets.all(8.0), + child: ListView( + children: [ + UserSelect(), + SizedBox( + height: 20, + ), + DynamicHeightExample(), + SizedBox( + height: 20, + ), + SearchField( + hint: 'Basic SearchField', + dynamicHeight: true, + maxSuggestionBoxHeight: 300, + onSuggestionTap: (SearchFieldListItem item) { + setState(() { + selectedValue = item; + }); + }, + selectedValue: selectedValue, + suggestions: + suggestions.map(SearchFieldListItem.new).toList(), + suggestionState: Suggestion.expand, + ), + SizedBox( + height: 20, + ), + TextFormField( + autovalidateMode: AutovalidateMode.onUserInteraction, + decoration: InputDecoration( + labelText: 'Flutter TextFormField', + ), + validator: (value) { + if (value == null || value.length < 4) { + return 'error'; + } + return null; + }), + SizedBox( + height: 50, + ), + Pagination(), + SizedBox( + height: 50, + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: SearchField( + maxSuggestionsInViewPort: 11, + suggestionAction: SuggestionAction.unfocus, + searchInputDecoration: SearchInputDecoration( + hintText: 'Search', + cursorColor: Colors.blue, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + ), + ), + selectedValue: selectedValue, + onSuggestionTap: (SearchFieldListItem item) { + setState(() { + selectedValue = item; + }); + }, + suggestions: suggestions + .map( + (e) => SearchFieldListItem(e, + item: e, + child: searchChild(e, isSelected: e == selected)), + ) + .toList(), + ), + ), + SizedBox( + height: 50, + ), + NetworkSample(), + SizedBox( + height: 50, + ), + SearchField( + suggestionDirection: SuggestionDirection.flex, + onSearchTextChanged: (query) { + final filter = suggestions + .where((element) => + element.toLowerCase().contains(query.toLowerCase())) + .toList(); + return filter + .map((e) => + SearchFieldListItem(e, child: searchChild(e))) + .toList(); + }, + selectedValue: SearchFieldListItem('United States'), + controller: searchController, + autovalidateMode: AutovalidateMode.onUserInteraction, + validator: (value) { + if (value == null || !suggestions.contains(value.trim())) { + return 'Enter a valid country name'; + } + return null; + }, + onSubmit: (x) {}, + autofocus: false, + key: const Key('searchfield'), + hint: 'Search by country name', + itemHeight: 50, + onTapOutside: (x) { + // focus.unfocus(); + }, + scrollbarDecoration: ScrollbarDecoration( + thickness: 12, + radius: Radius.circular(6), + trackColor: Colors.grey, + trackBorderColor: Colors.red, + thumbColor: Colors.orange, + ), + suggestionStyle: + const TextStyle(fontSize: 18, color: Colors.black), + suggestionItemDecoration: BoxDecoration( + // color: Colors.grey[100], + // borderRadius: BorderRadius.circular(10), + border: Border( + bottom: BorderSide( + color: Colors.grey.shade200, + width: 1, + ), + ), + ), + searchInputDecoration: SearchInputDecoration( + searchStyle: TextStyle(fontSize: 18, color: Colors.black), + hintStyle: TextStyle(fontSize: 18, color: Colors.grey), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(24), + borderSide: const BorderSide( + width: 1, + color: Colors.orange, + style: BorderStyle.solid, + ), + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(24), + borderSide: const BorderSide( + width: 1, + color: Colors.black, + style: BorderStyle.solid, + ), + ), + contentPadding: const EdgeInsets.symmetric( + horizontal: 20, + ), + ), + suggestionsDecoration: SuggestionDecoration( + // border: Border.all(color: Colors.orange), + elevation: 8.0, + selectionColor: Colors.grey.shade100, + hoverColor: Colors.purple.shade100, + gradient: LinearGradient( + colors: [ + Color(0xfffc466b), + Color.fromARGB(255, 103, 128, 255) + ], + stops: [0.25, 0.75], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ), + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(10), + bottomRight: Radius.circular(10), + )), + suggestions: suggestions + .map((e) => + SearchFieldListItem(e, child: searchChild(e))) + .toList(), + focusNode: focus, + suggestionState: Suggestion.expand, + onSuggestionTap: (SearchFieldListItem x) {}, + ), + SizedBox( + height: 50, + ), + SearchField( + enabled: false, + hint: 'Disabled SearchField', + suggestions: ['ABC', 'DEF', 'GHI', 'JKL'] + .map(SearchFieldListItem.new) + .toList(), + suggestionState: Suggestion.expand, + ), + SizedBox( + height: 50, + ), + NetworkSample(), + Text( + 'Counter: $counter', + style: Theme.of(context).textTheme.bodyLarge, + ), + ], + ), + )); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index 5d975e0..2fec1ab 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: searchfield description: A highly customizable, simple and easy to use flutter Widget to add a searchfield to your Flutter Application. This Widget allows you to search and select from list of suggestions. -version: 1.1.9 +version: 1.2.0 homepage: https://github.com/maheshj01/searchfield repository: https://github.com/maheshj01/searchfield issue_tracker: https://github.com/maheshj01/searchfield/issues