FlutterのLocalizationについできたこと~CupertinoDatePickerを日本語にできた~
こんにちは。VyseArtのトナカイです。
最近Flutterを学んでいます。
私はIT関連企業の事務系で社内ツールを作っているのですが、
今後、今の仕事でもFlutterつかえたらいいなという淡い期待をしていて、
ちょっとずつ勉強しています。
あとFlutterを使ってどうしても作ってみたいツールがあるんです。
SNS要素があるのですが、使ってくれた人が希望を持てるようなツールを作りたいと思っています!
今回は下記のような実装ができました。
Cupertino(iOS風)のウィジェットってかっこいいですよね^^
実装にあたり、もっと簡単な方法があるんじゃないだろうかと迷ったり、私的につまずいた部分があったので、
備忘録もかねて記事を書いていこうと思います。
Localizationしたくなった理由
そのように思った発端ですが、CupertinoDatePickerを使いたいと思ったら、英語表記だったことです。
同じ質問がstack over flowにありました。
この質問の解決方法を真似て日本語版のスクリプトを作ったところ、上記の画像のように日本語化することができました。
Localizationてどうやるの?
pubspec.yamlに下記を記述し、ターミナルでflutter pub getします。
dependencies: flutter: sdk: flutter flutter_localizations: sdk: flutter flutter_cupertino_localizations: ^1.0.1
次に、下記をインポートし、localizationsDelegates と supportedLocalesを記述します。
↓main.dart
import 'package:flutter/cupertino.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; MaterialApp( localizationsDelegates: [ GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, DefaultCupertinoLocalizations.delegate, ], supportedLocales: [ const Locale('en', 'US'), const Locale('ja', 'JP'), ], locale: Locale('ja', 'JP'),
次に、stack over flowの質問回答記事にあるように、Localizeしたい国の言語(自国語)のスクリプトを作っていきます。
↓JapaneseCupertinoLocalizations.dart
import 'dart:async'; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; class _CupertinoLocalizationDelegate extends LocalizationsDelegate<CupertinoLocalizations> { const _CupertinoLocalizationDelegate(); @override bool isSupported(Locale locale) => locale.languageCode == 'ja'; @override Future<CupertinoLocalizations> load(Locale locale)=> JapaneseCupertinoLocalizations.load(locale); @override bool shouldReload( _CupertinoLocalizationDelegate old) => false; @override String toString() => 'DefaultCupertinoLocalizations.delegate(ja_JP)'; } class JapaneseCupertinoLocalizations implements CupertinoLocalizations{ const JapaneseCupertinoLocalizations(); static const List<String> _shortWeekdays = <String>[ '(月)', '(火)', '(水)', '(木)', '(金)', '(土)', '(日)', ]; static const List<String> _shortMonths = <String> [ '1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月', ]; static const List<String> _months = <String>[ '1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月', ]; @override String datePickerYear(int yearIndex) => yearIndex.toString(); @override String datePickerMonth(int monthIndex) => _months[monthIndex -1]; @override String datePickerDayOfMonth(int dayIndex) => dayIndex.toString(); @override String datePickerHour(int hour) => hour.toString(); @override String datePickerHourSemanticsLabel( int hour) => hour.toString() + "時"; @override String datePickerMinute(int minute) => minute.toString().padLeft(2, '0'); @override String datePickerMinuteSemanticsLabel( int minute) { if(minute == 1) return '1 分'; return minute.toString() + '分'; } @override String datePickerMediumDate(DateTime date) { return '${_shortMonths[date.month - DateTime.january]} ' '${date.day.toString().padRight(2)+ '日'}' '${_shortWeekdays[date.weekday - DateTime.monday] } '; } @override DatePickerDateOrder get datePickerDateOrder => DatePickerDateOrder.mdy; @override DatePickerDateTimeOrder get datePickerDateTimeOrder => DatePickerDateTimeOrder.date_time_dayPeriod; @override String get anteMeridiemAbbreviation => '午前'; @override String get postMeridiemAbbreviation => '午後'; @override String get alertDialogLabel => 'Info'; @override String timerPickerHour(int hour ) => hour.toString(); @override String timerPickerMinute(int minute) => minute.toString(); @override String timerPickerSecond(int second) => second.toString(); @override String timerPickerHourLabel(int hour) => hour == 1 ? '時' : '時'; //参考の書式のまま @override String timerPickerMinuteLabel(int minute) => '分'; @override String timerPickerSecondLabel(int second) => '秒'; @override String get cutButtonLabel => 'カット'; @override String get copyButtonLabel => 'コピー'; @override String get pasteButtonLabel => 'ペースト'; @override String get selectAllButtonLabel => '選択'; @override String get todayLabel => '今日'; //参考サイトにはなかったが、参照しないとエラーが出るので独自で追加 /// Creates an object that provides US English resource values for the /// cupertino library widgets. /// /// The [locale] parameter is ignored. /// /// This method is typically used to create a [LocalizationsDelegate]. /// static Future<CupertinoLocalizations> load(Locale locale) { return SynchronousFuture<CupertinoLocalizations>(const JapaneseCupertinoLocalizations()); } /// A [LocalizationsDelegate] that uses [DefaultCupertinoLocalizations.load] /// to create an instance of this class. static const LocalizationsDelegate<CupertinoLocalizations> delegate = _CupertinoLocalizationDelegate(); }
このJapaneseCupertinoLocalizations.dartを先ほどのmain.dartにインポートします。
import 'JapaneseCupertinoLocalizations.dart' as jcl; //(ほかのライブラリと競合したのでas jclとしている)
そして、先ほどのlocalizationsDelegatesにこのライブラリのデリゲートを追加します。
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
DefaultCupertinoLocalizations.delegate,
jcl.JapaneseCupertinoLocalizations.delegate,
],
以上でCupertinoDatePickerを呼び出したときにLocalizeできているはずです。
CupertinoDatePickerの呼び出し方
Widget _buildBottomPicker(Widget picker) { return Container( height:216, padding:const EdgeInsets.only(top:6.0), color: CupertinoColors.white, child:DefaultTextStyle( style:const TextStyle( color:CupertinoColors.black, fontSize:22.0, ), child: GestureDetector( onTap:(){}, child:SafeArea( top:false, child: picker, ) ) ), ); }
_buildBottomPickerという関数を作ります。Containerでラップして、GestureDetectorはタップ処理を入れたいときに使うのでしょうか。その子にSafeAreaでラップしてウィジェットがはみ出ないようにしています。
引数のpickerが一番下階層の子になりそれが表示される仕組みです。
CupertinoButton( child:Text('来所時間'), onPressed: () async{ await showCupertinoModalPopup<void>( context: context, builder: (BuildContext context) { return _buildBottomPicker( CupertinoDatePicker( mode: CupertinoDatePickerMode.dateAndTime, initialDateTime: _date, onDateTimeChanged: (DateTime newDateTime) { setState(() { _date = newDateTime; }); }, ), ); }, ); debugPrint('閉じました'); }, ),
実際にこの_buildBottomPickerを使うときは下記のようにButtonのonPressedプロパティに、
showCupertinoModalPopup関数を入れ(てawaitさせ)ました。
showCupertinoModalPopupが返すウィジェットに_buildBottomPickerを設定して、
_buildBottomPickerの引数pikerにCupertinoDatePickerを設定しています。
こうすればポップアップが開かれ、CupertinoDatePickerのホイールが回り、
ポップアップが閉じられたとき
debugPrint('閉じました');
のログが残ります。
awaitの使い方がこれでいいのかなど、
初心者ながら自身薄ですが、
こういう風にしてFlutterの使えるコードを増やし、
成果物を出すことによって自信をつけていこうと思います。
以上。