Skip to content

Commit 70ce0af

Browse files
committed
Fixes to Country and Phone fields to be consistent with package; Also added pedantic for static analysis
1 parent 5963a13 commit 70ce0af

9 files changed

+510
-317
lines changed

analysis_options.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
include: package:pedantic/analysis_options.yaml

example/.flutter-plugins-dependencies

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"_info":"// This is a generated file; do not edit or check into version control.","dependencyGraph":[{"name":"flutter_keyboard_visibility","dependencies":[]},{"name":"flutter_plugin_android_lifecycle","dependencies":[]},{"name":"image_picker","dependencies":["flutter_plugin_android_lifecycle"]}]}
1+
{"_info":"// This is a generated file; do not edit or check into version control.","dependencyGraph":[{"name":"flutter_keyboard_visibility","dependencies":[]},{"name":"flutter_plugin_android_lifecycle","dependencies":[]},{"name":"image_picker","dependencies":["flutter_plugin_android_lifecycle"]},{"name":"phone_number","dependencies":[]}]}

example/lib/main.dart

Lines changed: 85 additions & 51 deletions
Large diffs are not rendered by default.

lib/flutter_form_builder.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export './src/fields/form_builder_chips_choice.dart';
88
export './src/fields/form_builder_chips_filter.dart';
99
export './src/fields/form_builder_chips_input.dart';
1010
export './src/fields/form_builder_color_picker.dart';
11+
export './src/fields/form_builder_country_picker.dart';
1112
export './src/fields/form_builder_date_range_picker.dart';
1213
export './src/fields/form_builder_date_time_picker.dart';
1314
export './src/fields/form_builder_dropdown.dart';

lib/src/country_picker_util.dart

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import 'package:country_pickers/countries.dart';
2+
import 'package:country_pickers/country.dart';
3+
4+
class CountryPickerUtil {
5+
static Country getCountryByIso3Code(String iso3Code) {
6+
try {
7+
return countryList.firstWhere(
8+
(country) => country.iso3Code.toLowerCase() == iso3Code.toLowerCase(),
9+
);
10+
} catch (error) {
11+
return null;
12+
}
13+
}
14+
15+
static Country getCountryByIsoCode(String isoCode) {
16+
try {
17+
return countryList.firstWhere(
18+
(country) => country.isoCode.toLowerCase() == isoCode.toLowerCase(),
19+
);
20+
} catch (error) {
21+
return null;
22+
}
23+
}
24+
25+
static Country getCountryByName(String name) {
26+
try {
27+
return countryList.firstWhere(
28+
(country) => country.name.toLowerCase() == name.toLowerCase(),
29+
);
30+
} catch (error) {
31+
return null;
32+
}
33+
}
34+
35+
static Country getCountryByPhoneCode(String phoneCode) {
36+
try {
37+
return countryList.firstWhere(
38+
(country) => country.phoneCode.toLowerCase() == phoneCode.toLowerCase(),
39+
);
40+
} catch (error) {
41+
return null;
42+
}
43+
}
44+
45+
static Country getCountryByCodeOrName(String codeOrName) {
46+
var country;
47+
country = getCountryByIso3Code(codeOrName);
48+
if (country != null) return country;
49+
country = getCountryByIsoCode(codeOrName);
50+
if (country != null) return country;
51+
country = getCountryByName(codeOrName);
52+
if (country != null) return country;
53+
country = getCountryByPhoneCode(codeOrName);
54+
if (country != null) return country;
55+
return country;
56+
}
57+
}

lib/src/fields/form_builder_country_picker.dart

Lines changed: 123 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import 'package:flutter/cupertino.dart';
44
import 'package:flutter/material.dart';
55
import 'package:flutter/widgets.dart';
66
import 'package:flutter_form_builder/flutter_form_builder.dart';
7+
import 'package:flutter_form_builder/src/country_picker_util.dart';
78

