Skip to content

TextField et TextEditingController - Bases de la saisie de texte

Le TextField est le widget fondamental pour la saisie de texte dans Flutter. Pour contrôler son contenu, on utilise un TextEditingController.

Voici un exemple complet d’utilisation du TextField avec un contrôleur :

import 'package:flutter/material.dart';
class MainScreen extends StatefulWidget {
const MainScreen({Key? key}) : super(key: key);
@override
State<MainScreen> createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
late final TextEditingController nameController;
String currentText = '';
@override
void initState() {
super.initState();
nameController = TextEditingController();
nameController.addListener(() {
print('text : ${nameController.text}');
print('selection : ${nameController.selection}');
setState(() => currentText = nameController.text);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: 320),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
TextField(
controller: nameController,
decoration: InputDecoration(
label: Text('Nom'),
),
),
Text('Nom : $currentText')
],
),
),
),
);
}
@override
void dispose() {
nameController.dispose();
super.dispose();
}
}

L’InputDecoration permet de personnaliser l’apparence du champ :

TextField(
controller: controller,
decoration: InputDecoration(
label: Text('Email'),
hintText: 'Entrez votre email',
prefixIcon: Icon(Icons.email),
suffixIcon: Icon(Icons.clear),
filled: true,
fillColor: Colors.grey[100],
border: OutlineInputBorder(),
errorText: hasError ? 'Email invalide' : null,
),
)

La propriété keyboardType permet de configurer le clavier adapté :

TextField(
keyboardType: TextInputType.emailAddress, // Clavier email
)
TextField(
keyboardType: TextInputType.phone, // Clavier numérique
)
TextField(
keyboardType: TextInputType.multiline, // Texte multiligne
maxLines: null,
)

Types disponibles :

  • TextInputType.text - Clavier par défaut
  • TextInputType.emailAddress - Clavier email
  • TextInputType.phone - Clavier téléphone
  • TextInputType.number - Clavier numérique
  • TextInputType.multiline - Texte multiligne
  • TextInputType.datetime - Saisie de date/heure
TextField(
onChanged: (value) => print('Texte modifié: $value'),
onEditingComplete: () => print('Édition terminée'),
onSubmitted: (value) => print('Formulaire soumis: $value'),
onTap: () => print('Champ tapé'),
)

Il est possible de formater automatiquement le texte saisi avec des TextInputFormatter :

class SpaceFormatter extends TextInputFormatter {
@override
TextEditingValue formatEditUpdate(
TextEditingValue oldValue,
TextEditingValue newValue) {
if (newValue.text.isEmpty || newValue.text.length == 1) return newValue;
final formattedText = newValue.text.replaceAll(' ', '').split('').join(' ');
return TextEditingValue(
text: formattedText,
selection: TextSelection.collapsed(offset: formattedText.length),
);
}
}
// Utilisation
TextField(
inputFormatters: [SpaceFormatter()],
)

Toujours disposer des contrôleurs pour éviter les fuites mémoire :

class FormScreen extends StatefulWidget {
@override
_FormScreenState createState() => _FormScreenState();
}
class _FormScreenState extends State<FormScreen> {
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
@override
void dispose() {
_emailController.dispose();
_passwordController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
// Formulaire...
}
}

Améliorer l’accessibilité avec des labels appropriés :

TextFormField(
decoration: InputDecoration(
labelText: 'Email',
helperText: 'Votre adresse email professionnelle',
),
keyboardType: TextInputType.emailAddress,
textInputAction: TextInputAction.next,
validator: validateEmail,
)

Implémenter une sauvegarde automatique des données :

class AutoSaveForm extends StatefulWidget {
@override
_AutoSaveFormState createState() => _AutoSaveFormState();
}
class _AutoSaveFormState extends State<AutoSaveForm> {
final _emailController = TextEditingController();
Timer? _debounceTimer;
@override
void initState() {
super.initState();
_emailController.addListener(_onTextChanged);
_loadSavedData();
}
void _onTextChanged() {
_debounceTimer?.cancel();
_debounceTimer = Timer(Duration(milliseconds: 500), () {
_saveData();
});
}
void _saveData() {
// Sauvegarder localement
SharedPreferences.getInstance().then((prefs) {
prefs.setString('draft_email', _emailController.text);
});
}
void _loadSavedData() {
SharedPreferences.getInstance().then((prefs) {
final savedEmail = prefs.getString('draft_email');
if (savedEmail != null) {
_emailController.text = savedEmail;
}
});
}
@override
Widget build(BuildContext context) {
return TextField(
controller: _emailController,
decoration: InputDecoration(
labelText: 'Email',
suffixIcon: Icon(Icons.cloud_done, color: Colors.green),
),
);
}
@override
void dispose() {
_debounceTimer?.cancel();
_emailController.dispose();
super.dispose();
}
}