Flutter 2.5 スケルトンをながめる(4)
前回 の続き。 Flutter2.5からスケルトンが提供されるようになったので、それをながめてみる。
6. settings
settingsディレクトリをながめる。このディレクトリには3つのファイルが含まれている。
サフィックスのとおり、コントローラー・サービス・画面である。
settingsを設定する画面と、その機能を提供するコントローラー、コントローラーのバックエンドとしてデーターの保存・復元を行うサービスが、ひとつのディレクトリに置かれている。
└── src
└── settings
├── settings_controller.dart
├── settings_service.dart
└── settings_view.dart
6.1. SettingsView
まず、コンストラクタで、SettingsControllerを引数で受け取って保存している。
つぎに、SampleItemListViewと同様に、画面遷移のためのrouteNameを設定している(4.3. 画面遷移(ルート)を参照)。
最後に、build関数で画面を生成している。
appBarは、タイトルを設定しているのみである。
bodyには、DropdownButtonをおき、3つのテーマモード(system/light/dark)を選択可能にしている。 valueにはcontroller.themeMode、onChangedにはcontroller.updateThemeModeを設定しており、SettingsControllerと連携している。
child: DropdownButton<ThemeMode>(
value: controller.themeMode,
onChanged: controller.updateThemeMode,
items: const [
onChangedでは、controller.updateThemeModeを引数なしで呼び出している。これで選択された値が渡るのか。
6.2. SettingsController
SettingsControllerクラスは、ChangeNotifierをmixinしている。 コンストラクタでSettingsServiceを受け取り、プライベート変数に保持している。 変数名を_(アンダースコア)で始めることでプライベート変数となるのは、Dartの言語仕様。
また、プロパティthemeModeを読み取り専用に設定している。
late ThemeMode _themeMode;
ThemeMode get themeMode => _themeMode;
メソッドは2つ、loadSettingsとupdateThemeMode。
loadSettingsは、保存されたサービスから読み込んでプロパティに保持するための関数。 このスケルトンでは、プロパティはthemeModeのみ。読み込み後は、notifyListeners関数を実行して、ウィジェットへの通知を行う。
updateThemeModeは、themeModeを変更するための関数。SettingsView内のDropdownButtonのonChangedから呼び出される。 呼び出し元では引数なしだが、こちらでは引数を受け取っている。仮引数がNull許容型なので、Nullチェックを行った後、プロパティに保存している。 プロパティはNull非許容型なので、チェックなしだとコンパイルエラーとなる。
Future<void> updateThemeMode(ThemeMode? newThemeMode) async {
if (newThemeMode == null) return;
_themeMode = newThemeMode;
つづいてnotifyListeners関数を実行してウィジェットへの通知を行う。
app.dartで生成しているMaterialApp内で、themeModeにこのコントローラーのthemeModeプロパティを割り当てているので、
このプロパティを変更することでアプリケーションのテーマの変更が反映される。
app.dart
theme: ThemeData(),
darkTheme: ThemeDate.dark(),
themeMode: settingsController.themeMode,
最後に、サービスを使って設定値を保存している。
6.3. SettingsService
サービスの読取・保存を担当するクラスだが、その機能はスケルトンでは実装されていない。
メソッドは2つ、themeModeとupdateThemeMode。
themeMode関数は、つねにThemeMode.systemを返却している。
updateThemeModeは、なにもしていない。
6.4. 改造
ということで、保存できるように改造する。
settings_service.dart内のコメントにあるように、shared_preferencesを使う。
Installingタブにあるとおりに、pubspec.yamlに追記した後、pub getを実行しておく。 pub getの後は、ホットリロードではなく、再デプロイが必要となる。
pubspec.yaml
dependencies:
flutter:
sdk: flutter
shared_preferences: ^2.0.8 //追記
SettingsServiceを変更する。
settings_service.dart
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
class SettingsService {
static const themeModeKey = 'themeMode';
Future<ThemeMode> themeMode() async {
final _storage = await SharedPreferences.getInstance();
var _themeMode = _storage.getString(themeModeKey);
switch (_themeMode) {
case 'ThemeMode.light':
return ThemeMode.light;
case 'ThemeMode.dark':
return ThemeMode.dark;
default:
return ThemeMode.system;
}
}
Future<void> updateThemeMode(ThemeMode theme) async {
final _storage = await SharedPreferences.getInstance();
await _storage.setString(themeModeKey, theme.toString());
}
}
モード名は列挙型にすべきかもしれないが、まずは、これで動作確認。
ところが、SharedPreferences.getInstance();でエラーが発生した。
E/flutter (10113): [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: Null check operator used on a null value
どうやら、事前にWidgetsFlutterBinding.ensureInitialized()を呼び出しておく必要があるらしい。
参考記事によると「一言で言うとrunApp()を呼び出す前にFlutter Engineの機能を利用したい場合にコールします」とのこと。
参考 [Flutter] WidgetsBindingとは何か?
たしかに、app.dart内で、runApp()の前にSettingsController.loadSettings()を呼び出しており、これがSettingsService.themeMode()を呼び出している。
app.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
この一行を追加することで、動作するようになった。
スケルトンをながめるのは、この記事で終了である。