Mettere insieme tutto: la schermata completa
Ecco il file main.dart completo, pronto da copiare e incollare. Usiamo due ListTile con icona: è il pattern più comune nei form, perché l'intera riga è toccabile e mostra chiaramente il valore selezionato.
Prossimi passi da provare:
- imposta
selectableDayPredicate in showDatePicker per disabilitare weekend o giorni non disponibili;
- usa
initialEntryMode: DatePickerEntryMode.input per la data di nascita (più veloce da digitare);
- salva il
DateTime con toIso8601String() e rileggilo con DateTime.parse().
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:intl/date_symbol_data_local.dart';
import 'package:intl/intl.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await initializeDateFormatting('it_IT', null);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(colorSchemeSeed: Colors.indigo, useMaterial3: true),
localizationsDelegates: const [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: const [Locale('it', 'IT'), Locale('en', 'US')],
locale: const Locale('it', 'IT'),
home: const AppuntamentoPage(),
);
}
}
class AppuntamentoPage extends StatefulWidget {
const AppuntamentoPage({super.key});
@override
State<AppuntamentoPage> createState() => _AppuntamentoPageState();
}
class _AppuntamentoPageState extends State<AppuntamentoPage> {
DateTime? _data;
TimeOfDay? _ora;
Future<void> _selezionaData() async {
final oggi = DateTime.now();
final scelta = await showDatePicker(
context: context,
initialDate: _data ?? oggi,
firstDate: DateTime(oggi.year - 1),
lastDate: DateTime(oggi.year + 5),
helpText: 'Seleziona la data',
confirmText: 'Conferma',
cancelText: 'Annulla',
);
if (scelta != null) setState(() => _data = scelta);
}
Future<void> _selezionaOra() async {
final scelta = await showTimePicker(
context: context,
initialTime: _ora ?? TimeOfDay.now(),
builder: (context, child) => MediaQuery(
data: MediaQuery.of(context).copyWith(alwaysUse24HourFormat: true),
child: child!,
),
);
if (scelta != null) setState(() => _ora = scelta);
}
DateTime? get _appuntamento {
if (_data == null || _ora == null) return null;
return DateTime(_data!.year, _data!.month, _data!.day, _ora!.hour, _ora!.minute);
}
void _salva() {
final a = _appuntamento;
if (a == null) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Seleziona sia la data che l\'orario')),
);
return;
}
final testo = DateFormat("d MMMM y 'alle' HH:mm", 'it_IT').format(a);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Appuntamento: $testo')),
);
}
@override
Widget build(BuildContext context) {
final dataText = _data == null
? 'Nessuna data selezionata'
: DateFormat('EEEE d MMMM y', 'it_IT').format(_data!);
final oraText = _ora == null ? 'Nessun orario selezionato' : _ora!.format(context);
return Scaffold(
appBar: AppBar(title: const Text('Nuovo appuntamento')),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
Card(
child: ListTile(
leading: const Icon(Icons.calendar_today),
title: const Text('Data'),
subtitle: Text(dataText),
trailing: const Icon(Icons.chevron_right),
onTap: _selezionaData,
),
),
const SizedBox(height: 8),
Card(
child: ListTile(
leading: const Icon(Icons.access_time),
title: const Text('Orario'),
subtitle: Text(oraText),
trailing: const Icon(Icons.chevron_right),
onTap: _selezionaOra,
),
),
const SizedBox(height: 24),
FilledButton.icon(
onPressed: _salva,
icon: const Icon(Icons.save),
label: const Text('Salva appuntamento'),
),
],
),
);
}
}
Risultato atteso
Una schermata con due schede toccabili per data e orario, testi in italiano e un pulsante che riepiloga l'appuntamento scelto.