Bonjour à tous,
J’ai une petite galère que je n’arrive pas à résoudre.
Dans un formulaire, je souhaite vérifier que le code postal saisi par l’utilisateur soit bien valide.
J’ai créé une fonction async à laquelle je fais passer la saisie de l’utilisateur et qui fait appel à une API.
Problème, je n’arrive pas à récupérer les valeurs (String) renvoyées par la fonction afin de les afficher à l’aide du validator (mes prints fonctionnent).
Ci-dessous les bouts de code pour vos yeux d’experts:
class _SignUpScreenState extends State<SignUpScreen> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final TextEditingController _postalCodeController = TextEditingController();
void _saveform() async {
setState(() {
_formKey.currentState!.validate();
});
}
Future<String> getPostalCode(int postalCode) async {
try {
final response = await http.get(Uri.parse(
'https://apicarto.ign.fr/api/codes-postaux/communes/$postalCode'));
var data = jsonDecode(response.body);
if (response.statusCode == 200 && data.isNotEmpty) {
print('Code postal valide');
return 'Code postal valide';
}
print('Entre un code postal valide');
return 'Entre un code postal valide';
} catch (e) {
print('Vérification du code postal impossible');
return 'Vérification du code postal impossible';
}
}
@override
void dispose() {
_postalCodeController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final screenHeightRatio =
MediaQuery.of(context).size.height / defaultScreenHeight;
return Scaffold(
appBar: const AppBarWidget(
isAppBarBottom: true,
appBarBottomText: 'Inscription',
appBarBottomTextColor: Colors.black,
),
body: SingleChildScrollView(
child: Form(
key: _formKey,
child: Column(
children: [
if (screenHeightRatio < 1)
SizedBox(
height: 5 * screenHeightRatio,
)
else
SizedBox(
height: 15 * screenHeightRatio,
),
InputFormField(
controller: _postalCodeController,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
LengthLimitingTextInputFormatter(5),
],
keyboardType: const TextInputType.numberWithOptions(
decimal: false,
signed: false,
),
labelText: 'Code postal :',
screenHeightRatio: screenHeightRatio,
textInputAction: TextInputAction.next,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Entre ton code postal';
}
if (value.isNotEmpty) {
getPostalCode(int.parse(value));
}
return null;
},
),
ButtonWidget(
buttonColor: greenColor,
buttonFontSize: 24,
buttonHeight: 50,
buttonText: 'Je valide',
buttonTextColor: Colors.white,
buttonWidth: 200,
buttonFunction: _saveform,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
'Déjà inscrit ?',
style: TextStyle(
fontFamily: 'GillSans',
),
),
TextButton(
onPressed: () {
Navigator.of(context).pushReplacementNamed('/signin');
},
child: const Text(
'Je me connecte',
style: TextStyle(
fontFamily: 'GillSans',
),
),
),
],
),
],
),
),
),
);
}
}
Et le code de mon widget InputFormField:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:winiz/config/constants.dart';
class InputFormField extends StatelessWidget {
final TextEditingController? controller;
final bool? enableCopyPaste;
final List<TextInputFormatter>? inputFormatters;
final TextInputType? keyboardType;
final String labelText;
final int? maxLength;
final bool? osbcureText;
final double? screenHeightRatio;
final IconButton? suffixIcon;
final Color? suffixIconColor;
final TextInputAction? textInputAction;
final String? Function(String?)? validator;
const InputFormField({
super.key,
required this.controller,
required this.labelText,
this.enableCopyPaste,
this.inputFormatters,
this.keyboardType,
this.maxLength,
bool? osbcureText,
double? screenHeightRatio,
this.suffixIcon,
this.suffixIconColor,
this.textInputAction,
this.validator,
}) : screenHeightRatio = screenHeightRatio ?? 1,
osbcureText = osbcureText ?? false;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10.0,
),
child: Container(
margin: EdgeInsets.symmetric(vertical: 5 * screenHeightRatio!),
padding: const EdgeInsets.symmetric(horizontal: 5.0, vertical: 1.0),
//height: 50 * screenHeightRatio!,
width: double.infinity,
decoration: BoxDecoration(
border: Border.all(
color: Colors.grey,
),
borderRadius: BorderRadius.circular(30),
color: yellowLightColor,
),
child: TextFormField(
controller: controller,
decoration: InputDecoration(
border: InputBorder.none,
contentPadding:
const EdgeInsets.symmetric(vertical: 5, horizontal: 20),
errorStyle: const TextStyle(
height: 0.3,
),
labelText: labelText,
suffixIcon: suffixIcon,
suffixIconColor: suffixIconColor,
),
enableInteractiveSelection: enableCopyPaste,
inputFormatters: inputFormatters,
keyboardType: keyboardType,
maxLength: maxLength,
obscureText: osbcureText!,
textInputAction: textInputAction,
validator: validator,
),
),
);
}
}
Un petit avis?
Merci beaucoup.
Edit:
Bon, après quelques grattages de tête, j’ai réussi à faire ce que je voulais. D’après ce que j’ai lu, on ne peut pas utiliser de fonction async directement à l’aide du validator.
J’exécute donc la fonction async quand on clique sur le bouton (fonction _saveform) avant le validator.
Je vous mets le code ci-dessous. N’hésitez pas à me faire des remarques si vous voyez des améliorations à apporter. Bonne soirée.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:http/http.dart' as http;
// import 'package:url_launcher/url_launcher.dart';
import '../config/constants.dart';
import '../widgets/appbar_widget.dart';
import '../widgets/button_widget.dart';
import '../widgets/checkbox_form_widget.dart';
import '../widgets/input_form_field_widget.dart';
class SignUpScreen extends StatefulWidget {
const SignUpScreen({super.key});
static const routeName = '/signup';
@override
State<SignUpScreen> createState() => _SignUpScreenState();
}
class _SignUpScreenState extends State<SignUpScreen> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final TextEditingController _postalCodeController = TextEditingController();
String? _postalCodeMessage;
void _saveform() async {
String? postalCodeMessage;
if (_postalCodeController.value.text != '') {
postalCodeMessage =
await checkPostalCode(_postalCodeController.value.text);
}
setState(() {
_postalCodeMessage = postalCodeMessage;
_formKey.currentState!.validate();
});
}
Future<String?> checkPostalCode(postalCode) async {
try {
final response = await http.get(Uri.parse(
'https://apicarto.ign.fr/api/codes-postaux/communes/$postalCode'));
if (response.statusCode == 200) {
return null;
}
if (response.statusCode == 400 || response.statusCode == 404) {
return 'Entre un code postal valide';
}
return null;
} catch (e) {
return 'Vérification du code postal impossible';
}
}
@override
void dispose() {
_postalCodeController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final screenHeightRatio =
MediaQuery.of(context).size.height / defaultScreenHeight;
return Scaffold(
appBar: const AppBarWidget(
isAppBarBottom: true,
appBarBottomText: 'Inscription',
appBarBottomTextColor: Colors.black,
),
body: SingleChildScrollView(
child: Form(
key: _formKey,
child: Column(
children: [
if (screenHeightRatio < 1)
SizedBox(
height: 5 * screenHeightRatio,
)
else
SizedBox(
height: 15 * screenHeightRatio,
),
InputFormField(
controller: _postalCodeController,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
LengthLimitingTextInputFormatter(5),
],
keyboardType: const TextInputType.numberWithOptions(
decimal: false,
signed: false,
),
labelText: 'Code postal :',
screenHeightRatio: screenHeightRatio,
textInputAction: TextInputAction.next,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Entre ton code postal';
}
if (_postalCodeMessage != null) {
return _postalCodeMessage;
}
return null;
},
),
const SizedBox(
height: 10,
),
ButtonWidget(
buttonColor: greenColor,
buttonFontSize: 24,
buttonHeight: 50,
buttonText: 'Je valide',
buttonTextColor: Colors.white,
buttonWidth: 200,
buttonFunction: _saveform,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
'Déjà inscrit ?',
style: TextStyle(
fontFamily: 'GillSans',
),
),
TextButton(
onPressed: () {
Navigator.of(context).pushReplacementNamed('/signin');
},
child: const Text(
'Je me connecte',
style: TextStyle(
fontFamily: 'GillSans',
),
),
),
],
),
],
),
),
),
);
}
}