Cupertino widgets⑤
ドラムになっている時間入力をする
iOSだとホイールというらしいが、Flutterドラムロールと呼んでいたような。あのスクロールするUIを再現したい。
CupertinoDatePicker classを使用すれば実現できますが、公式のコードだと英語のままなので日本語対応が必要です。他の国の言語に対応するのをローカライズというらしい。
公式の解説もしておくか
A date picker widget in iOS style.
There are several modes of the date picker listed in CupertinoDatePickerMode.
The class will display its children as consecutive columns. Its children order is based on internationalization, or the dateOrder property if specified.
Example of the picker in date mode:
US-English: | July | 13 | 2012 |
Vietnamese: | 13 | Tháng 7 | 2012 |
Can be used with showCupertinoModalPopup to display the picker modally at the bottom of the screen.
Sizes itself to its parent and may not render correctly if not given the full screen width. Content texts are shown with CupertinoTextThemeData.dateTimePickerTextStyle.
iOSスタイルの日付ピッカーウィジェット。
日付ピッカーにはいくつかのモードがあり、CupertinoDatePickerModeにリストされている。
このクラスは連続した列として子を表示します。その子の順序は国際化、または指定された場合はdateOrderプロパティに基づきます。
日付モードでのピッカーの例:
US-English: | 7月|13日|2012年|月|日
ベトナム語 | 13|Tháng 7|2012| ベトナム語:7月13日
showCupertinoModalPopupと併用することで、ピッカーを画面下部にモーダルに表示することができます。
画面幅いっぱいに表示されない場合、親画面と同じサイズになり、正しくレンダリングされない場合があります。コンテンツテキストはCupertinoTextThemeData.dateTimePickerTextStyleで表示されます。
Flutter3.27.0だとintl 0.19.0じゃないと失敗しました。intlはバージョンのエラー出やすいので注意が必要です😅
flutter_localizations
も追加が必要。
Flutter 3.27.0/Dart 3.6.0だとintl 0.19.0
が相性が良かった。
hashimotojunichi@hashimotojunichinoMac-mini widget_cookbook_demo % flutter --ver
sion
Flutter 3.27.0 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 8495dee1fd (3 weeks ago) • 2024-12-10 14:23:39 -0800
Engine • revision 83bacfc525
Tools • Dart 3.6.0 • DevTools 2.40.2
こんな感じで設定すればOKだった。パッケージ色々入れてますがintl
とflutter_localizations
だけでOKです。
pubspec.yamlの設定
name: widget_cookbook_demo
description: "A new Flutter project."
# The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
version: 1.0.0+1
environment:
sdk: ^3.6.0
# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
# consider running `flutter pub upgrade --major-versions`. Alternatively,
# dependencies can be manually updated by changing the version numbers below to
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter # Flutterのローカライゼーション
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.8
flutter_hooks: ^0.20.5
lottie: ^3.3.0
image_picker: ^1.1.2
intl: ^0.19.0
dev_dependencies:
flutter_test:
sdk: flutter
# The "flutter_lints" package below contains a set of recommended lints to
# encourage good coding practices. The lint set provided by the package is
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^5.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter packages.
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
# To add assets to your application, add an assets section, like this:
assets:
- assets/
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/to/resolution-aware-images
# For details regarding adding assets from package dependencies, see
# https://flutter.dev/to/asset-from-package
# To add custom fonts to your application, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts from package dependencies,
# see https://flutter.dev/to/font-from-package
example
こちらが全体のコード。この個人的に好きですね💙🤎
日本語対応したCupertinoDatePicker class
でサンプルを作成。
example
import 'package:flutter/cupertino.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:intl/intl.dart';
import 'package:intl/date_symbol_data_local.dart';
void main() {
initializeDateFormatting('ja_JP').then((_) {
runApp(const DatePickerApp());
});
}
class DatePickerApp extends StatelessWidget {
const DatePickerApp({super.key});
Widget build(BuildContext context) {
return const CupertinoApp(
// ここにローカライゼーションの設定を追加
localizationsDelegates: <LocalizationsDelegate<dynamic>>[
// iOSスタイルのローカライゼーション
GlobalCupertinoLocalizations.delegate,
// マテリアルデザインのローカライゼーション
GlobalMaterialLocalizations.delegate,
// 汎用的なローカライゼーション
GlobalWidgetsLocalizations.delegate,
],
// サポートする言語を指定
supportedLocales: [
Locale('ja', 'JP'),
],
// 言語を日本語に設定
locale: Locale('ja', 'JP'),
theme: CupertinoThemeData(brightness: Brightness.light),
home: DatePickerExample(),
);
}
}
class DatePickerExample extends StatefulWidget {
const DatePickerExample({super.key});
State<DatePickerExample> createState() => _DatePickerExampleState();
}
class _DatePickerExampleState extends State<DatePickerExample> {
DateTime date = DateTime.now();
DateTime time = DateTime.now();
DateTime dateTime = DateTime.now();
final dateFormat = DateFormat.yMMMEd('ja');
final timeFormat = DateFormat.Hm('ja');
final dateTimeFormat = DateFormat.yMMMEd('ja').add_Hm();
void _showDialog(Widget child) {
showCupertinoModalPopup<void>(
context: context,
builder: (BuildContext context) => Container(
height: 216,
padding: const EdgeInsets.only(top: 6.0),
margin: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
),
color: CupertinoColors.systemBackground.resolveFrom(context),
child: SafeArea(
top: false,
child: child,
),
),
);
}
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: const CupertinoNavigationBar(
middle: Text('日付選択'),
),
child: DefaultTextStyle(
style: TextStyle(
color: CupertinoColors.label.resolveFrom(context),
fontSize: 17.0,
),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_DatePickerItem(
children: <Widget>[
const Text('日付'),
CupertinoButton(
onPressed: () => _showDialog(
CupertinoDatePicker(
initialDateTime: date,
mode: CupertinoDatePickerMode.date,
use24hFormat: true,
showDayOfWeek: true,
onDateTimeChanged: (DateTime newDate) {
setState(() => date = newDate);
},
),
),
child: Text(
dateFormat.format(date),
style: const TextStyle(fontSize: 17.0),
),
),
],
),
_DatePickerItem(
children: <Widget>[
const Text('時刻'),
CupertinoButton(
onPressed: () => _showDialog(
CupertinoDatePicker(
initialDateTime: time,
mode: CupertinoDatePickerMode.time,
use24hFormat: true,
onDateTimeChanged: (DateTime newTime) {
setState(() => time = newTime);
},
),
),
child: Text(
timeFormat.format(time),
style: const TextStyle(fontSize: 17.0),
),
),
],
),
_DatePickerItem(
children: <Widget>[
const Text('日時'),
CupertinoButton(
onPressed: () => _showDialog(
CupertinoDatePicker(
initialDateTime: dateTime,
use24hFormat: true,
onDateTimeChanged: (DateTime newDateTime) {
setState(() => dateTime = newDateTime);
},
),
),
child: Text(
dateTimeFormat.format(dateTime),
style: const TextStyle(fontSize: 17.0),
),
),
],
),
],
),
),
),
);
}
}
class _DatePickerItem extends StatelessWidget {
const _DatePickerItem({required this.children});
final List<Widget> children;
Widget build(BuildContext context) {
return DecoratedBox(
decoration: const BoxDecoration(
border: Border(
top: BorderSide(
color: CupertinoColors.inactiveGray,
width: 0.0,
),
bottom: BorderSide(
color: CupertinoColors.inactiveGray,
width: 0.0,
),
),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: children,
),
),
);
}
}
まとめ
Cupertino widgetsを使ったアプリを開発しそうなプロジェクトに入りそうなので技術調査をしてみました。同じプロジェクトで、iOSだとCupertino widgetsに切り替える設定をするのかなと思うとエラーで詰まりそうで怖い😱
FlutterだとMaterial Designで開発することが基本だが、独自デザインを採用することもあり対応も必要な場面が出てくることもあったりします。
Discussion