Skip to content

Commit 2a319cd

Browse files
committed
dependency fetch call on null issue fixed
1 parent 769245f commit 2a319cd

14 files changed

+160
-95
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,8 @@ initial release
55
* other searchFiled dependency
66
* fetch content on dependency search resoved
77
* select item from suggestion
8+
9+
# 0.0.2
10+
* null pointer exception issue fixed for dependencyFetch
11+
* added more description & improved pub points
12+
* case sensitive toggle field added for default filter

example/lib/main.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ class MyApp extends StatefulWidget {
1313
}
1414

1515
class _MyAppState extends State<MyApp> {
16-
1716
final _firstController = TextSearchFieldController();
1817

1918
@override
@@ -35,10 +34,12 @@ class _MyAppState extends State<MyApp> {
3534
filterItems: [
3635
TextSearchFieldDataModel(key: "hey", value: "hello"),
3736
TextSearchFieldDataModel(key: "hey", value: "bro"),
37+
TextSearchFieldDataModel(key: "hey", value: "HELLO"),
3838
TextSearchFieldDataModel(key: "hey", value: "how are"),
3939
TextSearchFieldDataModel(key: "hey", value: "hello"),
4040
TextSearchFieldDataModel(key: "hey", value: "bro"),
41-
TextSearchFieldDataModel(key: "hey", value: "how are")
41+
TextSearchFieldDataModel(key: "hey", value: "how are"),
42+
4243
],
4344
onSelected: (primarySelected, index, item) async {
4445
print("primary item selected: $primarySelected");

lib/src/Utils.dart

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
class Utils {
22
/// this function is going to build and return key from [text]
3-
static String buildKey(String text){
3+
static String buildKey(String text) {
44
return text.replaceAll(" ", "_");
55
}
66
}
7-
8-

lib/src/global_key.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ extension GlobalKeyExtension on GlobalKey {
1111
return null;
1212
}
1313
}
14-
}
14+
}

lib/src/text_search_field.dart

Lines changed: 125 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import 'package:text_search_field/src/text_search_field_data_model.dart';
55
import 'package:touch_ripple_effect/touch_ripple_effect.dart';
66
import 'global_key.dart';
77

8-
98
/// this is a search for searching or filtering item from list, server or network
109
/// [hint] is a string to show hint to user in textSearchField,
1110
/// using [inputBorder] you can change your textSearchField broder colors and style,
@@ -27,7 +26,7 @@ import 'global_key.dart';
2726
/// you can define height of suggestion item with [suggestionItemContainerHeight]
2827
/// you can add alignment of text with [suggestionTextAlignment]
2928
/// with [suggestionContainerHeight] you can define suggestion height
30-
///
29+
/// with [caseSensitive] you can enable and disable case sensitive of default query filter
3130
///
3231
class TextSearchField extends StatefulWidget {
3332
final String? hint;
@@ -39,29 +38,46 @@ class TextSearchField extends StatefulWidget {
3938
final bool fullTextSearch;
4039
final TextInputAction? textInputAction;
4140
final Future<List<TextSearchFieldDataModel>?> Function(String query)? fetch;
42-
final List<TextSearchFieldDataModel>? Function(List<TextSearchFieldDataModel>? filterItems, String query)? query;
41+
final List<TextSearchFieldDataModel>? Function(
42+
List<TextSearchFieldDataModel>? filterItems, String query)? query;
4343
final Color? rippleColor;
44-
final Future<void> Function(bool isPrimary, int index, TextSearchFieldDataModel selectedItem)? onSelected;
44+
final Future<void> Function(
45+
bool isPrimary, int index, TextSearchFieldDataModel selectedItem)?
46+
onSelected;
4547
final TextSearchFieldController? controller;
4648
final TextSearchFieldController? dependency;
47-
final Future<List<TextSearchFieldDataModel>> Function(TextSearchFieldDataModel modelItem)? dependencyFetch;
49+
final Future<List<TextSearchFieldDataModel>> Function(
50+
TextSearchFieldDataModel modelItem)? dependencyFetch;
4851
final TextStyle? suggestionTextStyle;
4952
final BoxDecoration? suggestionItemDecoration;
5053
final double suggestionItemContainerHeight;
5154
final Alignment? suggestionTextAlignment;
5255
final double suggestionContainerHeight;
56+
final bool caseSensitive;
5357
// constructor
5458
const TextSearchField({
55-
super.key, this.hint, this.inputBorder, this.searchFieldHintTextStyle,
56-
this.initialValue, this.filterItems, this.fetch, this.query, this.fullTextSearch = false,
57-
this.rippleColor, this.onSelected, this.controller, this.dependency, this.textInputAction,
59+
super.key,
60+
this.hint,
61+
this.inputBorder,
62+
this.searchFieldHintTextStyle,
63+
this.initialValue,
64+
this.filterItems,
65+
this.fetch,
66+
this.query,
67+
this.fullTextSearch = false,
68+
this.rippleColor,
69+
this.onSelected,
70+
this.controller,
71+
this.dependency,
72+
this.textInputAction,
5873
this.dependencyFetch,
5974
this.searchFieldTextStyle,
6075
this.suggestionTextStyle,
6176
this.suggestionItemDecoration,
6277
this.suggestionItemContainerHeight = 50,
6378
this.suggestionTextAlignment,
6479
this.suggestionContainerHeight = 250,
80+
this.caseSensitive = false
6581
});
6682

6783
@override
@@ -75,38 +91,38 @@ class _SearchFieldState extends State<TextSearchField> {
7591
TextSearchFieldDataModel? currentValue;
7692
bool isSelected = false;
7793

78-
final OverlayPortalController _overlayPortalController = OverlayPortalController();
94+
final OverlayPortalController _overlayPortalController =
95+
OverlayPortalController();
7996
final _controller = TextEditingController();
8097
final _globalKey = GlobalKey();
8198

82-
8399
@override
84100
void initState() {
85101
_focusNode = FocusNode();
86102
// setting up initial value if there any
87-
if(widget.initialValue != null){
103+
if (widget.initialValue != null) {
88104
setCurrentValue(widget.initialValue!);
89105
}
90106

91107
_focusNode.addListener(() {
92108
// showing and hiding search suggestion list
93-
if(_focusNode.hasFocus){
109+
if (_focusNode.hasFocus) {
94110
_overlayPortalController.show();
95-
}else {
111+
} else {
96112
_overlayPortalController.hide();
97113
}
98114
});
99115
items = widget.filterItems;
100-
if(widget.dependency != null){
116+
if (widget.dependency != null) {
101117
widget.dependency!.selected = (TextSearchFieldDataModel item) async {
102118
// enabling loader and search field
103119
setState(() {
104120
isLoading = true;
105121
isSelected = true;
106122
});
107123
// calling dependency fetch method
108-
if(widget.dependencyFetch != null){
109-
items = await widget.dependencyFetch!(item);
124+
if (widget.dependencyFetch != null) {
125+
items = await widget.dependencyFetch!(item);
110126
}
111127
// disabling loader after content fetch
112128
setState(() {
@@ -117,11 +133,11 @@ class _SearchFieldState extends State<TextSearchField> {
117133
super.initState();
118134
}
119135

120-
void setCurrentValue(TextSearchFieldDataModel value){
136+
void setCurrentValue(TextSearchFieldDataModel value) {
121137
currentValue = value;
122138
_controller.text = value.value!;
123-
if(widget.controller != null){
124-
if(widget.controller!.selected != null){
139+
if (widget.controller != null) {
140+
if (widget.controller!.selected != null) {
125141
widget.controller!.selected!(value);
126142
}
127143
}
@@ -137,78 +153,116 @@ class _SearchFieldState extends State<TextSearchField> {
137153
child: Container(
138154
alignment: Alignment.center,
139155
height: widget.suggestionContainerHeight,
140-
decoration: const BoxDecoration(color: Colors.white, boxShadow: [BoxShadow(color: Colors.grey, blurRadius: 15, offset: Offset(2, 3))]),
141-
child: isLoading ? const CircularProgressIndicator(): ListView.builder(
142-
itemCount: items != null ? items!.length : 0,
143-
scrollDirection: Axis.vertical,
144-
itemBuilder: (BuildContext context, int index) {
145-
return TouchRippleEffect(
146-
rippleColor: widget.rippleColor ?? Colors.grey,
147-
onTap: (){
148-
_focusNode.unfocus();
149-
setCurrentValue(items![index]);
150-
if(widget.onSelected != null){
151-
widget.onSelected!(index == 0 ? true: false, index, items![index]);
152-
}
153-
},
154-
child: Container(
155-
key: Key(items![index].key!),
156-
alignment:widget.suggestionTextAlignment ?? Alignment.center,
157-
height: widget.suggestionItemContainerHeight,
158-
decoration: widget.suggestionItemDecoration ?? const BoxDecoration(color: Colors.white, shape: BoxShape.rectangle, border: Border(bottom: BorderSide(color: Colors.grey, width: 0.5))),
159-
child: Text(items![index].value!, style: widget.suggestionTextStyle ?? const TextStyle(color: Colors.black87, fontWeight: FontWeight.bold, fontSize: 18), textDirection: TextDirection.ltr,),
160-
),
161-
);
162-
}),
156+
decoration: const BoxDecoration(color: Colors.white, boxShadow: [
157+
BoxShadow(color: Colors.grey, blurRadius: 15, offset: Offset(2, 3))
158+
]),
159+
child: isLoading
160+
? const CircularProgressIndicator()
161+
: ListView.builder(
162+
itemCount: items != null ? items!.length : 0,
163+
scrollDirection: Axis.vertical,
164+
itemBuilder: (BuildContext context, int index) {
165+
return TouchRippleEffect(
166+
rippleColor: widget.rippleColor ?? Colors.grey,
167+
onTap: () {
168+
_focusNode.unfocus();
169+
setCurrentValue(items![index]);
170+
if (widget.onSelected != null) {
171+
widget.onSelected!(
172+
index == 0 ? true : false, index, items![index]);
173+
}
174+
},
175+
child: Container(
176+
key: Key(items![index].key!),
177+
alignment:
178+
widget.suggestionTextAlignment ?? Alignment.center,
179+
height: widget.suggestionItemContainerHeight,
180+
decoration: widget.suggestionItemDecoration ??
181+
const BoxDecoration(
182+
color: Colors.white,
183+
shape: BoxShape.rectangle,
184+
border: Border(
185+
bottom: BorderSide(
186+
color: Colors.grey, width: 0.5))),
187+
child: Text(
188+
items![index].value!,
189+
style: widget.suggestionTextStyle ??
190+
const TextStyle(
191+
color: Colors.black87,
192+
fontWeight: FontWeight.bold,
193+
fontSize: 18),
194+
textDirection: TextDirection.ltr,
195+
),
196+
),
197+
);
198+
}),
163199
),
164200
),
165201
controller: _overlayPortalController,
166202
child: TextField(
167203
key: _globalKey,
168204
controller: _controller,
169205
enabled: widget.dependency == null ? true : isSelected,
170-
style: widget.searchFieldTextStyle ?? const TextStyle(color: Colors.black,fontSize: 18, fontWeight: FontWeight.w400),
206+
style: widget.searchFieldTextStyle ??
207+
const TextStyle(
208+
color: Colors.black, fontSize: 18, fontWeight: FontWeight.w400),
171209
focusNode: _focusNode,
172210
textInputAction: widget.textInputAction ?? TextInputAction.go,
173211
keyboardType: TextInputType.text,
174212
maxLines: 1,
175-
onEditingComplete: (){
213+
onEditingComplete: () {
176214
// on keyboard go button press we are treating this request as a initial / primary item selected in the list
177-
setCurrentValue(TextSearchFieldDataModel(key: Utils.buildKey(_controller.value.text), value: _controller.value.text));
215+
setCurrentValue(TextSearchFieldDataModel(
216+
key: Utils.buildKey(_controller.value.text),
217+
value: _controller.value.text));
178218
// triggering onSelected function
179-
if(widget.onSelected != null){
219+
if (widget.onSelected != null) {
180220
widget.onSelected!(true, 0, currentValue!);
181221
}
182222
// removing focus from search field
183223
_focusNode.unfocus();
184224
},
185-
onChanged: (String value) async{
225+
onChanged: (String value) async {
186226
// enabling loader
187227
setState(() {
188228
isLoading = true;
189229
});
190230

191-
if(widget.query != null){
231+
if (widget.query != null) {
192232
// if user wants to add custom filter on query item then this statement is going to trigger
193233
items = widget.query!(widget.filterItems, value);
194-
}else if(widget.fetch != null) {
234+
} else if (widget.fetch != null) {
195235
// if user wants to add server / network query filter then this statement is going to trigger
196236
final res = await widget.fetch!(value);
197-
if(res!.isNotEmpty){
237+
if (res!.isNotEmpty) {
198238
// if there is an item in the response then trigger this
199-
items = [TextSearchFieldDataModel(key: Utils.buildKey(value), value: value), ...res];
200-
}else {
239+
items = [
240+
TextSearchFieldDataModel(
241+
key: Utils.buildKey(value), value: value),
242+
...res
243+
];
244+
} else {
201245
// if there is no items in the response then add on default item
202-
items = [TextSearchFieldDataModel(key: Utils.buildKey(value), value: value)];
246+
items = [
247+
TextSearchFieldDataModel(
248+
key: Utils.buildKey(value), value: value)
249+
];
203250
}
204-
}else {
251+
} else {
205252
// if none of the function has defined then it's going to trigger as a default query filter function
206-
if(value.isNotEmpty){
253+
if (value.isNotEmpty) {
207254
String pattern = r"\b" + value + r"\b";
208-
RegExp wordRegex = RegExp(pattern, caseSensitive: false);
209-
final lists = widget.filterItems?.where((element) => element.value!.contains(widget.fullTextSearch ? wordRegex: value)).toList();
210-
items = [TextSearchFieldDataModel(key: Utils.buildKey(value), value: value), ...?lists];
211-
}else {
255+
RegExp wordRegex = RegExp(pattern, caseSensitive: widget.caseSensitive);
256+
final lists = widget.filterItems
257+
?.where((element) => element.value!
258+
.contains(widget.fullTextSearch ? wordRegex : RegExp(value, caseSensitive: widget.caseSensitive)))
259+
.toList();
260+
items = [
261+
TextSearchFieldDataModel(
262+
key: Utils.buildKey(value), value: value),
263+
...?lists
264+
];
265+
} else {
212266
// if query string is empty then add default filter list in the request
213267
items = widget.filterItems;
214268
}
@@ -218,18 +272,26 @@ class _SearchFieldState extends State<TextSearchField> {
218272
isLoading = false;
219273
});
220274
},
221-
onTap: (){
275+
onTap: () {
222276
// this function is going to trigger on select search field
223277
_focusNode.requestFocus();
224278
},
225279
decoration: InputDecoration(
226-
border: widget.inputBorder ?? const OutlineInputBorder(borderSide: BorderSide(color: Colors.black87, width: 1.0, style: BorderStyle.solid)),
280+
border: widget.inputBorder ??
281+
const OutlineInputBorder(
282+
borderSide: BorderSide(
283+
color: Colors.black87,
284+
width: 1.0,
285+
style: BorderStyle.solid)),
227286
hintText: widget.hint ?? "please type your query",
228-
hintStyle: widget.searchFieldHintTextStyle ?? const TextStyle(color: Colors.grey, fontSize: 16, fontWeight: FontWeight.w400),
287+
hintStyle: widget.searchFieldHintTextStyle ??
288+
const TextStyle(
289+
color: Colors.grey,
290+
fontSize: 16,
291+
fontWeight: FontWeight.w400),
229292
),
230293
textDirection: TextDirection.ltr,
231294
),
232295
);
233296
}
234297
}
235-
Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
21
import 'package:text_search_field/src/text_search_field_data_model.dart';
32

43
class TextSearchFieldController {
54
String text;
65
bool isLoading;
76
void Function(TextSearchFieldDataModel model)? selected;
8-
TextSearchFieldController({this.text ="", this.isLoading = false, this.selected});
7+
TextSearchFieldController(
8+
{this.text = "", this.isLoading = false, this.selected});
99
}
10-
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
class TextSearchFieldDataModel{
1+
class TextSearchFieldDataModel {
22
String? key;
33
String? value;
44
TextSearchFieldDataModel({this.key, this.value});
55
}
6-

lib/text_search_field.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
export 'src/text_search_field.dart';
22
export 'src/text_search_field_data_model.dart';
3-
export 'src/text_search_field_controller.dart';
3+
export 'src/text_search_field_controller.dart';

0 commit comments

Comments
 (0)