🌐

Riverpod v-2 Remote Config

2024/07/27に公開

良いコードかわからないが試してみた

前回使ってみたソースコードを修正して使います

Riverpodを使うとソースコードが減りましたね。Viewとロジックを分けることができた。

hooks_riverpodを追加する:

flutter pub add \
hooks_riverpod \
riverpod_annotation \
dev:riverpod_generator \
dev:build_runner \
dev:custom_lint \
dev:riverpod_lint

プロバイダーを作成する

AsyncData, AsyncErrorの使い方なのですが、whenのときは、(_)でしたが、switchのときだと、同じように書けなかったので、空白にしてます。いいのかこれ???

import 'dart:async';

import 'package:firebase_remote_config/firebase_remote_config.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'remote_config_app.g.dart';

class RemoteConfigService {
  final FirebaseRemoteConfig _remoteConfig;

  RemoteConfigService(this._remoteConfig);

  Future<void> initialize() async {
    await _remoteConfig.setDefaults({
      "name": "Shah",
      "age": "28",
      "hobby": "Coding",
    });

    await _remoteConfig.setConfigSettings(RemoteConfigSettings(
      fetchTimeout: const Duration(seconds: 10),
      minimumFetchInterval: const Duration(seconds: 10),
    ));

    await _remoteConfig.fetchAndActivate();
  }

  String getString(String key) => _remoteConfig.getString(key);

  Stream<RemoteConfigUpdate> get onConfigUpdated => _remoteConfig.onConfigUpdated;
}

// Providers
(keepAlive: true)
RemoteConfigService remoteConfigService(RemoteConfigServiceRef ref) {
  return RemoteConfigService(FirebaseRemoteConfig.instance);
}


(keepAlive: true)
Future<void> remoteConfigInitialization(RemoteConfigInitializationRef ref) async {
  final remoteConfig = ref.watch(remoteConfigServiceProvider);
  await remoteConfig.initialize();
}


(keepAlive: true)
Stream<RemoteConfigUpdate> remoteConfigUpdate(RemoteConfigUpdateRef ref) {
  final remoteConfig = ref.watch(remoteConfigServiceProvider);
  return remoteConfig.onConfigUpdated;
}

// ------------------------------------------------------ 

// Widget
class RemoteConfigApp extends ConsumerWidget {
  const RemoteConfigApp({super.key});

  
  Widget build(BuildContext context, WidgetRef ref) {
    final remoteConfigInitialization = ref.watch(remoteConfigInitializationProvider);
    ref.listen(remoteConfigUpdateProvider, (_, __) async {
      await FirebaseRemoteConfig.instance.activate();
    });

    final remoteConfig = ref.watch(remoteConfigServiceProvider);

    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.indigo,
        title: const Text('Remote Config Riverpod v-2'),
      ),
      body: switch(remoteConfigInitialization) {
        // valueがないので、()の中には何もない
        AsyncData() => Padding(
          padding: const EdgeInsets.symmetric(horizontal: 23.0),
          child: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                const Text('Riverpod v-2 Remote Config App'),
                Text('Name: ${remoteConfig.getString('name')}'),
                Text('Age: ${remoteConfig.getString('age')}'),
                Text('Hobby: ${remoteConfig.getString('hobby')}'),
              ],
            ),
          ),
        ),
        AsyncError() => const Center(child: Text('Error')),
        _ => const Center(child: CircularProgressIndicator()),
      },
    );
  }
}

main.dartを修正してビルドしてみましょう。ProviderScopeで、MyApp()をwrapしましょう。

maind.dart
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:remote_config_app/firebase_options.dart';
import 'package:remote_config_app/remote_config_app.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );
  runApp(const ProviderScope(child: MyApp()));
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const RemoteConfigApp(),
    );
  }
}

実行すると、FirebaseのRemote Configから、値を取得できているようだ。

最後に

どこにも情報がないので試行錯誤してみた💦
なんでも Riverpod使えば良いと思わないが、StatefulWidgetを嫌う人たちがいるので、合わせるとなると、riverpod, flutter_hooksを使わざるをえない💦

RemoteConfigのメリットをまだ理解できませんが、最近覚えないといけなくなったので、キャッチアップ目的で試してます。

おまけ

古いRiverpodだとこんな感じですね。

import 'package:flutter/material.dart';
import 'package:firebase_remote_config/firebase_remote_config.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

// RemoteConfigサービス
class RemoteConfigService {
  final FirebaseRemoteConfig _remoteConfig;

  RemoteConfigService(this._remoteConfig);

  Future<void> initialize() async {
    await _remoteConfig.setDefaults({
      "name": "Shah",
      "age": "28",
      "hobby": "Coding",
    });

    await _remoteConfig.setConfigSettings(RemoteConfigSettings(
      fetchTimeout: const Duration(seconds: 10),
      minimumFetchInterval: const Duration(seconds: 10),
    ));

    await _remoteConfig.fetchAndActivate();
  }

  String getString(String key) => _remoteConfig.getString(key);

  Stream<RemoteConfigUpdate> get onConfigUpdated => _remoteConfig.onConfigUpdated;
}

// Providers
final remoteConfigProvider = Provider((ref) => RemoteConfigService(FirebaseRemoteConfig.instance));

final remoteConfigInitializationProvider = FutureProvider((ref) async {
  final remoteConfig = ref.watch(remoteConfigProvider);
  await remoteConfig.initialize();
});

final remoteConfigUpdateProvider = StreamProvider((ref) {
  final remoteConfig = ref.watch(remoteConfigProvider);
  return remoteConfig.onConfigUpdated;
});

// Widget
class MyHomePage extends ConsumerWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  
  Widget build(BuildContext context, WidgetRef ref) {
    final remoteConfigInitialization = ref.watch(remoteConfigInitializationProvider);
    ref.listen(remoteConfigUpdateProvider, (_, __) async {
      await FirebaseRemoteConfig.instance.activate();
    });

    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(title),
      ),
      body: remoteConfigInitialization.when(
        data: (_) => _buildBody(ref),
        loading: () => const Center(child: CircularProgressIndicator()),
        error: (error, stack) => Center(child: Text('Error: $error')),
      ),
    );
  }

  Widget _buildBody(WidgetRef ref) {
    final remoteConfig = ref.watch(remoteConfigProvider);
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 23.0),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          const Text('Welcome to the Remote Config App'),
          Text('Name: ${remoteConfig.getString('name')}'),
          Text('Age: ${remoteConfig.getString('age')}'),
          Text('Hobby: ${remoteConfig.getString('hobby')}'),
        ],
      ),
    );
  }
}

Discussion