Flutterから利用できるFirebaseサービスをカウンターアプリで実践(Remote Config編)
はじめに
Flutterから利用できるFirebaseの機能をカウンターアプリに実装し、内容をまとめたシリーズの第4回目です。
今回はアプリのアップデートを公開しなくても、動作や外観を変更できるFirebase Remote Configについてです。
公式では、以下のようなRemote Configのユースケースが紹介されています。
目次
シリーズの内容
回数 | 内容 | リンク |
---|---|---|
第0回 | 準備編 | ブログ |
第1回 | Analytics |
ブログ |
第2回 | Firebase Crashlytics |
ブログ |
第3回 | Firebase Performance Monitoring |
ブログ |
第4回 | Firebase Remote Config |
イマココ |
第5回 | Firebase Authentication |
ブログ |
第6回 | Cloud Firestore |
ブログ |
第7回 | Firebase Realtime Database |
ブログ |
第8回 | Cloud Storage for Firebase |
ブログ |
第9回 | Firebase Cloud Messaging |
ブログ |
第10回 | Firebase In-App Messaging |
ブログ |
第11回 | Firebase ML |
ブログ |
第12回 | Cloud Functions for Firebase |
ブログ |
第13回 | Firebase Hosting |
ブログ |
第14回 | Firebaseのその他のサービス | ブログ |
開発環境
項目 | 内容 |
---|---|
PC | Macbook Air(M1) |
Flutter | 3.0.1 |
Firebase | 11.0.1 |
FlutterFire | 0.2.2+2 |
デバッグデバイス | Android 12(APIレベル31) , Chrome |
準備
準備編が完了出来ているものとします。
導入方法
プロジェクトにfirebase_remote_configを導入するため、pubspec.yaml
に追記しインポートします。
Remote Config
を使用するために、firebase_analyticsも導入します。
dependencies: firebase_remote_config: ^2.0.7 firebase_analytics: ^9.1.8
初期化とパラメータの設定を行うため、メソッドを作って実行します。
シングルトンオブジェクトの取得では、最小フェッチ間隔を制御して、最適な時間を更新頻度にします。
アプリ内で使うパラメータの取得には、getString()
, getBool()
などのメソッドを使います。
import 'package:firebase_remote_config/firebase_remote_config.dart'; /// Firebase Remote Configの初期設定 class FirebaseRemoteConfigService { void initRemoteConfig() async { /// インスタンスの作成 final remoteConfig = FirebaseRemoteConfig.instance; /// シングルトンオブジェクトの取得 await remoteConfig.setConfigSettings(RemoteConfigSettings( fetchTimeout: const Duration(minutes: 1), minimumFetchInterval: const Duration(minutes: 5), )); /// アプリ内デフォルトパラメータ値の設定 await remoteConfig.setDefaults(const { "example_param": "Hello, world!", }); /// 値をフェッチ await remoteConfig.fetchAndActivate(); } }
初期化します。
@override void initState() { super.initState(); /// Firebase Remote Configの初期化 FirebaseRemoteConfigService().initRemoteConfig(); }
Text Widget
に書き出すと、設定した値(Hello, world!
)が出力されていることがわかります。
Text(FirebaseRemoteConfig.instance.getString("example_param")),
次に、値を変更するため、Firebase ConsoleのRemote Config
からバックエンド側の構成をセットアップします。
パラメータキーにsetDefaults
で決めたキーを入力、Default value
に新しい値を入力し、「変更を公開」します。
しばらくするとテキストがこんにちは、世界
に変更されていることが確認できました。
コード全文
前回からの変更点です。
Remote Config
を実装- Remote Configページを追加
- その他、コードの変更
main.dart
/// Flutter関係のインポート import 'package:counter_firebase/remote_config_page.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'dart:async'; /// Firebase関係のインポート import 'package:firebase_core/firebase_core.dart'; import 'firebase_options.dart'; import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:firebase_crashlytics/firebase_crashlytics.dart'; /// 他ページのインポート import 'package:counter_firebase/normal_counter_page.dart'; import 'package:counter_firebase/crash_page.dart'; /// メイン void main() async { /// クラッシュハンドラ runZonedGuarded<Future<void>>(() async { /// Firebaseの初期化 WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp( options: DefaultFirebaseOptions.currentPlatform, ); /// クラッシュハンドラ(Flutterフレームワーク内でスローされたすべてのエラー) FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterFatalError; /// runApp w/ Riverpod runApp(const ProviderScope(child: MyApp())); }, /// クラッシュハンドラ(Flutterフレームワーク内でキャッチされないエラー) (error, stack) => FirebaseCrashlytics.instance.recordError(error, stack, fatal: true)); } /// Providerの初期化 final counterProvider = StateNotifierProvider<Counter, int>((ref) { return Counter(); }); class Counter extends StateNotifier<int> { Counter() : super(0); /// カウントアップ void increment() => state++; } /// MaterialAppの設定 class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( title: 'Counter Firebase', theme: ThemeData( primarySwatch: Colors.blue, ), home: const MyHomePage(), debugShowCheckedModeBanner: false, ); } } /// ホーム画面 class MyHomePage extends ConsumerWidget { const MyHomePage({Key? key}) : super(key: key); @override Widget build(BuildContext context, WidgetRef ref) { return Scaffold( appBar: AppBar( title: const Text('My Homepage'), ), body: ListView( padding: const EdgeInsets.all(10), children: <Widget>[ _PagePushButton(context, 'ノーマルカウンター', const NormalCounterPage()), _PagePushButton(context, 'クラッシュページ', const CrashPage()), _PagePushButton( context, 'Remote Configカウンター', const RemoteConfigPage()), ], ), ); } } /// ページ遷移ボタン class _PagePushButton extends Container { _PagePushButton(BuildContext context, String buttonTitle, pagename) : super( padding: const EdgeInsets.all(10), child: ElevatedButton( child: Text(buttonTitle), onPressed: () { AnalyticsService().logPage(buttonTitle); Navigator.push( context, MaterialPageRoute(builder: (context) => pagename)); }, ), ); } /// Analyticsの実装 class AnalyticsService { /// ページ遷移のログ Future<void> logPage(String screenName) async { await FirebaseAnalytics.instance.logEvent( name: 'screen_view', parameters: { 'firebase_screen': screenName, }, ); } }
remote_config_page.dart
/// Flutter import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; /// Firebase関係のインポート import 'package:firebase_remote_config/firebase_remote_config.dart'; /// Other Page import 'package:counter_firebase/main.dart'; class RemoteConfigPage extends ConsumerStatefulWidget { const RemoteConfigPage({Key? key}) : super(key: key); @override RemoteConfigPageState createState() => RemoteConfigPageState(); } class RemoteConfigPageState extends ConsumerState<RemoteConfigPage> { @override void initState() { super.initState(); /// Firebase Remote Configの初期化 FirebaseRemoteConfigService().initRemoteConfig(); } @override Widget build(BuildContext context) { final counter = ref.watch(counterProvider); return Scaffold( appBar: AppBar( title: const Text('Homepage'), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ /// Remote Configのデータ取得 Text(FirebaseRemoteConfig.instance.getString("example_param")), Text( '$counter', style: Theme.of(context).textTheme.headline4, ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: () { ref.read(counterProvider.notifier).increment(); }, tooltip: 'Increment', child: const Icon(Icons.add), ), ); } } /// Firebase Remote Configの初期設定 class FirebaseRemoteConfigService { void initRemoteConfig() async { /// インスタンスの作成 final remoteConfig = FirebaseRemoteConfig.instance; /// シングルトンオブジェクトの取得 await remoteConfig.setConfigSettings(RemoteConfigSettings( fetchTimeout: const Duration(minutes: 1), minimumFetchInterval: const Duration(minutes: 5), )); /// アプリ内デフォルトパラメータ値の設定 await remoteConfig.setDefaults(const { "example_param": "Hello, world!", }); /// 値をフェッチ await remoteConfig.fetchAndActivate(); } }
GitHubのページを貼ります。