mirror of
https://github.com/Iconica-Development/flutter_address_form.git
synced 2025-05-19 10:43:45 +02:00
Fixed Form With using FormKey
This commit is contained in:
parent
3ba62681c9
commit
51de713b0e
3 changed files with 112 additions and 97 deletions
|
@ -37,59 +37,73 @@ class AddressFormExample extends StatelessWidget {
|
||||||
|
|
||||||
final _addressController = AddressController(
|
final _addressController = AddressController(
|
||||||
zipCodeValidator: (text) {
|
zipCodeValidator: (text) {
|
||||||
|
if (text != null) {
|
||||||
if (text.isEmpty) {
|
if (text.isEmpty) {
|
||||||
return 'Can\'t be empty';
|
return 'Can\'t be empty';
|
||||||
}
|
}
|
||||||
if (!RegExp(r'^[1-9][0-9]{3}\s?[a-zA-Z]{2}$').hasMatch(text)) {
|
if (!RegExp(r'^[1-9][0-9]{3}\s?[a-zA-Z]{2}$').hasMatch(text)) {
|
||||||
return 'Invalid zipcode';
|
return 'Invalid zipcode';
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
housenumberValidator: (text) {
|
housenumberValidator: (text) {
|
||||||
|
if (text != null) {
|
||||||
if (text.isEmpty) {
|
if (text.isEmpty) {
|
||||||
return 'Can\'t be empty';
|
return 'Can\'t be empty';
|
||||||
}
|
}
|
||||||
if (text.length >= 3 || int.tryParse(text) == null) {
|
if (text.length >= 3 || int.tryParse(text) == null) {
|
||||||
return 'Invalid number';
|
return 'Invalid number';
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
suffixValidator: (text) {
|
suffixValidator: (text) {
|
||||||
|
if (text != null) {
|
||||||
if (text.isNotEmpty && RegExp(r'/^[a-z]*$/').hasMatch(text)) {
|
if (text.isNotEmpty && RegExp(r'/^[a-z]*$/').hasMatch(text)) {
|
||||||
return 'Invalid prefix';
|
return 'Invalid prefix';
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
streetValidator: (text) {
|
streetValidator: (text) {
|
||||||
|
if (text != null) {
|
||||||
if (text.isEmpty) {
|
if (text.isEmpty) {
|
||||||
return 'Can\'t be empty';
|
return 'Can\'t be empty';
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
cityValidator: (text) {
|
||||||
|
if (text != null) {
|
||||||
|
if (text.isEmpty) {
|
||||||
|
return 'Can\'t be empty';
|
||||||
|
}
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
onAutoComplete: (address) {
|
onAutoComplete: (address) {
|
||||||
return address;
|
return address;
|
||||||
},
|
},
|
||||||
cityValidator: (text) {
|
|
||||||
if (text.isEmpty) {
|
|
||||||
return 'Can\'t be empty';
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(),
|
appBar: AppBar(
|
||||||
|
title: const Text('Address Form'),
|
||||||
|
),
|
||||||
body: Column(
|
body: Column(
|
||||||
children: [
|
children: [
|
||||||
AddressForm(
|
AddressForm(
|
||||||
onSubmit: (value) => value, controller: _addressController),
|
onSubmit: (value) => value,
|
||||||
TextButton(
|
controller: _addressController,
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
_addressController.validate();
|
_addressController.validate();
|
||||||
},
|
},
|
||||||
child: Text('Test'),
|
child: const Text('Validate'),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -47,7 +47,8 @@ class AddressForm extends StatefulWidget {
|
||||||
class _AddressFormState extends State<AddressForm> {
|
class _AddressFormState extends State<AddressForm> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Flexible(
|
return Form(
|
||||||
|
key: widget._addressController._formKey,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
AddressFormTextField(
|
AddressFormTextField(
|
||||||
|
@ -55,21 +56,23 @@ class _AddressFormState extends State<AddressForm> {
|
||||||
controller: widget._addressController._zipcodeController,
|
controller: widget._addressController._zipcodeController,
|
||||||
fieldDecoration: widget.zipCodeDecoration,
|
fieldDecoration: widget.zipCodeDecoration,
|
||||||
),
|
),
|
||||||
Flexible(
|
Row(
|
||||||
child: Row(
|
|
||||||
children: [
|
children: [
|
||||||
AddressFormTextField(
|
Expanded(
|
||||||
|
child: AddressFormTextField(
|
||||||
validator: widget._addressController.housenumberValidator,
|
validator: widget._addressController.housenumberValidator,
|
||||||
controller: widget._addressController._housenumberController,
|
controller: widget._addressController._housenumberController,
|
||||||
fieldDecoration: widget.housenumberDecoration,
|
fieldDecoration: widget.housenumberDecoration,
|
||||||
),
|
),
|
||||||
AddressFormTextField(
|
),
|
||||||
|
Expanded(
|
||||||
|
child: AddressFormTextField(
|
||||||
validator: widget._addressController.suffixValidator,
|
validator: widget._addressController.suffixValidator,
|
||||||
controller: widget._addressController._suffixController,
|
controller: widget._addressController._suffixController,
|
||||||
fieldDecoration: widget.suffixDecoration,
|
fieldDecoration: widget.suffixDecoration,
|
||||||
),
|
),
|
||||||
],
|
|
||||||
),
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
AddressFormTextField(
|
AddressFormTextField(
|
||||||
validator: widget._addressController.streetValidator,
|
validator: widget._addressController.streetValidator,
|
||||||
|
@ -88,37 +91,26 @@ class _AddressFormState extends State<AddressForm> {
|
||||||
}
|
}
|
||||||
|
|
||||||
class AddressFormTextField extends StatelessWidget {
|
class AddressFormTextField extends StatelessWidget {
|
||||||
AddressFormTextField(
|
const AddressFormTextField({
|
||||||
{super.key,
|
super.key,
|
||||||
required this.fieldDecoration,
|
required this.fieldDecoration,
|
||||||
required this.controller,
|
required this.controller,
|
||||||
required this.validator});
|
this.validator,
|
||||||
|
});
|
||||||
|
|
||||||
final TextEditingController controller;
|
final TextEditingController controller;
|
||||||
final InputDecoration fieldDecoration;
|
final InputDecoration fieldDecoration;
|
||||||
final String? Function(String) validator;
|
final String? Function(String?)? validator;
|
||||||
|
|
||||||
late InputDecoration _addressFieldDecoration;
|
|
||||||
|
|
||||||
String? get _errorText => validator(controller.value.text);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ValueListenableBuilder<TextEditingValue>(
|
return Container(
|
||||||
valueListenable: controller,
|
|
||||||
builder: (context, value, _) {
|
|
||||||
_addressFieldDecoration =
|
|
||||||
fieldDecoration.copyWith(errorText: _errorText);
|
|
||||||
return Flexible(
|
|
||||||
child: Container(
|
|
||||||
margin: const EdgeInsets.all(10),
|
margin: const EdgeInsets.all(10),
|
||||||
child: TextField(
|
child: TextFormField(
|
||||||
controller: controller,
|
controller: controller,
|
||||||
decoration: _addressFieldDecoration,
|
decoration: fieldDecoration,
|
||||||
|
validator: validator,
|
||||||
),
|
),
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -127,17 +119,19 @@ class AddressController extends ChangeNotifier {
|
||||||
/// An optional value to initialize the form field to, or null otherwise.
|
/// An optional value to initialize the form field to, or null otherwise.
|
||||||
final AddressModel? initialValue;
|
final AddressModel? initialValue;
|
||||||
|
|
||||||
|
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
||||||
|
|
||||||
/// When the form changes, the function passes the current `AddressModel` as an argument and gives the possibility to manipulate and return a `AddressModel`.
|
/// When the form changes, the function passes the current `AddressModel` as an argument and gives the possibility to manipulate and return a `AddressModel`.
|
||||||
final FutureOr<AddressModel> Function(AddressModel)? onAutoComplete;
|
final FutureOr<AddressModel> Function(AddressModel)? onAutoComplete;
|
||||||
|
|
||||||
AddressController(
|
AddressController(
|
||||||
{this.initialValue,
|
{this.initialValue,
|
||||||
this.onAutoComplete,
|
this.onAutoComplete,
|
||||||
required this.zipCodeValidator,
|
this.zipCodeValidator,
|
||||||
required this.housenumberValidator,
|
this.housenumberValidator,
|
||||||
required this.suffixValidator,
|
this.suffixValidator,
|
||||||
required this.streetValidator,
|
this.streetValidator,
|
||||||
required this.cityValidator}) {
|
this.cityValidator}) {
|
||||||
_model = initialValue ??
|
_model = initialValue ??
|
||||||
const AddressModel(
|
const AddressModel(
|
||||||
zipcode: null,
|
zipcode: null,
|
||||||
|
@ -156,11 +150,11 @@ class AddressController extends ChangeNotifier {
|
||||||
|
|
||||||
late AddressModel _model;
|
late AddressModel _model;
|
||||||
|
|
||||||
final String? Function(String) zipCodeValidator;
|
final String? Function(String?)? zipCodeValidator;
|
||||||
final String? Function(String) housenumberValidator;
|
final String? Function(String?)? housenumberValidator;
|
||||||
final String? Function(String) suffixValidator;
|
final String? Function(String?)? suffixValidator;
|
||||||
final String? Function(String) streetValidator;
|
final String? Function(String?)? streetValidator;
|
||||||
final String? Function(String) cityValidator;
|
final String? Function(String?)? cityValidator;
|
||||||
|
|
||||||
late final _zipcodeController =
|
late final _zipcodeController =
|
||||||
TextEditingController(text: initialValue?.zipcode);
|
TextEditingController(text: initialValue?.zipcode);
|
||||||
|
@ -175,15 +169,7 @@ class AddressController extends ChangeNotifier {
|
||||||
AddressModel get model => _model;
|
AddressModel get model => _model;
|
||||||
|
|
||||||
bool validate() {
|
bool validate() {
|
||||||
if (zipCodeValidator.call(_zipcodeController.text) == null &&
|
return _formKey.currentState!.validate();
|
||||||
streetValidator.call(_streetController.text) == null &&
|
|
||||||
housenumberValidator.call(_housenumberController.text) == null &&
|
|
||||||
suffixValidator.call(_suffixController.text) == null &&
|
|
||||||
cityValidator.call(_cityController.text) == null) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _update() async {
|
void _update() async {
|
||||||
|
|
|
@ -4,8 +4,13 @@ import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:flutter_address_form/flutter_address_form.dart';
|
import 'package:flutter_address_form/flutter_address_form.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
late RegExp zipcodeRegExp;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
// Testing with Dutch ZipCode
|
||||||
|
zipcodeRegExp = RegExp(r'^[1-9][0-9]{3}\s?[a-zA-Z]{2}$');
|
||||||
|
});
|
||||||
testWidgets('Render App with AddressForm Widget', (tester) async {
|
testWidgets('Render App with AddressForm Widget', (tester) async {
|
||||||
final RegExp zipcodeRegExp = RegExp(r'^[1-9][0-9]{3}\s?[a-zA-Z]{2}$');
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
home: Scaffold(
|
home: Scaffold(
|
||||||
|
@ -14,44 +19,54 @@ void main() {
|
||||||
onSubmit: (value) => value,
|
onSubmit: (value) => value,
|
||||||
controller: AddressController(
|
controller: AddressController(
|
||||||
zipCodeValidator: (text) {
|
zipCodeValidator: (text) {
|
||||||
|
if (text != null) {
|
||||||
if (text.isEmpty) {
|
if (text.isEmpty) {
|
||||||
return 'Can\'t be empty';
|
return 'Can\'t be empty';
|
||||||
}
|
}
|
||||||
if (!zipcodeRegExp.hasMatch(text)) {
|
if (!zipcodeRegExp.hasMatch(text)) {
|
||||||
return 'Invalid zipcode';
|
return 'Invalid zipcode';
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
housenumberValidator: (text) {
|
housenumberValidator: (text) {
|
||||||
|
if (text != null) {
|
||||||
if (text.isEmpty) {
|
if (text.isEmpty) {
|
||||||
return 'Can\'t be empty';
|
return 'Can\'t be empty';
|
||||||
}
|
}
|
||||||
if (text.length >= 3 || int.tryParse(text) == null) {
|
if (text.length >= 3 || int.tryParse(text) == null) {
|
||||||
return 'Invalid number';
|
return 'Invalid number';
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
suffixValidator: (text) {
|
suffixValidator: (text) {
|
||||||
|
if (text != null) {
|
||||||
if (text.isNotEmpty && RegExp(r'/^[a-z]*$/').hasMatch(text)) {
|
if (text.isNotEmpty && RegExp(r'/^[a-z]*$/').hasMatch(text)) {
|
||||||
return 'Invalid prefix';
|
return 'Invalid prefix';
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
streetValidator: (text) {
|
streetValidator: (text) {
|
||||||
|
if (text != null) {
|
||||||
if (text.isEmpty) {
|
if (text.isEmpty) {
|
||||||
return 'Can\'t be empty';
|
return 'Can\'t be empty';
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
cityValidator: (text) {
|
||||||
|
if (text != null) {
|
||||||
|
if (text.isEmpty) {
|
||||||
|
return 'Can\'t be empty';
|
||||||
|
}
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
onAutoComplete: (address) {
|
onAutoComplete: (address) {
|
||||||
return address;
|
return address;
|
||||||
},
|
},
|
||||||
cityValidator: (text) {
|
|
||||||
if (text.isEmpty) {
|
|
||||||
return 'Can\'t be empty';
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
Loading…
Reference in a new issue