89
class FormBuilderCountryPicker extends StatefulWidget {
910
final String attribute;
@@ -21,6 +22,7 @@ class FormBuilderCountryPicker extends StatefulWidget {
2122
final EdgeInsets titlePadding;
2223
final bool isSearchable;
2324
final Text dialogTitle;
25+
final String initialValue;
2426
final String defaultSelectedCountryIsoCode;
2527
final List<String> priorityListByIsoCode;
2628
final List<String> countryFilterByIsoCode;
@@ -29,154 +31,105 @@ class FormBuilderCountryPicker extends StatefulWidget {
2931
final double cupertinoPickerSheetHeight;
3032
final Color cursorColor;
3133

32-
FormBuilderCountryPicker(
33-
{Key key,
34-
@required this.attribute,
35-
this.validators = const [],
36-
this.readOnly = false,
37-
this.decoration = const InputDecoration(),
38-
this.style,
39-
this.onChanged,
40-
this.valueTransformer,
41-
this.onSaved,
42-
this.searchText,
43-
this.titlePadding,
44-
this.dialogTitle,
45-
this.isSearchable,
46-
@required this.defaultSelectedCountryIsoCode,
47-
this.priorityListByIsoCode,
48-
this.countryFilterByIsoCode,
49-
this.dialogTextStyle,
50-
this.isCupertinoPicker,
51-
this.cupertinoPickerSheetHeight,
52-
this.cursorColor})
53-
: assert(defaultSelectedCountryIsoCode != null),
34+
FormBuilderCountryPicker({
35+
Key key,
36+
@required this.attribute,
37+
this.defaultSelectedCountryIsoCode = "US",
38+
this.initialValue,
39+
this.validators = const [],
40+
this.readOnly = false,
41+
this.decoration = const InputDecoration(),
42+
this.style,
43+
this.onChanged,
44+
this.valueTransformer,
45+
this.onSaved,
46+
this.searchText,
47+
this.titlePadding,
48+
this.dialogTitle,
49+
this.isSearchable,
50+
this.priorityListByIsoCode,
51+
this.countryFilterByIsoCode,
52+
this.dialogTextStyle,
53+
this.isCupertinoPicker = false,
54+
this.cupertinoPickerSheetHeight,
55+
this.cursorColor,
56+
}) : assert(initialValue != null),
5457
super(key: key);
5558

5659
@override
57-
_FormBuilderCountryPickerState createState() => _FormBuilderCountryPickerState();
60+
_FormBuilderCountryPickerState createState() =>
61+
_FormBuilderCountryPickerState();
5862
}
5963

6064
class _FormBuilderCountryPickerState extends State<FormBuilderCountryPicker> {
6165
bool _readOnly = false;
6266
final GlobalKey<FormFieldState> _fieldKey = GlobalKey<FormFieldState>();
6367
FormBuilderState _formState;
64-
Country _selectedDialogCountry;
65-
66-
void _openCupertinoCountryPicker() => showCupertinoModalPopup<void>(
67-
context: context,
68-
builder: (BuildContext context) {
69-
return CountryPickerCupertino(
70-
pickerSheetHeight: widget.cupertinoPickerSheetHeight ?? 300.0,
71-
onValuePicked: (Country country) => setState(() => _selectedDialogCountry = country),
72-
itemFilter: widget.countryFilterByIsoCode != null
73-
? (c) => widget.countryFilterByIsoCode.contains(c.isoCode)
74-
: null,
75-
priorityList: widget.priorityListByIsoCode != null
76-
? List.generate(widget.priorityListByIsoCode.length,
77-
(index) => CountryPickerUtils.getCountryByIsoCode(widget.priorityListByIsoCode[index]))
78-
: null,
79-
);
80-
},
81-
);
82-
83-
void _openCountryPickerDialog() => showDialog(
84-
context: context,
85-
builder: (context) => Theme(
86-
data: Theme.of(context).copyWith(
87-
cursorColor: Theme.of(context).primaryColor,
88-
primaryColor: widget.cursorColor ?? Theme.of(context).primaryColor,
89-
),
90-
child: CountryPickerDialog(
91-
titlePadding: widget.titlePadding ?? EdgeInsets.all(8.0),
92-
searchCursorColor: widget.cursorColor ?? Theme.of(context).primaryColor,
93-
searchInputDecoration: InputDecoration(hintText: widget.searchText ?? 'Search...'),
94-
isSearchable: widget.isSearchable ?? true,
95-
title: widget.dialogTitle ??
96-
Text(
97-
'Select Your Country',
98-
style: widget.dialogTextStyle ?? widget.style,
99-
),
100-
onValuePicked: (Country country) => setState(() => _selectedDialogCountry = country),
101-
itemFilter: widget.countryFilterByIsoCode != null
102-
? (c) => widget.countryFilterByIsoCode.contains(c.isoCode)
103-
: null,
104-
priorityList: widget.priorityListByIsoCode != null
105-
? List.generate(widget.priorityListByIsoCode.length,
106-
(index) => CountryPickerUtils.getCountryByIsoCode(widget.priorityListByIsoCode[index]))
107-
: null,
108-
itemBuilder: _buildDialogItem,
109-
),
110-
),
111-
);
112-
113-
Widget _buildDialogItem(Country country) => Container(
114-
child: ListTile(
115-
contentPadding: EdgeInsets.zero,
116-
leading: CountryPickerUtils.getDefaultFlagImage(country),
117-
title: Text("${country.name}"),
118-
visualDensity: VisualDensity.compact,
119-
),
120-
);
68+
Country _initialValue;
12169

12270
@override
12371
void initState() {
12472
_formState = FormBuilder.of(context);
12573
_formState?.registerFieldKey(widget.attribute, _fieldKey);
126-
_selectedDialogCountry = CountryPickerUtils.getCountryByIsoCode(widget.defaultSelectedCountryIsoCode);
127-
74+
_initialValue =
75+
CountryPickerUtil.getCountryByCodeOrName(widget.initialValue) ??
76+
CountryPickerUtil.getCountryByIsoCode(
77+
widget.defaultSelectedCountryIsoCode);
12878
super.initState();
12979
}
13080

131-
@override
132-
void dispose() {
133-
_formState?.unregisterFieldKey(widget.attribute);
134-
super.dispose();
135-
}
136-
13781
@override
13882
Widget build(BuildContext context) {
13983
_readOnly = (_formState?.readOnly == true) ? true : widget.readOnly;
14084

14185
return FormField<Country>(
14286
key: _fieldKey,
14387
enabled: !_readOnly,
144-
initialValue: CountryPickerUtils.getCountryByIsoCode(widget.defaultSelectedCountryIsoCode),
88+
initialValue: _initialValue,
14589
validator: (val) {
14690
for (int i = 0; i < widget.validators.length; i++) {
147-
if (widget.validators[i](val) != null) return widget.validators[i](val);
91+
if (widget.validators[i](val) != null) {
92+
return widget.validators[i](val);
93+
}
14894
}
14995
return null;
15096
},
15197
onSaved: (val) {
15298
dynamic transformed;
15399
if (widget.valueTransformer != null) {
154-
transformed = widget.valueTransformer(_selectedDialogCountry);
100+
transformed = widget.valueTransformer(val);
155101
_formState?.setAttributeValue(widget.attribute, transformed);
156-
} else
157-
_formState?.setAttributeValue(widget.attribute, _selectedDialogCountry.name);
102+
} else {
103+
_formState?.setAttributeValue(widget.attribute, val.name);
104+
}
158105
if (widget.onSaved != null) {
159-
widget.onSaved(transformed ?? _selectedDialogCountry.name);
106+
widget.onSaved(transformed ?? val.name);
160107
}
161108
},
162109
builder: (FormFieldState<Country> field) {
163110
return GestureDetector(
164-
onTap: widget.isCupertinoPicker != null
165-
? (widget.isCupertinoPicker ? _openCupertinoCountryPicker : _openCountryPickerDialog)
166-
: _openCountryPickerDialog,
111+
onTap: () {
112+
FocusScope.of(context).requestFocus(FocusNode());
113+
if (widget.isCupertinoPicker) {
114+
_openCupertinoCountryPicker(field);
115+
} else {
116+
_openCountryPickerDialog(field);
117+
}
118+
},
167119
child: InputDecorator(
168120
decoration: widget.decoration.copyWith(
169121
errorText: field.errorText,
170122
),
171123
child: Row(
124+
key: ObjectKey(field.value),
172125
children: [
173-
CountryPickerUtils.getDefaultFlagImage(_selectedDialogCountry),
126+
CountryPickerUtils.getDefaultFlagImage(field.value),
174127
SizedBox(
175128
width: 10,
176129
),
177130
Expanded(
178131
child: Text(
179-
"${_selectedDialogCountry.name}",
132+
"${field.value?.name ?? ''}",
180133
style: widget.style,
181134
),
182135
),
@@ -187,4 +140,75 @@ class _FormBuilderCountryPickerState extends State<FormBuilderCountryPicker> {
187140
},
188141
);
189142
}
143+
144+
void _openCupertinoCountryPicker(FormFieldState field) =>
145+
showCupertinoModalPopup<void>(
146+
context: context,
147+
builder: (BuildContext context) {
148+
return CountryPickerCupertino(
149+
pickerSheetHeight: widget.cupertinoPickerSheetHeight ?? 300.0,
150+
onValuePicked: (Country value) => field.didChange(value),
151+
itemFilter: widget.countryFilterByIsoCode != null
152+
? (c) => widget.countryFilterByIsoCode.contains(c.isoCode)
153+
: null,
154+
priorityList: widget.priorityListByIsoCode != null
155+
? List.generate(
156+
widget.priorityListByIsoCode.length,
157+
(index) => CountryPickerUtils.getCountryByIsoCode(
158+
widget.priorityListByIsoCode[index]))
159+
: null,
160+
);
161+
},
162+
);
163+
164+
void _openCountryPickerDialog(FormFieldState field) => showDialog(
165+
context: context,
166+
builder: (context) => Theme(
167+
data: Theme.of(context).copyWith(
168+
cursorColor: Theme.of(context).primaryColor,
169+
primaryColor: widget.cursorColor ?? Theme.of(context).primaryColor,
170+
),
171+
child: CountryPickerDialog(
172+
titlePadding: widget.titlePadding ?? EdgeInsets.all(8.0),
173+
searchCursorColor:
174+
widget.cursorColor ?? Theme.of(context).primaryColor,
175+
searchInputDecoration:
176+
InputDecoration(hintText: widget.searchText ?? 'Search...'),
177+
isSearchable: widget.isSearchable ?? true,
178+
title: widget.dialogTitle ??
179+
Text(
180+
'Select Your Country',
181+
style: widget.dialogTextStyle ?? widget.style,
182+
),
183+
onValuePicked: (Country value) => field.didChange(value),
184+
itemFilter: widget.countryFilterByIsoCode != null
185+
? (c) => widget.countryFilterByIsoCode.contains(c.isoCode)
186+
: null,
187+
priorityList: widget.priorityListByIsoCode != null
188+
? List.generate(
189+
widget.priorityListByIsoCode.length,
190+
(index) => CountryPickerUtils.getCountryByIsoCode(
191+
widget.priorityListByIsoCode[index]))
192+
: null,
193+
itemBuilder: _buildDialogItem,
194+
),
195+
),
196+
);
197+
198+
Widget _buildDialogItem(Country country) {
199+
return Container(
200+
child: ListTile(
201+
contentPadding: EdgeInsets.zero,
202+
leading: CountryPickerUtils.getDefaultFlagImage(country),
203+
title: Text("${country.name}"),
204+
// visualDensity: VisualDensity.compact, //TODO: Re-enable after Flutter 1.17
205+
),
206+
);
207+
}
208+
209+
@override
210+
void dispose() {
211+
_formState?.unregisterFieldKey(widget.attribute);
212+
super.dispose();
213+
}
190214
}

0 commit comments

Comments
 (0)