[Widget]ドロップダウンで時間選択用のダイアログを作る

動作環境: Flutter 2.8.1 Dart 2.15.1
LINEでこのページを共有するTwitterでこのページを共有するこのページをはてなブックマークに追加

DropdownButtonを使って時刻を選択するサンプルを作りました。
引数に選択範囲の最小と最大を指定すると、その範囲内に限定した状態で選択できるようになります。

lib/main.dart
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Builder(builder: (context) {
return Scaffold(
appBar: AppBar(
title: const Text('サンプル'),
centerTitle: true,
backgroundColor: Colors.green,
),
body: Container(
color: Colors.green.shade200,
padding: const EdgeInsets.all(20.0),
child: Center(
child: ElevatedButton(
child: const Text('時間を決める', style: TextStyle(fontSize: 30.0)),
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) => const TimePickerDialog(
minTime: TimeOfDay(hour: 9, minute: 30),
maxTime: TimeOfDay(hour: 24, minute: 0),
));
},
),
),
),
);
}),
);
}
}
class TimePickerDialog extends StatefulWidget {
const TimePickerDialog(
{Key? key, required this.minTime, required this.maxTime})
: super(key: key);
final TimeOfDay minTime;
final TimeOfDay maxTime;
@override
State<TimePickerDialog> createState() => _TimePickerDialogState();
}
class _TimePickerDialogState extends State<TimePickerDialog> {
int hour = 0;
int minute = 0;
@override
void initState() {
hour = widget.minTime.hour;
minute = widget.minTime.minute;
super.initState();
}
@override
Widget build(BuildContext context) {
return AlertDialog(
title: const Text('時間を設定してください'),
content: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
DropdownButton(
items: selectableHours.map<DropdownMenuItem<int>>((int value) {
return DropdownMenuItem<int>(
value: value,
child: Text(
value.toString(),
style: const TextStyle(fontSize: 30.0),
),
);
}).toList(),
value: hour,
onChanged: (int? newValue) {
setState(() {
hour = newValue!;
minute = selectableMinMinute;
});
}),
const Text(
':',
style: TextStyle(fontSize: 30.0),
),
DropdownButton(
items:
selectableMinutes.map<DropdownMenuItem<int>>((int value) {
return DropdownMenuItem<int>(
value: value,
child: Text(
value.toString(),
style: const TextStyle(fontSize: 30.0),
),
);
}).toList(),
value: minute,
onChanged: (int? newValue) {
setState(() {
minute = newValue!;
});
}),
],
),
actions: [
ElevatedButton(
child: const Text('Cancel'),
onPressed: () => Navigator.pop(context),
),
ElevatedButton(
child: const Text('OK'),
onPressed: () {
print('selected time is $hour:$minute');
},
),
]);
}
List<int> get selectableHours {
return List<int>.generate((widget.maxTime.hour - widget.minTime.hour + 1),
(int index) => widget.minTime.hour + index);
}
int get selectableMinMinute {
return widget.minTime.hour == hour ? widget.minTime.minute : 0;
}
int get selectableMaxMinute {
return widget.maxTime.hour == hour ? widget.maxTime.minute : 59;
}
List<int> get selectableMinutes {
return List<int>.generate((selectableMaxMinute - selectableMinMinute + 1),
(int index) => selectableMinMinute + index);
}
}

Flutterだと、TimePickerというWidgetを呼び出す方法があります。
通常だとこちらの方が手軽で便利ですが、時刻の選択範囲が0から24時間という欠点があります。

例えば、25時(深夜1時)を選択できるようにしたい、みたいな要件があったときはTimePickerは使えません。
そうしたときはWidgetを駆使して自分で作らないといけないですね。
あとは、TimePickerのグルグルUIを採用したくないときも該当すると思います。

サンプルでは引数をTimeOfDayというFlutter組み込みのクラスで取り扱っていますが、実は25時以降を選べるようにしたいときはこのクラスは使えません。
引数の渡し方も独自に工夫する必要がありそうです。

*「Run」を押すと実行できます。