Open10

DatePickerの週始まりを日曜から月曜にしたい

m.kosukem.kosuke

Localeで変わるということがわかったので、CalendarDatePickerの中を見ると、 MaterialLocalizations#firstDayOfWeekIndex を参照して、週始まりを決めていた

https://github.com/flutter/flutter/blob/3.3.5/packages/flutter/lib/src/material/calendar_date_picker.dart#L922-L934

(Flutter 3.3.5で確認)

firstDayOfWeekIndex の定義はこちら

https://github.com/flutter/flutter/blob/3.3.5/packages/flutter/lib/src/material/material_localizations.dart#L345-L355

m.kosukem.kosuke

MaterialLocalizations とは?

https://github.com/flutter/flutter/blob/3.3.5/packages/flutter/lib/src/material/material_localizations.dart#L75-L83

Materialウィジェットで利用されるリソースの定義群。

  • DefaultMaterialLocalizations
    • 実装クラス
    • 英語
  • GlobalMaterialLocalizations
    • その他の言語をサポート
    • 抽象クラス

https://github.com/flutter/flutter/blob/3.3.5/packages/flutter_localizations/lib/src/material_localizations.dart#L199-L200

GlobalMaterialLocalizationsの実装クラスはgenerated_material_localizations.dartに多くの言語でgenerateされている。日本語バージョンは下記 GlobalMaterialLocalizationsJa

https://github.com/flutter/flutter/blob/3.3.5/packages/flutter_localizations/lib/src/l10n/generated_material_localizations.dart#L18651

m.kosukem.kosuke

日本語用の GlobalMaterialLocalizationsJaで、firstDayOfWeekをoverrideすれば対応できるのではないかと予測。

m.kosukem.kosuke

カスタム GlobalMaterialLocalizationsJa

/// カレンダーの週始まりを月曜にするため、[MaterialLocalizationJa]をoverrideして
/// [firstDayOfWeekIndex]を変更します。
class CustomLocalization extends MaterialLocalizationJa {
  /// コンストラクタの引数は、Flutter層の実装をほぼそのまま持ってきています。
  CustomLocalization()
      : super(
          fullYearFormat: DateFormat('y年', 'ja'),
          compactDateFormat: DateFormat('y/M/d', 'ja'),
          shortDateFormat: DateFormat('y年M月d日', 'ja'),
          mediumDateFormat: DateFormat('M月d日(E)', 'ja'),
          longDateFormat: DateFormat('y年M月d日EEEE', 'ja'),
          yearMonthFormat: DateFormat('y年M月', 'ja'),
          shortMonthDayFormat: DateFormat('M月d日', 'ja'),
          decimalFormat: NumberFormat('#,##0.###', 'ja'),
          twoDigitZeroPaddedFormat: NumberFormat('00', 'ja'),
        );

  /// 週の始まりを月曜(1)に設定。
  @override
  int get firstDayOfWeekIndex => 1;

  static const LocalizationsDelegate<MaterialLocalizations> delegate =
      _CustomDelegate();
}

class _CustomDelegate extends LocalizationsDelegate<MaterialLocalizations> {
  const _CustomDelegate();

  @override
  Future<MaterialLocalizations> load(Locale locale) async {
    // 言語リソースをloadするため、先に[GlobalMaterialLocalizations]をコールする
    final defaultLocalization =
        await GlobalMaterialLocalizations.delegate.load(locale);

    // 日本語の場合は[CustomLocalizations]で上書きする
    if (locale.languageCode == 'ja') {
      return CustomLocalization();
    }

    // それ以外の時は[GlobalMaterialLocalizations]から取得したものを利用する
    return defaultLocalization;
  }

  @override
  bool isSupported(Locale locale) {
    return locale.languageCode == 'ja';
  }

  @override
  bool shouldReload(_CustomDelegate old) => false;
}
m.kosukem.kosuke

CustomLocalizationのポイント

  • 基本、日本語設定をそのまま使いたいので、MaterialLocalizationJaをimplementして作成
  • superコンストラクタへの引数は、Flutterコードの中を見てほぼそのまま持ってきました。
    • もちろん、カスタマイズしたければカスタマイズしても良さそう
    • Localeにjaを指定しないと、DateFormatがデフォルトの英語表記になってしまうので、ちゃんと指定する
  • firstDayOfWeekIndexをoverrideして1(月曜)に固定する。これが一番やりたかったこと
  • delegateの部分は、他のコードを参考に。

_CustomDelegateのポイント

  • loadでは、先にLocale(翻訳リソース)を読み込ませる必要があるため、GlobalMaterialLocalizations.delegate.load(locale)をコールしておく必要がある
    • その上で、日本語であればCustomLocalizationを返却する
    • それ以外の言語の場合は、そのままGlobalMaterialLocalizations.delegate.load(locale)の戻り値を返す

参考)
https://github.com/flutter/flutter/blob/3.3.5/packages/flutter_localizations/lib/src/material_localizations.dart#L666-L671