Skip to content

Widgets d'input spécialisés Flutter - Switch, Checkbox, Radio, Slider, Dropdown

Flutter propose une gamme complète de widgets d’input spécialisés pour différents types de saisie : boutons à bascule, cases à cocher, boutons radio, curseurs et listes déroulantes.

Le Switch permet de créer un interrupteur binaire :

class SwitchExample extends StatefulWidget {
@override
_SwitchExampleState createState() => _SwitchExampleState();
}
class _SwitchExampleState extends State<SwitchExample> {
bool switched = false;
@override
Widget build(BuildContext context) {
return Switch(
value: switched,
onChanged: (value) => setState(() => switched = value),
);
}
}
class CheckboxExample extends StatefulWidget {
@override
_CheckboxExampleState createState() => _CheckboxExampleState();
}
class _CheckboxExampleState extends State<CheckboxExample> {
bool checked = false;
@override
Widget build(BuildContext context) {
return Row(
children: [
Checkbox(
value: checked,
onChanged: (value) => setState(() => checked = value!),
),
InkWell(
child: const Text("J'accepte les CGU !"),
onTap: () => setState(() => checked = !checked),
),
],
);
}
}

Les boutons radio permettent de sélectionner une option parmi plusieurs :

class RadioExample extends StatefulWidget {
@override
_RadioExampleState createState() => _RadioExampleState();
}
class _RadioExampleState extends State<RadioExample> {
int radioSelection = 1;
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Row(
children: [
Radio<int>(
value: 1,
groupValue: radioSelection,
onChanged: (value) => setState(() => radioSelection = value!),
),
const Text('Option 1')
],
),
Row(
children: [
Radio<int>(
value: 2,
groupValue: radioSelection,
onChanged: (value) => setState(() => radioSelection = value!),
),
const Text('Option 2')
],
),
],
);
}
}

Le Slider permet la sélection d’une valeur numérique :

class SliderExample extends StatefulWidget {
@override
_SliderExampleState createState() => _SliderExampleState();
}
class _SliderExampleState extends State<SliderExample> {
double sliderValue = 5.0;
@override
Widget build(BuildContext context) {
return Column(
children: [
Text('Valeur: ${sliderValue.toStringAsFixed(1)}'),
Slider(
value: sliderValue,
min: 0,
max: 10,
divisions: 100,
label: '${sliderValue.toStringAsPrecision(3)}',
onChanged: (value) => setState(() => sliderValue = value),
),
],
);
}
}
class DropdownExample extends StatefulWidget {
@override
_DropdownExampleState createState() => _DropdownExampleState();
}
class _DropdownExampleState extends State<DropdownExample> {
int? citySelection;
@override
Widget build(BuildContext context) {
return DropdownButton<int>(
value: citySelection,
hint: const Text('Sélectionnez votre destination'),
items: const [
DropdownMenuItem(child: Text('Madrid'), value: 1),
DropdownMenuItem(child: Text('Rome'), value: 2),
DropdownMenuItem(child: Text('Lisbonne'), value: 3),
],
onChanged: (int? value) => setState(() => citySelection = value),
);
}
}

Le widget Autocomplete intègre un mécanisme de suggestion automatique :

const cities = ['Paris', 'Lyon', 'Marseille', 'Toulouse', 'Nice'];
class AutoCompleteExample extends StatefulWidget {
@override
_AutoCompleteExampleState createState() => _AutoCompleteExampleState();
}
class _AutoCompleteExampleState extends State<AutoCompleteExample> {
String? selectedCity;
@override
Widget build(BuildContext context) {
return Column(
children: [
Autocomplete<String>(
optionsBuilder: (TextEditingValue value) {
if (value.text.isEmpty) return <String>[];
return cities.where((element) =>
element.toLowerCase().contains(value.text.toLowerCase())
);
},
onSelected: (value) => setState(() => selectedCity = value),
fieldViewBuilder: (context, controller, focusNode, onEditingComplete) {
return TextFormField(
controller: controller,
focusNode: focusNode,
onEditingComplete: onEditingComplete,
decoration: InputDecoration(
labelText: 'Ville',
border: OutlineInputBorder(),
),
);
},
),
if (selectedCity != null)
Padding(
padding: const EdgeInsets.all(8.0),
child: Text('Ville sélectionnée: $selectedCity'),
),
],
);
}
}

Voici un exemple intégrant plusieurs widgets d’input :

class CompleteInputForm extends StatefulWidget {
@override
_CompleteInputFormState createState() => _CompleteInputFormState();
}
class _CompleteInputFormState extends State<CompleteInputForm> {
bool _notifications = true;
bool _acceptTerms = false;
int _priority = 1;
double _budget = 50.0;
String? _category;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Formulaire complet')),
body: Padding(
padding: EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Switch
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Notifications'),
Switch(
value: _notifications,
onChanged: (value) => setState(() => _notifications = value),
),
],
),
// Checkbox
Row(
children: [
Checkbox(
value: _acceptTerms,
onChanged: (value) => setState(() => _acceptTerms = value!),
),
Text('J\'accepte les conditions d\'utilisation'),
],
),
// Radio buttons
Text('Priorité:'),
Row(
children: [
Radio<int>(
value: 1,
groupValue: _priority,
onChanged: (value) => setState(() => _priority = value!),
),
Text('Normale'),
Radio<int>(
value: 2,
groupValue: _priority,
onChanged: (value) => setState(() => _priority = value!),
),
Text('Élevée'),
],
),
// Slider
Text('Budget: ${_budget.toStringAsFixed(0)}€'),
Slider(
value: _budget,
min: 0,
max: 100,
divisions: 10,
onChanged: (value) => setState(() => _budget = value),
),
// Dropdown
DropdownButton<String>(
value: _category,
hint: Text('Sélectionner une catégorie'),
isExpanded: true,
items: [
DropdownMenuItem(child: Text('Personnel'), value: 'personal'),
DropdownMenuItem(child: Text('Professionnel'), value: 'work'),
DropdownMenuItem(child: Text('Urgent'), value: 'urgent'),
],
onChanged: (value) => setState(() => _category = value),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: _acceptTerms ? () {
// Traitement du formulaire
print('Notifications: $_notifications');
print('Priorité: $_priority');
print('Budget: $_budget');
print('Catégorie: $_category');
} : null,
child: Text('Valider'),
),
],
),
),
);
}
}

Bonnes pratiques pour les widgets d’input

Section titled “Bonnes pratiques pour les widgets d’input”

Groupez les widgets similaires dans des sections distinctes :

Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Préférences', style: Theme.of(context).textTheme.headline6),
SwitchListTile(
title: Text('Notifications push'),
subtitle: Text('Recevoir des notifications'),
value: _pushNotifications,
onChanged: (value) => setState(() => _pushNotifications = value),
),
SwitchListTile(
title: Text('Mode sombre'),
subtitle: Text('Interface sombre'),
value: _darkMode,
onChanged: (value) => setState(() => _darkMode = value),
),
],
)

Assurez-vous que les valeurs obligatoires sont sélectionnées :

String? _validateSelection() {
if (_category == null) {
return 'Veuillez sélectionner une catégorie';
}
if (!_acceptTerms) {
return 'Vous devez accepter les conditions';
}
return null;
}

Ajoutez des labels appropriés pour l’accessibilité :

Semantics(
label: 'Activer les notifications',
child: Switch(
value: _notifications,
onChanged: (value) => setState(() => _notifications = value),
),
)