WIP adresscontroller validator

This commit is contained in:
Thomas Klein Langenhorst 2022-10-25 08:36:32 +02:00
parent 1b4d920586
commit affa510a5c
5 changed files with 187 additions and 145 deletions

View file

@ -31,54 +31,66 @@ class MyApp extends StatelessWidget {
} }
class AddressFormExample extends StatelessWidget { class AddressFormExample extends StatelessWidget {
AddressFormExample({Key? key}) : super(key: key); AddressFormExample({super.key});
final RegExp zipcodeRegExp = RegExp(r'^[1-9][0-9]{3}\s?[a-zA-Z]{2}$'); // final RegExp zipcodeRegExp = RegExp(r'^[1-9][0-9]{3}\s?[a-zA-Z]{2}$');
final _addressController = AddressController(
zipCodeValidator: (text) {
if (text.isEmpty) {
return 'Can\'t be empty';
}
if (!RegExp(r'^[1-9][0-9]{3}\s?[a-zA-Z]{2}$').hasMatch(text)) {
return 'Invalid zipcode';
}
return null;
},
housenumberValidator: (text) {
if (text.isEmpty) {
return 'Can\'t be empty';
}
if (text.length >= 3 || int.tryParse(text) == null) {
return 'Invalid number';
}
return null;
},
suffixValidator: (text) {
if (text.isNotEmpty && RegExp(r'/^[a-z]*$/').hasMatch(text)) {
return 'Invalid prefix';
}
return null;
},
streetValidator: (text) {
if (text.isEmpty) {
return 'Can\'t be empty';
}
return null;
},
onAutoComplete: (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(),
body: AddressForm( body: Column(
zipCodeValidator: (text) { children: [
if (text.isEmpty) { AddressForm(
return 'Can\'t be empty'; onSubmit: (value) => value, controller: _addressController),
} TextButton(
if (!zipcodeRegExp.hasMatch(text)) { onPressed: () {
return 'Invalid zipcode'; _addressController.validate;
} },
return null; child: Text('Test'))
}, ],
housenumberValidator: (text) {
if (text.isEmpty) {
return 'Can\'t be empty';
}
if (text.length >= 3 || int.tryParse(text) == null) {
return 'Invalid number';
}
return null;
},
suffixValidator: (text) {
if (text.isNotEmpty && RegExp(r'/^[a-z]*$/').hasMatch(text)) {
return 'Invalid prefix';
}
return null;
},
streetValidator: (text) {
if (text.isEmpty) {
return 'Can\'t be empty';
}
return null;
},
cityValidator: (text) {
if (text.isEmpty) {
return 'Can\'t be empty';
}
return null;
},
controller: AddressController(onAutoComplete: (address) {
return address;
}),
), ),
); );
} }

View file

@ -1,3 +1,4 @@
library flutter_address_form; library flutter_address_form;
export 'src/address_form.dart'; export 'src/address_form.dart';
export 'src/models/address_model.dart';

View file

