🌊
【Flutter】アプリのどこにいてもメンテナンスモードを作動させる
はじめに
モバイルアプリの要件に「メンテナンスモード」が含まれることは多いと思います。
しかし、ネット上には「main()
内でメンテナンスフラグを取得してtrue
の時はメンテナンス画面に遷移させる」ような 「アプリ起動時にしか作動しないメンテナンスモード」 の記事はたくさんありますが、 「アプリのどこからでも作動し、かつ、自動解除もするメンテナンスモード」 の記事はあまり無いように感じたので書いてみました。
前提
下記を使用
- Firebase / Remote Config
- Riverpod
- GoRouter
実現方法(概要)
ざっくり下記を実装すればOKです
- リアルタイムRemoteConfigのリッスン
- 上記のProviderまたはNotifier
- GoRouterで上記ProviderまたはNotifierをwatch
- MaterialAppで上記GoRouterをwatch
実現方法(具体)
実現方法(概要)をコード付きで解説していきます。
1.リアルタイムRemoteConfigのリッスン
リッスン自体は公式Doc通りに下記で行えます。
import 'package:firebase_remote_config/firebase_remote_config.dart';
// インスタンス取得と初期設定
FirebaseRemoteConfig remoteConfig = FirebaseRemoteConfig.instance;
await remoteConfig.setConfigSettings(RemoteConfigSettings(
fetchTimeout: const Duration(minutes: 1),
// 自動フェッチしてくれるので0でOK。
// 自動フェッチを使わない場合は明示的なポーリングが必要なので当該値の設定が必要。
minimumFetchInterval: const Duration(hours: 0),
));
// 対象設定変数のデフォルト値の設定(なくてもいいけどあった方が安全)
await remoteConfig.setDefaults(const {"is_maintenance": false});
// リアルタイムRemoteConfigのリッスンを開始
remoteConfig.onConfigUpdated.listen((RemoteConfigUpdate update) async {
// 更新された値を有効化
await remoteConfig.activate();
// あとはやりたき処理を実装...
});
2. 上記のProviderまたはNotifier
Riverpodを使って上記リスナーの永続化と取得した設定変数をstateで状態管理します。
firebase_remote_config_notifier.dart
import 'package:firebase_remote_config/firebase_remote_config.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'firebase_remote_config_notifier.g.dart';
(keepAlive: true) // 永続化。代わりにeager_initializationでもOK。
// Notifierにしてるけど、別でServiceを定義してそれのProviderでもOK。
class FirebaseRemoteConfigNotifier extends _$FirebaseRemoteConfigNotifier {
late FirebaseRemoteConfig remoteConfig;
bool build() {
initialize();
return remoteConfig.getBool('is_maintenance');
}
Future<void> initialize() async {
remoteConfig = FirebaseRemoteConfig.instance;
await remoteConfig.setConfigSettings(RemoteConfigSettings(
fetchTimeout: const Duration(minutes: 1),
minimumFetchInterval: const Duration(hours: 0),
));
await remoteConfig.setDefaults(const {
"is_maintenance": false,
});
remoteConfig.onConfigUpdated.listen((RemoteConfigUpdate update) async {
await remoteConfig.activate();
state = remoteConfig.getBool('is_maintenance');
});
}
}
3. GoRouterで上記ProviderまたはNotifierをwatch
GoRouterでルーティングを定義して、かつ、上記Notifierをwatchします。
go_router.dart
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:sample/notifier/firebase_remote_config_notifier.dart';
part 'go_router.g.dart';
GoRouter goRouter(GoRouterRef ref) {
// 設定変数をwatch
bool isMaintenance = ref.watch(firebaseRemoteConfigNotifierProvider);
return GoRouter(
// ここで設定変数に応じて分岐
initialLocation: isMaintenance ? '/maintenance' : '/home',
routes: [
GoRoute(
path: '/maintenance',
builder: (context, state) => const MaintenanceView(),
routes: const []),
GoRoute(
path: '/home',
builder: (context, state) => const HomeView(),
routes: const []),
]);
}
4. MaterialAppで上記GoRouterをwatch
最後にMaterialAppのrouterConfigにGoRouterをセットします。
main.dart
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:sample/go_router.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Firebaseの初期化
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
runApp(ProviderScope(child: const MyApp()));
}
class MyApp extends ConsumerWidget {
const MyApp({super.key});
Widget build(BuildContext context, WidgetRef ref) {
return MaterialApp.router(routerConfig: ref.watch(goRouterProvider);
}
}
以上
RemoteConfigの設定変数値の更新をトリガーにGoRouterがリビルドされて、アプリのどこにいてもメンテナンスモードが作動できます。
また、設定変数値が更新されると自動でメンテナンス画面からホーム画面に遷移します。
Discussion