Input Utente
TextField
Descrizione
TextField
è il widget di base per l’inserimento di testo da parte dell’utente.
Supporta input singola linea o multilinea, personalizzazioni grafiche, validazioni, icone, e molto altro.
È usato per form, login, ricerca, commenti e qualunque input utente.
Attributi utili
Attributo | Tipo | Descrizione |
---|---|---|
controller | TextEditingController? | Controlla e accede al contenuto del campo di testo |
decoration | InputDecoration? | Personalizza il campo (label, hint, icone, bordi, ecc.) |
keyboardType | TextInputType? | Specifica il tipo di tastiera (email, numero, testo, ecc.) |
obscureText | bool | Nasconde il testo (utile per password) |
onChanged | Function(String)? | Callback chiamata ogni volta che il testo cambia |
onSubmitted | Function(String)? | Chiamata quando l’utente preme invio |
maxLines | int? | Numero massimo di righe (default: 1) |
readOnly | bool | Rende il campo non modificabile |
enabled | bool? | Abilita/disabilita il campo |
Esempio
TextField(
decoration: InputDecoration(
labelText: 'Email',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.email),
),
keyboardType: TextInputType.emailAddress,
onChanged: (value) {
print('Email inserita: $value');
},
)
Esercizio
- Crea un form con 2
TextField
: uno per l’email e uno per la password (conobscureText
a true). Aggiungi un pulsante “Login” che stampa i valori inseriti.
Esercizio Bonus
Crea un widget
CustomTextField
che:
- accetta
label
,icon
eisPassword
come parametri,- imposta automaticamente il tipo di tastiera e il bordo,
- fornisce un modo per accedere al valore corrente (tramite
controller
).
Uso di TextEditingController
e ciclo di vita
Quando hai bisogno di accedere direttamente al contenuto di un TextField
, ad esempio per leggere o impostare il testo, puoi usare un TextEditingController
.
Questo oggetto gestisce il valore del campo e consente anche di ascoltare le modifiche.
È importante creare il controller in un StatefulWidget
e ricordarsi di chiamare dispose()
per evitare memory leak.
Esempio completo
class EmailInput extends StatefulWidget {
@override
State<EmailInput> createState() => _EmailInputState();
}
class _EmailInputState extends State<EmailInput> {
final TextEditingController _controller = TextEditingController();
@override
void dispose() {
_controller.dispose(); // Importante: libera risorse
super.dispose();
}
void _submit() {
final email = _controller.text;
print('Email inserita: $email');
}
@override
Widget build(BuildContext context) {
return Column(
children: [
TextField(
controller: _controller,
decoration: InputDecoration(labelText: 'Email'),
),
ElevatedButton(
onPressed: _submit,
child: Text('Invia'),
),
],
);
}
}
TextEditingController
è utile anche per:
- precompilare campi (
controller.text = "valore iniziale";
) - aggiornare dinamicamente il contenuto da codice
- ascoltare modifiche con
addListener
TextEditingController
con TextFormField
e validator
Se stai lavorando con un Form
, puoi combinare TextEditingController
e validator
per:
- gestire il contenuto del campo di input,
- validare il testo inserito,
- accedere facilmente al valore al momento dell’invio.
Esempio completo con validazione
class LoginForm extends StatefulWidget {
@override
State<LoginForm> createState() => _LoginFormState();
}
class _LoginFormState extends State<LoginForm> {
final _formKey = GlobalKey<FormState>();
final TextEditingController _emailController = TextEditingController();
@override
void dispose() {
_emailController.dispose();
super.dispose();
}
void _submitForm() {
if (_formKey.currentState!.validate()) {
final email = _emailController.text;
print('Login con email: $email');
}
}
@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
children: [
TextFormField(
controller: _emailController,
decoration: InputDecoration(labelText: 'Email'),
keyboardType: TextInputType.emailAddress,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Inserisci un\'email';
}
if (!value.contains('@')) {
return 'Email non valida';
}
return null;
},
),
SizedBox(height: 16),
ElevatedButton(
onPressed: _submitForm,
child: Text('Login'),
),
],
),
);
}
}
Puoi usare più TextEditingController
per gestire più campi del form, ad esempio per email e password.
Ricorda sempre di eliminare tutti i controller in dispose()
.
Checkbox
Descrizione
Checkbox
è un widget per selezioni binarie (vero/falso).
Viene spesso utilizzato in form, impostazioni e preferenze utente.
Può essere combinato con ListTile
per un layout più accessibile e ordinato.
Attributi utili
Attributo | Tipo | Descrizione |
---|---|---|
value | bool | Stato attuale del checkbox (true/false) |
onChanged | Function(bool?) | Callback quando l’utente cambia lo stato |
activeColor | Color? | Colore della spunta quando selezionata |
checkColor | Color? | Colore della spunta (segno di check) |
fillColor | MaterialStateProperty<Color?>? | Colore del riempimento |
tristate | bool | Permette uno stato nullo (null, true, false) |
Esempio
bool isChecked = false;
Checkbox(
value: isChecked,
onChanged: (bool? newValue) {
setState(() {
isChecked = newValue ?? false;
});
},
)
Esercizio
Crea una lista di 3 checkbox con etichette diverse. Ogni checkbox deve aggiornare il suo stato individualmente.
Esercizio Bonus
Crea un widget
CustomCheckboxTile
che:
- accetta un titolo e un booleano come input,
- visualizza il titolo accanto alla checkbox,
- usa un
ListTile
per l’allineamento,- cambia il colore in base allo stato selezionato.
CheckboxListTile
Descrizione
CheckboxListTile
combina una Checkbox
con un layout in stile lista.
Fornisce titolo, sottotitolo e supporto per icone, rendendolo perfetto per le impostazioni.
Usa ListTile
internamente. Ottimo per UX coerente.
Attributi utili
Attributo | Tipo | Descrizione |
---|---|---|
value | bool | Stato della checkbox |
onChanged | Function(bool?) | Callback al cambiamento |
title | Widget | Titolo principale |
subtitle | Widget? | Testo secondario sotto il titolo |
secondary | Widget? | Widget alla fine (es. icona) |
controlAffinity | ListTileControlAffinity | Posizione della checkbox (leading/trailing) |
Esempio
CheckboxListTile(
title: Text('Accetta termini'),
value: isChecked,
onChanged: (bool? value) {
setState(() {
isChecked = value ?? false;
});
},
)
Esercizio
- Crea un elenco di 3
CheckboxListTile
per attivare/disattivare diverse preferenze utente.
Switch
Descrizione
Switch
è un widget per controlli binari ON/OFF.
Simile a un interruttore di impostazioni.
Attributi utili
Attributo | Tipo | Descrizione |
---|---|---|
value | bool | Stato dell’interruttore |
onChanged | Function(bool) | Callback al cambiamento |
activeColor | Color? | Colore quando attivo |
inactiveThumbColor | Color? | Colore del pollice da spento |
inactiveTrackColor | Color? | Colore dello sfondo da spento |
Esempio
Switch(
value: isSwitched,
onChanged: (bool value) {
setState(() {
isSwitched = value;
});
},
)
Esercizio utile
Crea una schermata con uno
Switch
che cambia tema (chiaro/scuro).
SwitchListTile
Descrizione
SwitchListTile
è la versione “tile” del widget Switch
.
Fornisce un layout con testo e interruttore integrato.
Attributi utili
Attributo | Tipo | Descrizione |
---|---|---|
value | bool | Stato dell’interruttore |
onChanged | Function(bool) | Callback al cambiamento |
title | Widget | Titolo principale |
subtitle | Widget? | Testo secondario sotto il titolo |
secondary | Widget? | Icona o altro widget finale |
Esempio
SwitchListTile(
title: Text('Notifiche'),
value: isEnabled,
onChanged: (bool value) {
setState(() {
isEnabled = value;
});
},
)
Esercizio
- Crea un elenco di 2
SwitchListTile
per abilitare/disabilitare impostazioni utente.
Slider
Descrizione
Slider
è un widget che consente all’utente di selezionare un valore da un intervallo continuo.
Attributi utili
Attributo | Tipo | Descrizione |
---|---|---|
value | double | Valore corrente del cursore |
onChanged | Function(double) | Callback durante il movimento |
min | double | Valore minimo selezionabile |
max | double | Valore massimo selezionabile |
divisions | int? | Suddivisione in step discreti (opzionale) |
label | String? | Etichetta mostrata sopra il cursore |
Esempio pratico
Slider(
value: sliderValue,
min: 0,
max: 100,
divisions: 10,
label: '${sliderValue.round()}',
onChanged: (double value) {
setState(() {
sliderValue = value;
});
},
)
Esercizio
- Crea uno
Slider
che controlla la dimensione di un testo visualizzato in tempo reale.
.adaptive
Descrizione
Molti widget come Switch
, Checkbox
e CupertinoSwitch
hanno una versione .adaptive
.
Questa variante sceglie automaticamente lo stile appropriato in base alla piattaforma (Android o iOS).
È utile per avere UI coerenti senza scrivere codice condizionale con Platform.isIOS
o simili.
Esempio pratico
Switch.adaptive(
value: isDarkMode,
onChanged: (bool value) {
setState(() {
isDarkMode = value;
});
},
)
Esercizio
- Crea una schermata con uno
Switch.adaptive
che attiva/disattiva una modalità scura e mostra unoCheckbox.adaptive
per confermare un’opzione.
Esercizio Bonus
Crea un widget
PlatformSwitch
che:
- Usa
Switch.adaptive
- Mostra un’icona diversa su iOS e Android (es.
Icons.apple
vsIcons.android
)- Cambia dinamicamente il colore della UI al cambio stato
GestureDetector
Descrizione
GestureDetector
intercetta e gestisce gesture dell’utente: tap, doppio tap, swipe, long press, ecc.
Usalo per rendere tappabile qualunque widget.
Attributi utili
Attributo | Tipo | Descrizione |
---|---|---|
onTap | Function()? | Chiamato al tocco singolo |
onDoubleTap | Function()? | Chiamato al doppio tocco |
onLongPress | Function()? | Chiamato dopo una pressione prolungata |
onPanUpdate | Function(DragUpdateDetails)? | Movimento durante trascinamento |
Esempio
GestureDetector(
onTap: () => print('Toccato!'),
child: Container(
padding: EdgeInsets.all(20),
color: Colors.amber,
child: Text('Tocca qui'),
),
)
Esercizio
- Crea un quadrato colorato che cambia colore ogni volta che viene toccato usando
GestureDetector
.
InkWell
Descrizione
InkWell
è un widget che aggiunge un effetto visivo “ripple” quando viene toccato.
Deve essere usato all’interno di un widget Material (es. Card, Container con Material).
Attributi utili
Attributo | Tipo | Descrizione |
---|---|---|
onTap | Function()? | Chiamato al tocco |
onLongPress | Function()? | Pressione prolungata |
borderRadius | BorderRadius? | Bordo arrotondato per l’effetto ripple |
Esempio
InkWell(
onTap: () => print('Ripple!'),
borderRadius: BorderRadius.circular(12),
child: Container(
padding: EdgeInsets.all(20),
color: Colors.teal,
child: Text('Premi me'),
),
)
Esercizio utile
- Crea una card cliccabile con
InkWell
che mostra un messaggio quando premuta.
Buttons in Flutter
Descrizione
I pulsanti (button) sono widget fondamentali. Flutter fornisce un set ampio di button personalizzabili, ciascuno pensato per contesti diversi (Material, Cupertino, azioni floating, pulsanti testuali, ecc.).
ElevatedButton
Descrizione
Pulsante rialzato con effetto ombra, usato per azioni primarie.
Attributi utili
Attributo | Tipo | Descrizione |
---|---|---|
onPressed | Function() | Azione al click |
child | Widget | Contenuto del bottone |
style | ButtonStyle? | Stile (colore, padding, forma, ecc.) |
Esempio
ElevatedButton(
onPressed: () => print('Premuto!'),
child: Text('Invia'),
)
//Esempio con style
ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.teal,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 16),
elevation: 4,
),
child: Text('Conferma'),
)
TextButton
Descrizione
Pulsante piatto senza sfondo o bordo, per azioni secondarie.
Esempio
TextButton(
onPressed: () {},
child: Text('Annulla'),
)
TextButton(
onPressed: () {},
style: TextButton.styleFrom(
foregroundColor: Colors.orange,
padding: EdgeInsets.all(16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: Text('Annulla'),
)
OutlinedButton
Descrizione
Simile a TextButton
, ma con un bordo visibile.
Esempio
OutlinedButton(
onPressed: () {},
child: Text('Info'),
)
OutlinedButton(
onPressed: () {},
style: OutlinedButton.styleFrom(
side: BorderSide(color: Colors.red),
foregroundColor: Colors.red,
padding: EdgeInsets.symmetric(horizontal: 16),
),
child: Text('Info'),
)
IconButton
Descrizione
Pulsante che contiene solo un’icona, utile per toolbar o azioni rapide.
Esempio
IconButton(
onPressed: () {},
icon: Icon(Icons.thumb_up),
)
FloatingActionButton
Descrizione
Pulsante fluttuante usato spesso per azione primaria su una schermata.
Esempio
FloatingActionButton(
onPressed: () {},
child: Icon(Icons.add),
)
CupertinoButton
Descrizione
Pulsante in stile iOS (Cupertino), usato in app native Apple.
Esempio
CupertinoButton(
onPressed: () {},
child: Text('iOS Style'),
)
DropdownButton
Descrizione
Non è un bottone classico, ma consente di selezionare un valore da una lista.
Attributi utili
Attributo | Tipo | Descrizione |
---|---|---|
value | T | Valore attualmente selezionato |
items | List<DropdownMenuItem<T>> | Elementi selezionabili |
onChanged | Function(T?) | Callback alla selezione |
hint | Widget? | Testo mostrato prima della selezione |
isExpanded | bool | Allarga il bottone a tutta la larghezza |
Esempio Base
DropdownButton<String>(
value: selectedValue,
items: ['Uno', 'Due', 'Tre']
.map((e) => DropdownMenuItem(value: e, child: Text(e)))
.toList(),
onChanged: (value) {
setState(() {
selectedValue = value!;
});
},
)
Esempio avanzato con icone
DropdownButton<String>(
value: selected,
items: [
DropdownMenuItem(
value: 'home',
child: Row(
children: [Icon(Icons.home), SizedBox(width: 8), Text('Home')],
),
),
DropdownMenuItem(
value: 'settings',
child: Row(
children: [Icon(Icons.settings), SizedBox(width: 8), Text('Settings')],
),
),
],
onChanged: (value) => setState(() => selected = value!),
)
Paragonabile alla select ma con alcune differenze rispetto a HTML
- In Flutter puoi personalizzare ogni voce (es. icone, padding).
- Il menu viene disegnato con Overlay, quindi non è parte del layout fisso.
- Supporta anche DropdownButtonFormField per validazione nei form.
Decorazione avanzata di Widget con WidgetStateProperty
La classe ButtonStyle accetta WidgetStateProperty<T> per molte delle sue proprietà (tra cui padding, backgroundColor, foregroundColor, side, ecc.).
Questo ci consente di definire lo style in base allo stato in cui si trova il widget come pressed
, hovered
WidgetStatePropertyAll
WidgetStatePropertyAll o WidgetStateProperty.all() equivale a definire una proprietà per tutti gli stati lo stesso avviene quando usiamo ClasseButton.styleFrom() come visto in precedenza
Esercizio utile
- Crea una schermata con tutti i principali pulsanti Flutter, ciascuno con un’azione diversa.
Esercizio bonus
Crea un widget
CustomButton
che:
- Usa
ElevatedButton
- Accetta testo, colore e icona opzionale
- Può disabilitarsi con una proprietà booleana
Divider
Descrizione
Divider
è un widget semplice usato per separare visivamente contenuti verticali, come elementi in una lista, sezioni in una colonna, o gruppi di informazioni.
Viene rappresentato come una linea orizzontale, con colore, spessore e margini personalizzabili.
Attributi utili
Attributo | Descrizione |
---|---|
color | Colore della linea (di default usa il colore del tema) |
thickness | Spessore della linea (default: 0.0, visibile solo se height > 0) |
height | Altezza totale dell’area occupata dal divider (include anche margini verticali) |
indent | Spazio vuoto a sinistra (in pixel) |
endIndent | Spazio vuoto a destra (in pixel) |
Esempio
Divider()
Divider(
color: Colors.grey,
thickness: 2,
height: 32,
indent: 16,
endIndent: 16,
)
Divider verticale: VerticalDivider
Se vuoi separare elementi orizzontali (es. all’interno di una Row
), puoi usare VerticalDivider
:
Row(
children: [
Expanded(child: Text('A')),
VerticalDivider(color: Colors.black, thickness: 1),
Expanded(child: Text('B')),
],
)
VerticalDivider
funziona solo all’interno di un contenitore che ha altezza, come una Row
con SizedBox
o Container
.