@ -1,4 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'dart:ffi';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -6,74 +7,74 @@ import 'package:flutter_address_form/src/models/address_model.dart';
/// A widget that creates a form with different address fields widgets. /// A widget that creates a form with different address fields widgets.
/// Returns a `AddressModel` Object from a `AddressController`. /// Returns a `AddressModel` Object from a `AddressController`.
class AddressForm extends StatelessWidget { class AddressForm extends StatefulWidget {
AddressForm({ AddressForm({
Key? key, super.key,
this.zipCodeLabel = const Text('Zipcode'),
this.housenumberLabel = const Text('Housenumber'),
this.suffixLabel = const Text('Suffix'),
this.streetLabel = const Text('Street'),
this.cityLabel = const Text('City'),
required this.zipCodeValidator,
required this.housenumberValidator,
required this.suffixValidator,
required this.streetValidator,
required this.cityValidator,
AddressController? controller, AddressController? controller,
this.zipCodeDecoration = const InputDecoration(label: Text('Zipcode')),
this.housenumberDecoration =
const InputDecoration(label: Text('Housenumber')),
this.suffixDecoration = const InputDecoration(label: Text('Suffix')),
this.streetDecoration = const InputDecoration(label: Text('Street')),
this.cityDecoration = const InputDecoration(label: Text('City')),
required this.onSubmit,
}) { }) {
_addressController = _addressController = controller ??
controller ?? AddressController(onAutoComplete: (model) => model); AddressController(
onAutoComplete: (model) => model,
zipCodeValidator: (text) => null,
cityValidator: (text) => null,
housenumberValidator: (text) => null,
streetValidator: (text) => null,
suffixValidator: (text) => null,
);
} }
final Widget zipCodeLabel; final InputDecoration zipCodeDecoration;
final Widget housenumberLabel; final InputDecoration housenumberDecoration;
final Widget suffixLabel; final InputDecoration suffixDecoration;
final Widget streetLabel; final InputDecoration streetDecoration;
final Widget cityLabel; final InputDecoration cityDecoration;
final String? Function(String) zipCodeValidator; final ValueChanged<String> onSubmit;
final String? Function(String) housenumberValidator;
final String? Function(String) suffixValidator;
final String? Function(String) streetValidator;
final String? Function(String) cityValidator;
/// Controls the `AddressModel` /// Controls the `AddressModel`
late final AddressController _addressController; late final AddressController _addressController;
@override
State<AddressForm> createState() => _AddressFormState();
}
class _AddressFormState extends State<AddressForm> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Column( return Column(
children: [ children: [
AddressFormTextField( AddressFormTextField(
controller: _addressController._zipcodeController, controller: widget._addressController._zipcodeController,
validator: zipCodeValidator, fieldDecoration: widget.zipCodeDecoration,
label: zipCodeLabel,
), ),
Flexible( Flexible(
child: Row( child: Row(
children: [ children: [
AddressFormTextField( AddressFormTextField(
controller: _addressController._housenumberController, controller: widget._addressController._housenumberController,
validator: housenumberValidator, fieldDecoration: widget.housenumberDecoration,
label: housenumberLabel,
), ),
AddressFormTextField( AddressFormTextField(
controller: _addressController._suffixController, controller: widget._addressController._suffixController,
validator: suffixValidator, fieldDecoration: widget.suffixDecoration,
label: suffixLabel,
), ),
], ],
), ),
), ),
AddressFormTextField( AddressFormTextField(
controller: _addressController._streetController, controller: widget._addressController._streetController,
validator: streetValidator, fieldDecoration: widget.streetDecoration,
label: streetLabel,
), ),
AddressFormTextField( AddressFormTextField(
controller: _addressController._cityController, controller: widget._addressController._cityController,
validator: cityValidator, fieldDecoration: widget.cityDecoration,
label: cityLabel,
), ),
], ],
); );
@ -81,36 +82,35 @@ class AddressForm extends StatelessWidget {
} }
class AddressFormTextField extends StatelessWidget { class AddressFormTextField extends StatelessWidget {
final Widget label; AddressFormTextField({
final TextEditingController controller; super.key,
final String? Function(String) validator; required this.fieldDecoration,
const AddressFormTextField({
Key? key,
required this.label,
required this.controller, required this.controller,
required this.validator, }) {
}) : super(key: key); _addressFieldDecoration = fieldDecoration;
}
String? get _errorText => validator(controller.value.text); final TextEditingController controller;
final InputDecoration fieldDecoration;
late final InputDecoration _addressFieldDecoration;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ValueListenableBuilder<TextEditingValue>( return ValueListenableBuilder<TextEditingValue>(
valueListenable: controller, valueListenable: controller,
builder: (context, value, _) { builder: (context, value, _) {
return Flexible( return Flexible(
child: Container( child: Container(
margin: const EdgeInsets.all(10), margin: const EdgeInsets.all(10),
child: TextField( child: TextField(
controller: controller, controller: controller,
decoration: InputDecoration( decoration: _addressFieldDecoration,
label: label,
border: const OutlineInputBorder(),
errorText: _errorText),
),
), ),
); ),
}); );
},
);
} }
} }
@ -121,7 +121,14 @@ class AddressController extends ChangeNotifier {
/// 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({this.initialValue, this.onAutoComplete}) { AddressController(
{this.initialValue,
this.onAutoComplete,
required this.zipCodeValidator,
required this.housenumberValidator,
required this.suffixValidator,
required this.streetValidator,
required this.cityValidator}) {
_model = initialValue ?? _model = initialValue ??
const AddressModel( const AddressModel(
zipcode: null, zipcode: null,
@ -140,6 +147,12 @@ class AddressController extends ChangeNotifier {
late AddressModel _model; late AddressModel _model;
final String? Function(String) zipCodeValidator;
final String? Function(String) housenumberValidator;
final String? Function(String) suffixValidator;
final String? Function(String) streetValidator;
final String? Function(String) cityValidator;
late final _zipcodeController = late final _zipcodeController =
TextEditingController(text: initialValue?.zipcode); TextEditingController(text: initialValue?.zipcode);
late final _streetController = late final _streetController =
@ -152,6 +165,20 @@ class AddressController extends ChangeNotifier {
AddressModel get model => _model; AddressModel get model => _model;
bool get validate => _validate();
bool _validate() {
if (zipCodeValidator.call(_zipcodeController.text) == null &&
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 {
AddressModel updatedModel = _model.copyWith( AddressModel updatedModel = _model.copyWith(
zipcode: _zipcodeController.text, zipcode: _zipcodeController.text,

View file

@ -1,7 +1,6 @@
name: flutter_address_form name: flutter_address_form
description: A new Flutter package project. description: A new Flutter package project.
version: 0.0.1 version: 0.0.1
homepage:
environment: environment:
sdk: '>=2.18.2 <3.0.0' sdk: '>=2.18.2 <3.0.0'

View file

@ -11,45 +11,48 @@ void main() {
home: Scaffold( home: Scaffold(
appBar: AppBar(), appBar: AppBar(),
body: AddressForm( body: AddressForm(
zipCodeValidator: (text) { onSubmit: (value) => value,
if (text.isEmpty) { controller: AddressController(
return 'Can\'t be empty'; zipCodeValidator: (text) {
} if (text.isEmpty) {
if (!zipcodeRegExp.hasMatch(text)) { return 'Can\'t be empty';
return 'Invalid zipcode'; }
} if (!zipcodeRegExp.hasMatch(text)) {
return null; return 'Invalid zipcode';
}, }
housenumberValidator: (text) { return null;
if (text.isEmpty) { },
return 'Can\'t be empty'; housenumberValidator: (text) {
} if (text.isEmpty) {
if (text.length >= 3 || int.tryParse(text) == null) { return 'Can\'t be empty';
return 'Invalid number'; }
} if (text.length >= 3 || int.tryParse(text) == null) {
return null; return 'Invalid number';
}, }
suffixValidator: (text) { return null;
if (text.isNotEmpty && RegExp(r'/^[a-z]*$/').hasMatch(text)) { },
return 'Invalid prefix'; suffixValidator: (text) {
} if (text.isNotEmpty && RegExp(r'/^[a-z]*$/').hasMatch(text)) {
return null; return 'Invalid prefix';
}, }
streetValidator: (text) { return null;
if (text.isEmpty) { },
return 'Can\'t be empty'; streetValidator: (text) {
} if (text.isEmpty) {
return null; return 'Can\'t be empty';
}, }
cityValidator: (text) { return null;
if (text.isEmpty) { },
return 'Can\'t be empty'; onAutoComplete: (address) {
} return address;
return null; },
}, cityValidator: (text) {
controller: AddressController(onAutoComplete: (address) { if (text.isEmpty) {
return address; return 'Can\'t be empty';
}), }
return null;
},
),
), ),
), ),
), ),