はじめに
iOSアプリで生年月日を入力する際、最も一般的なUIはドラムロール式のピッカーです。
Flutterには CupertinoDatePicker
というiOS標準のUIを再現できるウィジェットがあります。
本記事では CupertinoDatePicker
を使って、生年月日入力を実装する方法を解説します。
【補足】
違う方法でのドラムロールUIで生年月日の入力の記事も掲載中です!!
ご確認いただけましたら幸いです!
https://qiita.com/_kansae_/items/8ee0725f49d7b1b400ab
目次
- CupertinoDatePickerとは
- iOSアプリにおける生年月日入力のUI
- CupertinoDatePickerの実装方法
- CupertinoDatePickerの実装方法(日本語対応)
1. CupertinoDatePickerとは
CupertinoDatePicker
は、iOSのドラムロール形式の日付選択UIを提供するウィジェットです。
iOSネイティブの UIDatePicker
に似ており、iPhoneユーザーに馴染みのあるデザインを実現できます。
2. iOSアプリにおける生年月日入力の適正UI
iOSでは、生年月日はカレンダーよりもドラムロール形式のピッカーが一般的です。
この形式のメリットは以下の通りです。
- スクロール操作で素早く日付選択が可能
- 年、月、日を個別に選べるため間違えにくい
- iOSユーザーが違和感なく操作できる
※あくまで一般的なので、絶対に「CupertinoDatePicker」を使用してくださいというわけではありません。
3. 今回の実装について
今回はTextFormFieldをタップするとピッカーが開く 仕様にします。
この実装では、showModalBottomSheet
を使い、下からスライド表示されるモーダルで CupertinoDatePicker
を表示します。
また日本語対応についての実装例も記載しておりますので参考にしていただけたらと思います。
4. CupertinoDatePickerの実装方法
以下のコードで、生年月日選択UIを実装できます。
実装コード
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false, // デバッグバナーを非表示に
home: DatePickerExample(), // メイン画面をDatePickerExampleに設定
);
}
}
class DatePickerExample extends StatefulWidget {
const DatePickerExample({super.key});
@override
_DatePickerExampleState createState() => _DatePickerExampleState();
}
class _DatePickerExampleState extends State<DatePickerExample> {
// 生年月日を管理するためのTextEditingController
final TextEditingController _dateController = TextEditingController();
// 日付選択用のモーダルボトムシートを表示
void _showDatePicker(BuildContext context) {
showModalBottomSheet(
context: context,
builder: (BuildContext builder) {
return SizedBox(
height: 250, // ボトムシートの高さを設定
child: Column(
children: [
// CupertinoDatePicker(iOS風の日付ピッカー)
Expanded(
child: CupertinoDatePicker(
mode: CupertinoDatePickerMode.date, // 日付モードに設定
initialDateTime: DateTime(2000, 1, 1), // 初期日付
minimumDate: DateTime(1950, 1, 1), // 最小日付(1950年1月1日)
maximumDate: DateTime.now(), // 最大日付(今日の日付)
onDateTimeChanged: (DateTime newDate) {
// 日付変更時にテキストフィールドに反映
setState(() {
_dateController.text =
"${newDate.year}/${newDate.month}/${newDate.day}";
});
},
),
),
// 完了ボタン
TextButton(
onPressed: () => Navigator.pop(context), // ボトムシートを閉じる
child: Text("完了"),
)
],
),
);
},
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('生年月日入力')),
body: Padding(
padding: EdgeInsets.all(16),
child: TextFormField(
controller: _dateController,
readOnly: true, // タップでのみ日付選択可能
decoration: InputDecoration(
labelText: "生年月日",
fillColor: Colors.white,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(color: Colors.blue, width: 1.5),
),
),
// フィールドがタップされた時に日付ピッカーを表示
onTap: () => _showDatePicker(context),
),
),
);
}
}
5. 日本語表記にする方法
pubspec.yamlにflutter_localizationsを追記
・追記後 Pub get
の実行
flutter_localizations:
sdk: flutter
・4のコードにflutter_localizationsで日本語表記にするコードを追記
実装コード
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
//日本語表記にするためにflutter_localizationsのインポート
import 'package:flutter_localizations/flutter_localizations.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false, // デバッグバナーを非表示に
home: DatePickerExample(), // メイン画面をDatePickerExampleに設定
//日本語表記のためのローカライズ設定
localizationsDelegates: [
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
],
supportedLocales: [
const Locale('ja'),
],
);
}
}
class DatePickerExample extends StatefulWidget {
const DatePickerExample({super.key});
@override
_DatePickerExampleState createState() => _DatePickerExampleState();
}
class _DatePickerExampleState extends State<DatePickerExample> {
// 生年月日を管理するためのTextEditingController
final TextEditingController _dateController = TextEditingController();
// 日付選択用のモーダルボトムシートを表示
void _showDatePicker(BuildContext context) {
showModalBottomSheet(
context: context,
builder: (BuildContext builder) {
return SizedBox(
height: 250, // ボトムシートの高さを設定
child: Column(
children: [
// CupertinoDatePicker(iOS風の日付ピッカー)
Expanded(
child: CupertinoDatePicker(
mode: CupertinoDatePickerMode.date, // 日付モードに設定
initialDateTime: DateTime(2000, 1, 1), // 初期日付
minimumDate: DateTime(1950, 1, 1), // 最小日付(1950年1月1日)
maximumDate: DateTime.now(), // 最大日付(今日の日付)
onDateTimeChanged: (DateTime newDate) {
// 日付変更時にテキストフィールドに反映
setState(() {
_dateController.text =
"${newDate.year}/${newDate.month}/${newDate.day}";
});
},
),
),
// 完了ボタン
TextButton(
onPressed: () => Navigator.pop(context), // ボトムシートを閉じる
child: Text("完了"),
)
],
),
);
},
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('生年月日入力')),
body: Padding(
padding: EdgeInsets.all(16),
child: TextFormField(
controller: _dateController,
readOnly: true, // タップでのみ日付選択可能
decoration: InputDecoration(
labelText: "生年月日",
fillColor: Colors.white,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(color: Colors.blue, width: 1.5),
),
),
// フィールドがタップされた時に日付ピッカーを表示
onTap: () => _showDatePicker(context),
),
),
);
}
}
まとめ
Flutterを使用するようになりAndroidでも作成が可能という点から
iOSではどのようなUIが適切なのかより考えさせられるようになりました。
今回の生年月日入力についても、使用するターゲットや使用用途によっては
カレンダーUIの方が良かったりするのかもしれません。
絶対にこれだ!!はないと思うので臨機応変に対応できるよう
実装の知見を増やしていきたいと思う今日この頃でした👀
誤った内容がありましたらコメントお願い致します!