From 2614ecb0ee1fdfeefee84dbe87047f0d2d109c6d Mon Sep 17 00:00:00 2001 From: vasilich Date: Tue, 12 Jan 2021 20:18:28 +0200 Subject: [PATCH 1/8] clear and dropdown icon builders `suffixIcons` adds an ability to switch icon management through the `suffixIcon` of `InputDecoration` --- CHANGELOG.md | 4 ++ example/ios/Runner.xcodeproj/project.pbxproj | 17 +---- example/lib/main.dart | 68 ++++++++++++++++++-- lib/dropdown_search.dart | 45 +++++++++---- pubspec.yaml | 2 +- 5 files changed, 101 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e82957b..68baa1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## [0.4.9] - 2021.01.12 +* added an ability to override the clear and dropdown icon buttons with builder +* `suffixIcons` adds an ability to switch icon management through the `suffixIcon` of `InputDecoration` + ## [0.4.8] - 2020.11.20 * fix bug caused by last flutter SDK breaking changes [#69](https://github.com/salim-lachdhaf/searchable_dropdown/issues/69) * Add a getter for the selected item diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 7d30b69..f1159c3 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -9,11 +9,7 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; - 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; - 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; - 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; @@ -27,8 +23,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */, - 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -39,13 +33,11 @@ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; - 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; @@ -58,8 +50,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, - 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -69,9 +59,7 @@ 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( - 3B80C3931E831B6300D905FE /* App.framework */, 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, - 9740EEBA1CF902C7004384FC /* Flutter.framework */, 9740EEB21CF90195004384FC /* Debug.xcconfig */, 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 9740EEB31CF90195004384FC /* Generated.xcconfig */, @@ -203,7 +191,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin"; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; @@ -255,7 +243,6 @@ /* Begin XCBuildConfiguration section */ 249021D3217E4FDB00AE95B9 /* Profile */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -331,7 +318,6 @@ }; 97C147031CF9000F007C117D /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -387,7 +373,6 @@ }; 97C147041CF9000F007C117D /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; diff --git a/example/lib/main.dart b/example/lib/main.dart index 81777c2..81a8adc 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -53,6 +53,60 @@ class _MyHomePageState extends State { selectedItem: "Tunisia", ), Divider(), + + ///Menu Mode with overriden icon and dropdown buttons + Row( + children: [ + Expanded( + flex: 2, + child: DropdownSearch( + validator: (v) => v == null ? "required field" : null, + hint: "Select a country", + mode: Mode.MENU, + dropdownSearchDecoration: InputDecoration( + filled: true, + border: UnderlineInputBorder( + borderSide: BorderSide(color: Color(0xFF01689A)), + ), + ), + suffixIcons: true, + clearButtonBuilder: (_, onPressed) => IconButton( + icon: const Icon(Icons.clear, + size: 24, color: Colors.black), + onPressed: onPressed, + ), + dropdownButtonBuilder: (_, onPressed) => IconButton( + icon: const Icon(Icons.arrow_drop_down, + size: 24, color: Colors.black), + onPressed: onPressed, + ), + showSelectedItem: true, + items: [ + "Brazil", + "Italia (Disabled)", + "Tunisia", + 'Canada' + ], + label: "Menu mode *", + showClearButton: true, + onChanged: print, + popupItemDisabled: (String s) => s.startsWith('I'), + selectedItem: "Tunisia", + ), + ), + Expanded( + child: TextField( + decoration: InputDecoration( + filled: true, + labelText: "Menu mode *", + border: UnderlineInputBorder( + borderSide: BorderSide(color: Color(0xFF01689A)), + ), + ), + )) + ], + ), + Divider(), DropdownSearch( searchBoxController: TextEditingController(text: 'Mrs'), mode: Mode.BOTTOM_SHEET, @@ -180,12 +234,14 @@ class _MyHomePageState extends State { .changeSelectedItem("No"); }, child: Text("set to 'NO'")), - RaisedButton( - onPressed: () { - _openDropDownProgKey.currentState - .changeSelectedItem("Yes"); - }, - child: Text("set to 'YES'")), + Material( + child: RaisedButton( + onPressed: () { + _openDropDownProgKey.currentState + .changeSelectedItem("Yes"); + }, + child: Text("set to 'YES'")), + ), RaisedButton( onPressed: () { _openDropDownProgKey.currentState diff --git a/lib/dropdown_search.dart b/lib/dropdown_search.dart index f272f5a..e7eb92f 100644 --- a/lib/dropdown_search.dart +++ b/lib/dropdown_search.dart @@ -25,6 +25,7 @@ typedef Widget ErrorBuilder( BuildContext context, String searchEntry, dynamic exception); typedef Widget EmptyBuilder(BuildContext context, String searchEntry); typedef Widget LoadingBuilder(BuildContext context, String searchEntry); +typedef Widget IconButtonBuilder(BuildContext context, VoidCallback onPressed); enum Mode { DIALOG, BOTTOM_SHEET, MENU } @@ -125,9 +126,18 @@ class DropdownSearch extends StatefulWidget { ///custom dropdown clear button icon widget final Widget clearButton; + ///custom clear button widget builder + final IconButtonBuilder clearButtonBuilder; + ///custom dropdown icon button widget final Widget dropDownButton; + ///custom dropdown button widget builder + final IconButtonBuilder dropdownButtonBuilder; + + ///whether to manage the clear and dropdown icons via InputDecoration suffixIcon + final bool suffixIcons; + ///If true, the dropdownBuilder will continue the uses of material behavior ///This will be useful if you want to handle a custom UI only if the item !=null final bool dropdownBuilderSupportsNullItem; @@ -182,7 +192,10 @@ class DropdownSearch extends StatefulWidget { this.autoFocusSearchBox = false, this.dialogMaxWidth, this.clearButton, + this.clearButtonBuilder, this.dropDownButton, + this.dropdownButtonBuilder, + this.suffixIcons = false, this.dropdownBuilderSupportsNullItem = false, this.popupShape, this.popupItemDisabled, @@ -244,7 +257,7 @@ class DropdownSearchState extends State> { : Text(_selectedItemAsString(data), style: Theme.of(context).textTheme.subtitle1), ), - _manageTrailingIcons(data), + if (!widget.suffixIcons) _manageTrailingIcons(data), ], ); } @@ -270,7 +283,7 @@ class DropdownSearchState extends State> { (widget.dropdownBuilder == null || widget.dropdownBuilderSupportsNullItem), isFocused: isFocused, - decoration: _manageDropdownDecoration(state), + decoration: _manageDropdownDecoration(state, value), child: _defaultSelectItemWidget(value), ); }); @@ -279,7 +292,7 @@ class DropdownSearchState extends State> { } ///manage dropdownSearch field decoration - InputDecoration _manageDropdownDecoration(FormFieldState state) { + InputDecoration _manageDropdownDecoration(FormFieldState state, T data) { return (widget.dropdownSearchDecoration ?? InputDecoration( contentPadding: EdgeInsets.fromLTRB(12, 12, 0, 0), @@ -289,6 +302,7 @@ class DropdownSearchState extends State> { enabled: widget.enabled, labelText: widget.label, hintText: widget.hint, + suffixIcon: widget.suffixIcons ? _manageTrailingIcons(data) : null, errorText: state.errorText); } @@ -305,20 +319,27 @@ class DropdownSearchState extends State> { ///function that manage Trailing icons(close, dropDown) Widget _manageTrailingIcons(T data) { + final clearButtonPressed = () => _handleOnChangeSelectedItem(null); + final dropdownButtonPressed = () => _selectSearchMode(data); + return Row( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.end, children: [ if (data != null && widget.showClearButton) - IconButton( - icon: widget.clearButton ?? const Icon(Icons.clear, size: 24), - onPressed: () => _handleOnChangeSelectedItem(null), - ), - IconButton( - icon: widget.dropDownButton ?? - const Icon(Icons.arrow_drop_down, size: 24), - onPressed: () => _selectSearchMode(data), - ), + widget.clearButtonBuilder != null + ? widget.clearButtonBuilder(context, clearButtonPressed) + : IconButton( + icon: widget.clearButton ?? const Icon(Icons.clear, size: 24), + onPressed: clearButtonPressed, + ), + widget.dropdownButtonBuilder != null + ? widget.dropdownButtonBuilder(context, dropdownButtonPressed) + : IconButton( + icon: widget.dropDownButton ?? + const Icon(Icons.arrow_drop_down, size: 24), + onPressed: dropdownButtonPressed, + ), ], ); } diff --git a/pubspec.yaml b/pubspec.yaml index c09dd3a..6a8b238 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: dropdown_search description: Simple and robust Searchable Dropdown with item search feature, making it possible to use an offline item list or filtering URL for easy customization. -version: 0.4.8 +version: 0.4.9 homepage: https://github.com/salim-lachdhaf git: https://github.com/salim-lachdhaf/searchable_dropdown repository: https://github.com/salim-lachdhaf/searchable_dropdown From e75b75c7defb352ad9061237c917091c83c67588 Mon Sep 17 00:00:00 2001 From: vasilich Date: Thu, 14 Jan 2021 16:30:46 +0200 Subject: [PATCH 2/8] wrap clearIconBuilder and dropdownIconBuilder in gesture detector --- example/lib/main.dart | 25 +++++++++++++++---------- lib/dropdown_search.dart | 16 +++++++++++++--- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index 81a8adc..5bb9fb6 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -70,15 +70,21 @@ class _MyHomePageState extends State { ), ), suffixIcons: true, - clearButtonBuilder: (_, onPressed) => IconButton( - icon: const Icon(Icons.clear, - size: 24, color: Colors.black), - onPressed: onPressed, + clearButtonBuilder: (_) => Padding( + padding: const EdgeInsets.all(8.0), + child: const Icon( + Icons.clear, + size: 24, + color: Colors.black, + ), ), - dropdownButtonBuilder: (_, onPressed) => IconButton( - icon: const Icon(Icons.arrow_drop_down, - size: 24, color: Colors.black), - onPressed: onPressed, + dropdownButtonBuilder: (_) => Padding( + padding: const EdgeInsets.all(8.0), + child: const Icon( + Icons.arrow_drop_down, + size: 24, + color: Colors.black, + ), ), showSelectedItem: true, items: [ @@ -230,8 +236,7 @@ class _MyHomePageState extends State { child: Text("Open dropdownSearch")), RaisedButton( onPressed: () { - _openDropDownProgKey.currentState - .changeSelectedItem("No"); + _openDropDownProgKey.currentState.clear(); }, child: Text("set to 'NO'")), Material( diff --git a/lib/dropdown_search.dart b/lib/dropdown_search.dart index e7eb92f..94805de 100644 --- a/lib/dropdown_search.dart +++ b/lib/dropdown_search.dart @@ -25,7 +25,7 @@ typedef Widget ErrorBuilder( BuildContext context, String searchEntry, dynamic exception); typedef Widget EmptyBuilder(BuildContext context, String searchEntry); typedef Widget LoadingBuilder(BuildContext context, String searchEntry); -typedef Widget IconButtonBuilder(BuildContext context, VoidCallback onPressed); +typedef Widget IconButtonBuilder(BuildContext context); enum Mode { DIALOG, BOTTOM_SHEET, MENU } @@ -328,13 +328,19 @@ class DropdownSearchState extends State> { children: [ if (data != null && widget.showClearButton) widget.clearButtonBuilder != null - ? widget.clearButtonBuilder(context, clearButtonPressed) + ? GestureDetector( + onTap: clearButtonPressed, + child: widget.clearButtonBuilder(context), + ) : IconButton( icon: widget.clearButton ?? const Icon(Icons.clear, size: 24), onPressed: clearButtonPressed, ), widget.dropdownButtonBuilder != null - ? widget.dropdownButtonBuilder(context, dropdownButtonPressed) + ? GestureDetector( + onTap: dropdownButtonPressed, + child: widget.dropdownButtonBuilder(context), + ) : IconButton( icon: widget.dropDownButton ?? const Icon(Icons.arrow_drop_down, size: 24), @@ -497,6 +503,10 @@ class DropdownSearchState extends State> { void changeSelectedItem(T selectedItem) => _handleOnChangeSelectedItem(selectedItem); + ///Change selected Value; this function is public USED to clear selected + ///value PROGRAMMATICALLY, Otherwise you can use [_handleOnChangeSelectedItem] + void clear() => _handleOnChangeSelectedItem(null); + ///get selected value programmatically T get getSelectedItem => _selectedItemNotifier.value; From bcc60dc70ac2595f2de1ef3a74c39859413834a1 Mon Sep 17 00:00:00 2001 From: vasilich Date: Thu, 14 Jan 2021 16:33:01 +0200 Subject: [PATCH 3/8] Revert "wrap clearIconBuilder and dropdownIconBuilder in gesture detector" This reverts commit cebc2d9f --- example/lib/main.dart | 25 ++++++++++--------------- lib/dropdown_search.dart | 16 +++------------- 2 files changed, 13 insertions(+), 28 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index 5bb9fb6..81a8adc 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -70,21 +70,15 @@ class _MyHomePageState extends State { ), ), suffixIcons: true, - clearButtonBuilder: (_) => Padding( - padding: const EdgeInsets.all(8.0), - child: const Icon( - Icons.clear, - size: 24, - color: Colors.black, - ), + clearButtonBuilder: (_, onPressed) => IconButton( + icon: const Icon(Icons.clear, + size: 24, color: Colors.black), + onPressed: onPressed, ), - dropdownButtonBuilder: (_) => Padding( - padding: const EdgeInsets.all(8.0), - child: const Icon( - Icons.arrow_drop_down, - size: 24, - color: Colors.black, - ), + dropdownButtonBuilder: (_, onPressed) => IconButton( + icon: const Icon(Icons.arrow_drop_down, + size: 24, color: Colors.black), + onPressed: onPressed, ), showSelectedItem: true, items: [ @@ -236,7 +230,8 @@ class _MyHomePageState extends State { child: Text("Open dropdownSearch")), RaisedButton( onPressed: () { - _openDropDownProgKey.currentState.clear(); + _openDropDownProgKey.currentState + .changeSelectedItem("No"); }, child: Text("set to 'NO'")), Material( diff --git a/lib/dropdown_search.dart b/lib/dropdown_search.dart index 94805de..e7eb92f 100644 --- a/lib/dropdown_search.dart +++ b/lib/dropdown_search.dart @@ -25,7 +25,7 @@ typedef Widget ErrorBuilder( BuildContext context, String searchEntry, dynamic exception); typedef Widget EmptyBuilder(BuildContext context, String searchEntry); typedef Widget LoadingBuilder(BuildContext context, String searchEntry); -typedef Widget IconButtonBuilder(BuildContext context); +typedef Widget IconButtonBuilder(BuildContext context, VoidCallback onPressed); enum Mode { DIALOG, BOTTOM_SHEET, MENU } @@ -328,19 +328,13 @@ class DropdownSearchState extends State> { children: [ if (data != null && widget.showClearButton) widget.clearButtonBuilder != null - ? GestureDetector( - onTap: clearButtonPressed, - child: widget.clearButtonBuilder(context), - ) + ? widget.clearButtonBuilder(context, clearButtonPressed) : IconButton( icon: widget.clearButton ?? const Icon(Icons.clear, size: 24), onPressed: clearButtonPressed, ), widget.dropdownButtonBuilder != null - ? GestureDetector( - onTap: dropdownButtonPressed, - child: widget.dropdownButtonBuilder(context), - ) + ? widget.dropdownButtonBuilder(context, dropdownButtonPressed) : IconButton( icon: widget.dropDownButton ?? const Icon(Icons.arrow_drop_down, size: 24), @@ -503,10 +497,6 @@ class DropdownSearchState extends State> { void changeSelectedItem(T selectedItem) => _handleOnChangeSelectedItem(selectedItem); - ///Change selected Value; this function is public USED to clear selected - ///value PROGRAMMATICALLY, Otherwise you can use [_handleOnChangeSelectedItem] - void clear() => _handleOnChangeSelectedItem(null); - ///get selected value programmatically T get getSelectedItem => _selectedItemNotifier.value; From c9c34163dd964f1e8247184e24dfc2c6a82c03b6 Mon Sep 17 00:00:00 2001 From: vasilich Date: Thu, 14 Jan 2021 16:37:31 +0200 Subject: [PATCH 4/8] add additional logic to clear button --- example/lib/main.dart | 36 +++++++++++++++++++++++++++++++----- lib/dropdown_search.dart | 4 ++++ 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index 81a8adc..e61818c 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -70,11 +70,37 @@ class _MyHomePageState extends State { ), ), suffixIcons: true, - clearButtonBuilder: (_, onPressed) => IconButton( - icon: const Icon(Icons.clear, - size: 24, color: Colors.black), - onPressed: onPressed, - ), + clearButtonBuilder: (_, onPressed) { + // set up the button + Widget okButton = FlatButton( + child: Text("OK"), + onPressed: () { + onPressed(); + Navigator.of(context).pop(); + }, + ); + + // set up the AlertDialog + AlertDialog alert = AlertDialog( + title: Text("Are you sure..."), + content: Text("...you want to clear the selection"), + actions: [ + okButton, + ], + ); + + return IconButton( + icon: const Icon(Icons.clear, + size: 24, color: Colors.black), + onPressed: () => // show the dialog + showDialog( + context: context, + builder: (BuildContext context) { + return alert; + }, + ), + ); + }, dropdownButtonBuilder: (_, onPressed) => IconButton( icon: const Icon(Icons.arrow_drop_down, size: 24, color: Colors.black), diff --git a/lib/dropdown_search.dart b/lib/dropdown_search.dart index e7eb92f..4ad15c4 100644 --- a/lib/dropdown_search.dart +++ b/lib/dropdown_search.dart @@ -497,6 +497,10 @@ class DropdownSearchState extends State> { void changeSelectedItem(T selectedItem) => _handleOnChangeSelectedItem(selectedItem); + ///Change selected Value; this function is public USED to clear selected + ///value PROGRAMMATICALLY, Otherwise you can use [_handleOnChangeSelectedItem] + void clear() => _handleOnChangeSelectedItem(null); + ///get selected value programmatically T get getSelectedItem => _selectedItemNotifier.value; From 298f499930ccf575ab51b5382ea10c7dcffd9c6a Mon Sep 17 00:00:00 2001 From: vasilich Date: Fri, 15 Jan 2021 10:56:29 +0200 Subject: [PATCH 5/8] suffixIcons => showAsSuffixIcons --- example/lib/main.dart | 2 +- lib/dropdown_search.dart | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index e61818c..d249464 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -69,7 +69,7 @@ class _MyHomePageState extends State { borderSide: BorderSide(color: Color(0xFF01689A)), ), ), - suffixIcons: true, + showAsSuffixIcons: true, clearButtonBuilder: (_, onPressed) { // set up the button Widget okButton = FlatButton( diff --git a/lib/dropdown_search.dart b/lib/dropdown_search.dart index 4ad15c4..0618d63 100644 --- a/lib/dropdown_search.dart +++ b/lib/dropdown_search.dart @@ -136,7 +136,7 @@ class DropdownSearch extends StatefulWidget { final IconButtonBuilder dropdownButtonBuilder; ///whether to manage the clear and dropdown icons via InputDecoration suffixIcon - final bool suffixIcons; + final bool showAsSuffixIcons; ///If true, the dropdownBuilder will continue the uses of material behavior ///This will be useful if you want to handle a custom UI only if the item !=null @@ -195,7 +195,7 @@ class DropdownSearch extends StatefulWidget { this.clearButtonBuilder, this.dropDownButton, this.dropdownButtonBuilder, - this.suffixIcons = false, + this.showAsSuffixIcons = false, this.dropdownBuilderSupportsNullItem = false, this.popupShape, this.popupItemDisabled, @@ -257,7 +257,7 @@ class DropdownSearchState extends State> { : Text(_selectedItemAsString(data), style: Theme.of(context).textTheme.subtitle1), ), - if (!widget.suffixIcons) _manageTrailingIcons(data), + if (!widget.showAsSuffixIcons) _manageTrailingIcons(data), ], ); } @@ -302,7 +302,8 @@ class DropdownSearchState extends State> { enabled: widget.enabled, labelText: widget.label, hintText: widget.hint, - suffixIcon: widget.suffixIcons ? _manageTrailingIcons(data) : null, + suffixIcon: + widget.showAsSuffixIcons ? _manageTrailingIcons(data) : null, errorText: state.errorText); } From 4eb267b2ae23b79df423804dcdec7297e42af642 Mon Sep 17 00:00:00 2001 From: vasilich Date: Fri, 15 Jan 2021 19:43:58 +0200 Subject: [PATCH 6/8] Revert "Revert "wrap clearIconBuilder and dropdownIconBuilder in gesture detector"" This reverts commit ca884ae2 --- example/lib/main.dart | 50 ++++++++++++---------------------------- lib/dropdown_search.dart | 12 +++++++--- 2 files changed, 24 insertions(+), 38 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index d249464..eb6fc50 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -70,41 +70,21 @@ class _MyHomePageState extends State { ), ), showAsSuffixIcons: true, - clearButtonBuilder: (_, onPressed) { - // set up the button - Widget okButton = FlatButton( - child: Text("OK"), - onPressed: () { - onPressed(); - Navigator.of(context).pop(); - }, - ); - - // set up the AlertDialog - AlertDialog alert = AlertDialog( - title: Text("Are you sure..."), - content: Text("...you want to clear the selection"), - actions: [ - okButton, - ], - ); - - return IconButton( - icon: const Icon(Icons.clear, - size: 24, color: Colors.black), - onPressed: () => // show the dialog - showDialog( - context: context, - builder: (BuildContext context) { - return alert; - }, - ), - ); - }, - dropdownButtonBuilder: (_, onPressed) => IconButton( - icon: const Icon(Icons.arrow_drop_down, - size: 24, color: Colors.black), - onPressed: onPressed, + clearButtonBuilder: (_) => Padding( + padding: const EdgeInsets.all(8.0), + child: const Icon( + Icons.clear, + size: 24, + color: Colors.black, + ), + ), + dropdownButtonBuilder: (_) => Padding( + padding: const EdgeInsets.all(8.0), + child: const Icon( + Icons.arrow_drop_down, + size: 24, + color: Colors.black, + ), ), showSelectedItem: true, items: [ diff --git a/lib/dropdown_search.dart b/lib/dropdown_search.dart index 0618d63..71b8356 100644 --- a/lib/dropdown_search.dart +++ b/lib/dropdown_search.dart @@ -25,7 +25,7 @@ typedef Widget ErrorBuilder( BuildContext context, String searchEntry, dynamic exception); typedef Widget EmptyBuilder(BuildContext context, String searchEntry); typedef Widget LoadingBuilder(BuildContext context, String searchEntry); -typedef Widget IconButtonBuilder(BuildContext context, VoidCallback onPressed); +typedef Widget IconButtonBuilder(BuildContext context); enum Mode { DIALOG, BOTTOM_SHEET, MENU } @@ -329,13 +329,19 @@ class DropdownSearchState extends State> { children: [ if (data != null && widget.showClearButton) widget.clearButtonBuilder != null - ? widget.clearButtonBuilder(context, clearButtonPressed) + ? GestureDetector( + onTap: clearButtonPressed, + child: widget.clearButtonBuilder(context), + ) : IconButton( icon: widget.clearButton ?? const Icon(Icons.clear, size: 24), onPressed: clearButtonPressed, ), widget.dropdownButtonBuilder != null - ? widget.dropdownButtonBuilder(context, dropdownButtonPressed) + ? GestureDetector( + onTap: dropdownButtonPressed, + child: widget.dropdownButtonBuilder(context), + ) : IconButton( icon: widget.dropDownButton ?? const Icon(Icons.arrow_drop_down, size: 24), From bb58b39fcd839a5b8eb76c66002628225b42d827 Mon Sep 17 00:00:00 2001 From: vasilich Date: Fri, 15 Jan 2021 20:06:21 +0200 Subject: [PATCH 7/8] onBeforeChange callback --- example/lib/main.dart | 26 ++++++++++++++++++++++++++ lib/dropdown_search.dart | 24 ++++++++++++++++++++++-- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index eb6fc50..fce6d54 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -51,6 +51,32 @@ class _MyHomePageState extends State { onChanged: print, popupItemDisabled: (String s) => s.startsWith('I'), selectedItem: "Tunisia", + onBeforeChange: (a, b) { + AlertDialog alert = AlertDialog( + title: Text("Are you sure..."), + content: Text("...you want to clear the selection"), + actions: [ + FlatButton( + child: Text("OK"), + onPressed: () { + Navigator.of(context).pop(true); + }, + ), + FlatButton( + child: Text("NOT OK"), + onPressed: () { + Navigator.of(context).pop(false); + }, + ), + ], + ); + + return showDialog( + context: context, + builder: (BuildContext context) { + return alert; + }); + }, ), Divider(), diff --git a/lib/dropdown_search.dart b/lib/dropdown_search.dart index 71b8356..5d7127f 100644 --- a/lib/dropdown_search.dart +++ b/lib/dropdown_search.dart @@ -26,6 +26,7 @@ typedef Widget ErrorBuilder( typedef Widget EmptyBuilder(BuildContext context, String searchEntry); typedef Widget LoadingBuilder(BuildContext context, String searchEntry); typedef Widget IconButtonBuilder(BuildContext context); +typedef Future BeforeChange(T prevItem, T nextItem); enum Mode { DIALOG, BOTTOM_SHEET, MENU } @@ -159,6 +160,9 @@ class DropdownSearch extends StatefulWidget { ///if you do not use online search final Duration searchDelay; + /// + final BeforeChange onBeforeChange; + DropdownSearch({ Key key, this.onSaved, @@ -203,6 +207,7 @@ class DropdownSearch extends StatefulWidget { this.onPopupDismissed, this.searchBoxController, this.searchDelay, + this.onBeforeChange, }) : assert(isFilteredOnline != null), assert(dropdownBuilderSupportsNullItem != null), assert(enabled != null), @@ -465,8 +470,23 @@ class DropdownSearchState extends State> { ///handle on change value , if the validation is active , we validate the new selected item void _handleOnChangeSelectedItem(T selectedItem) { - _selectedItemNotifier.value = selectedItem; - if (widget.onChanged != null) widget.onChanged(selectedItem); + final changeItem = () { + _selectedItemNotifier.value = selectedItem; + if (widget.onChanged != null) widget.onChanged(selectedItem); + }; + + if (widget.onBeforeChange != null) { + widget + .onBeforeChange(_selectedItemNotifier.value, selectedItem) + .then((value) { + if (value == true) { + changeItem(); + } + }); + } else { + changeItem(); + } + _handleFocus(false); } From 7f7eee6c089051cbd102c975a11d984e47a83af3 Mon Sep 17 00:00:00 2001 From: vasilich Date: Fri, 15 Jan 2021 20:54:51 +0200 Subject: [PATCH 8/8] readme update --- README.md | 3 +++ lib/dropdown_search.dart | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a8a7644..c903882 100644 --- a/README.md +++ b/README.md @@ -183,6 +183,9 @@ You can customize the layout of the DropdownSearch and its items. [EXAMPLE](http |`popupItemDisabled`|defines if an item of the popup is enabled or not, if the item is disabled, it cannot be clicked| |`popupBarrierColor`|set a custom color for the popup barrier| |`searchBoxController`|search box controller| +|`clearButtonBuilder`|custom clear button builder| +|`dropdownButtonBuilder`|custom dropdown button builder| +|`onBeforeChange`|callback executed before applying value change| |`searchDelay`|delay before searching| # Attention diff --git a/lib/dropdown_search.dart b/lib/dropdown_search.dart index 5d7127f..ae72744 100644 --- a/lib/dropdown_search.dart +++ b/lib/dropdown_search.dart @@ -156,11 +156,12 @@ class DropdownSearch extends StatefulWidget { ///called when popup is dismissed final VoidCallback onPopupDismissed; + /// callback executed before applying value change ///delay before searching, change it to Duration(milliseconds: 0) ///if you do not use online search final Duration searchDelay; - /// + /// callback executed before applying value change final BeforeChange onBeforeChange; DropdownSearch({