Skip to content

Commit 468b265

Browse files
committed
Input chips now working - requires overlay
1 parent ee94c78 commit 468b265

File tree

6 files changed

+337
-229
lines changed

6 files changed

+337
-229
lines changed

.idea/workspace.xml

Lines changed: 160 additions & 143 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

example/lib/main.dart

Lines changed: 134 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,51 @@ class MyApp extends StatelessWidget {
1616
}
1717
}
1818

19+
class AppProfile {
20+
final String name;
21+
final String email;
22+
final String imageUrl;
23+
24+
const AppProfile(this.name, this.email, this.imageUrl);
25+
26+
@override
27+
bool operator ==(Object other) =>
28+
identical(this, other) ||
29+
other is AppProfile &&
30+
runtimeType == other.runtimeType &&
31+
name == other.name;
32+
33+
@override
34+
int get hashCode => name.hashCode;
35+
36+
@override
37+
String toString() {
38+
return 'Profile{$name}';
39+
}
40+
}
41+
1942
class MyHomePage extends StatelessWidget {
2043
@override
2144
Widget build(BuildContext context) {
45+
const mockResults = <AppProfile>[
46+
AppProfile('Stock Man', '[email protected]',
47+
'https://d2gg9evh47fn9z.cloudfront.net/800px_COLOURBOX4057996.jpg'),
48+
AppProfile('Paul', '[email protected]',
49+
'https://mbtskoudsalg.com/images/person-stock-image-png.png'),
50+
AppProfile('Fred', '[email protected]',
51+
'https://media.istockphoto.com/photos/feeling-great-about-my-corporate-choices-picture-id507296326'),
52+
AppProfile('Bera', '[email protected]',
53+
'https://upload.wikimedia.org/wikipedia/commons/7/7c/Profile_avatar_placeholder_large.png'),
54+
AppProfile('John', '[email protected]',
55+
'https://upload.wikimedia.org/wikipedia/commons/7/7c/Profile_avatar_placeholder_large.png'),
56+
AppProfile('Thomas', '[email protected]',
57+
'https://upload.wikimedia.org/wikipedia/commons/7/7c/Profile_avatar_placeholder_large.png'),
58+
AppProfile('Norbert', '[email protected]',
59+
'https://upload.wikimedia.org/wikipedia/commons/7/7c/Profile_avatar_placeholder_large.png'),
60+
AppProfile('Marina', '[email protected]',
61+
'https://upload.wikimedia.org/wikipedia/commons/7/7c/Profile_avatar_placeholder_large.png'),
62+
];
63+
2264
return Scaffold(
2365
appBar: AppBar(
2466
title: Text('Flutter FormBuilder Example'),
@@ -27,10 +69,47 @@ class MyHomePage extends StatelessWidget {
2769
margin: EdgeInsets.all(15.0),
2870
child: FormBuilder(
2971
context,
30-
// autovalidate: true,
72+
autovalidate: true,
3173
// showResetButton: true,
3274
// resetButtonContent: Text("Clear Form"),
3375
controls: [
76+
FormBuilderInput.chipsInput(
77+
label: 'Test',
78+
attribute: 'chips_test',
79+
require: true,
80+
suggestionsCallback: (String query) {
81+
if (query.length != 0) {
82+
return mockResults.where((profile) {
83+
return profile.name.contains(query) ||
84+
profile.email.contains(query);
85+
}).toList(growable: false);
86+
} else {
87+
return const <AppProfile>[];
88+
}
89+
},
90+
chipBuilder: (context, state, profile) {
91+
return InputChip(
92+
key: ObjectKey(profile),
93+
label: Text(profile.name),
94+
avatar: CircleAvatar(
95+
backgroundImage: NetworkImage(profile.imageUrl),
96+
),
97+
onDeleted: () => state.deleteChip(profile),
98+
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
99+
);
100+
},
101+
suggestionBuilder: (context, state, profile) {
102+
return ListTile(
103+
key: ObjectKey(profile),
104+
leading: CircleAvatar(
105+
backgroundImage: NetworkImage(profile.imageUrl),
106+
),
107+
title: Text(profile.name),
108+
subtitle: Text(profile.email),
109+
onTap: () => state.selectSuggestion(profile),
110+
);
111+
},
112+
),
34113
FormBuilderInput.textField(
35114
type: FormBuilderInput.TYPE_TEXT,
36115
attribute: "name",
@@ -68,12 +147,12 @@ class MyHomePage extends StatelessWidget {
68147
require: true,
69148
),
70149
FormBuilderInput.textField(
71-
type: FormBuilderInput.TYPE_PHONE,
72-
attribute: "phone",
73-
label: "Phone",
74-
hint: "Including country code (+254)"
75-
//require: true,
76-
),
150+
type: FormBuilderInput.TYPE_PHONE,
151+
attribute: "phone",
152+
label: "Phone",
153+
hint: "Including country code (+254)"
154+
//require: true,
155+
),
77156
FormBuilderInput.password(
78157
attribute: "password",
79158
label: "Password",
@@ -114,23 +193,21 @@ class MyHomePage extends StatelessWidget {
114193
],
115194
),
116195
FormBuilderInput.checkbox(
117-
label: "I accept the terms and conditions",
118-
attribute: "accept_terms",
119-
hint: "Kindly make sure you've read all the terms and conditions",
120-
validator: (value){
121-
if(!value)
122-
return "Accept terms to continue";
123-
}
124-
),
196+
label: "I accept the terms and conditions",
197+
attribute: "accept_terms",
198+
hint:
199+
"Kindly make sure you've read all the terms and conditions",
200+
validator: (value) {
201+
if (!value) return "Accept terms to continue";
202+
}),
125203
FormBuilderInput.switchInput(
126204
label: "I accept the terms and conditions",
127205
attribute: "accept_terms_switch",
128-
hint: "Kindly make sure you've read all the terms and conditions",
129-
validator: (value){
130-
if(!value)
131-
return "Accept terms to continue";
132-
}
133-
),
206+
hint:
207+
"Kindly make sure you've read all the terms and conditions",
208+
validator: (value) {
209+
if (!value) return "Accept terms to continue";
210+
}),
134211
FormBuilderInput.slider(
135212
label: "Slider",
136213
attribute: "slider",
@@ -157,41 +234,42 @@ class MyHomePage extends StatelessWidget {
157234
hint: "Hint",
158235
),
159236
FormBuilderInput.segmentedControl(
160-
label: "Movie Rating (Archer)",
161-
attribute: "movie_rating",
162-
require: true,
163-
options: [
164-
FormBuilderInputOption(
165-
value: 1,
166-
),
167-
FormBuilderInputOption(
168-
value: 2,
169-
),
170-
FormBuilderInputOption(
171-
value: 3,
172-
),
173-
FormBuilderInputOption(
174-
value: 4,
175-
),
176-
FormBuilderInputOption(
177-
value: 5,
178-
),
179-
FormBuilderInputOption(
180-
value: 6,
181-
),
182-
FormBuilderInputOption(
183-
value: 7,
184-
),
185-
FormBuilderInputOption(
186-
value: 8,
187-
),
188-
FormBuilderInputOption(
189-
value: 9,
190-
),
191-
FormBuilderInputOption(
192-
value: 10,
193-
),
194-
]),
237+
label: "Movie Rating (Archer)",
238+
attribute: "movie_rating",
239+
require: true,
240+
options: [
241+
FormBuilderInputOption(
242+
value: 1,
243+
),
244+
FormBuilderInputOption(
245+
value: 2,
246+
),
247+
FormBuilderInputOption(
248+
value: 3,
249+
),
250+
FormBuilderInputOption(
251+
value: 4,
252+
),
253+
FormBuilderInputOption(
254+
value: 5,
255+
),
256+
FormBuilderInputOption(
257+
value: 6,
258+
),
259+
FormBuilderInputOption(
260+
value: 7,
261+
),
262+
FormBuilderInputOption(
263+
value: 8,
264+
),
265+
FormBuilderInputOption(
266+
value: 9,
267+
),
268+
FormBuilderInputOption(
269+
value: 10,
270+
),
271+
],
272+
),
195273
],
196274
onChanged: () {
197275
print("Form value changed");

lib/flutter_form_builder.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
library flutter_form_builder;
22

3+
export './src/chips_input.dart';
34
export './src/form_builder.dart';
45
export './src/form_builder_input.dart';
56
export './src/form_builder_input_option.dart';

lib/src/chips_input.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import 'dart:async';
22
import 'package:flutter/material.dart';
33
import 'package:flutter/services.dart';
44

5-
typedef ChipsInputSuggestions<T> = Future<List<T>> Function(String query);
5+
typedef ChipsInputSuggestions<T> = FutureOr<List<T>> Function(String query);
66
typedef ChipSelected<T> = void Function(T data, bool selected);
77
typedef ChipsBuilder<T> = Widget Function(BuildContext context, ChipsInputState<T> state, T data);
88

lib/src/form_builder.dart

Lines changed: 35 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -622,36 +622,42 @@ class _FormBuilderState extends State<FormBuilder> {
622622
);
623623
}));
624624
break;
625-
/*case FormBuilderInput.TYPE_CHIPS_INPUT:
626-
formControlsList.add(ChipsInput(
627-
decoration: InputDecoration(prefixIcon: Icon(Icons.search), hintText: 'Profile search', labelText: ),
628-
// findSuggestions: _findSuggestions,
629-
// onChanged: _onChanged,
630-
chipBuilder: (BuildContext context, ChipsInputState<AppProfile> state, AppProfile profile) {
631-
return InputChip(
632-
key: ObjectKey(profile),
633-
label: Text(profile.name),
634-
avatar: CircleAvatar(
635-
backgroundImage: NetworkImage(profile.imageUrl),
636-
),
637-
onDeleted: () => state.deleteChip(profile),
638-
onSelected: (_) => _onChipTapped(profile),
639-
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
640-
);
641-
},
642-
suggestionBuilder: (BuildContext context, ChipsInputState<AppProfile> state, AppProfile profile) {
643-
return ListTile(
644-
key: ObjectKey(profile),
645-
leading: CircleAvatar(
646-
backgroundImage: NetworkImage(profile.imageUrl),
647-
),
648-
title: Text(profile.name),
649-
subtitle: Text(profile.email),
650-
onTap: () => state.selectSuggestion(profile),
651-
);
652-
},
625+
case FormBuilderInput.TYPE_CHIPS_INPUT:
626+
formControlsList.add(SizedBox(
627+
height: 200.0,
628+
child: FormField(
629+
initialValue: formControl.value ?? [],
630+
onSaved: (value) {
631+
formData[formControl.attribute] = value;
632+
},
633+
validator: (value) {
634+
if (formControl.require && value.length == 0)
635+
return "${formControl.label} is required";
636+
if (formControl.validator != null)
637+
return formControl.validator(value);
638+
},
639+
builder: (FormFieldState<dynamic> field) {
640+
return ChipsInput(
641+
decoration: InputDecoration(
642+
// prefixIcon: Icon(Icons.search),
643+
hintText: formControl.hint,
644+
labelText: formControl.label,
645+
errorText: field.errorText,
646+
),
647+
findSuggestions: formControl.suggestionsCallback,
648+
onChanged: (data) {
649+
setState(() {
650+
formControls[count].value = data;
651+
});
652+
field.didChange(data);
653+
},
654+
chipBuilder: formControl.chipBuilder,
655+
suggestionBuilder: formControl.suggestionBuilder,
656+
);
657+
},
658+
),
653659
));
654-
break;*/
660+
break;
655661
}
656662
}
657663

lib/src/form_builder_input.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
22
import 'package:flutter_typeahead/flutter_typeahead.dart';
33

44
import './form_builder_input_option.dart';
5+
import './chips_input.dart';
56

67
//TODO: Consider adding RangeSlider - https://pub.dartlang.org/packages/flutter_range_slider
78
//TODO: Consider adding ColorPicker - https://pub.dartlang.org/packages/flutter_colorpicker
@@ -48,6 +49,8 @@ class FormBuilderInput {
4849
List<FormBuilderInputOption> options;
4950
SuggestionsCallback suggestionsCallback;
5051
ItemBuilder itemBuilder;
52+
ChipsBuilder suggestionBuilder;
53+
ChipsBuilder chipBuilder;
5154

5255
FormBuilderInput.textField({
5356
@required this.label,
@@ -248,6 +251,9 @@ class FormBuilderInput {
248251
FormBuilderInput.chipsInput({
249252
@required this.label,
250253
@required this.attribute,
254+
@required this.suggestionsCallback,
255+
@required this.suggestionBuilder,
256+
@required this.chipBuilder,
251257
this.hint,
252258
this.value,
253259
this.require = false,

0 commit comments

Comments
 (0)