くらげになりたい。

くらげのようにふわふわ生きたい日曜プログラマなブログ。趣味の備忘録です。

Android-RateをFlutter/Dartで実装する

昔使ってて好きだったレビュー依頼用のライブラリのAndroid-Rate。
Flutterでも使いたいなと思い、リライトしてみたときの備忘録(*´ω`*)

Android-Rateとは

レビューを依頼するダイアログを表示する便利ライブラリ。

  • インストールしてからの日数
  • アプリを起動した回数

などを指定して、ダイアログを表示できたりする。

ダイアログも用意されているけど、今回は自前のダイアログがいいので、
起動回数とかをダイアログの表示条件のチェックをする部分のみ書き直してみた。

使い方

起動時はこんな感じでmonitor()を1度だけ呼び出す。

import 'package:riverpod_annotation/riverpod_annotation.dart';

part "main.g.dart";

@Riverpod(keepAlive: true)
AppRate appRate(AppRateRef ref) => throw UnimplementedError();


FutureOr<void> main() async {
  WidgetsBinding widgetsBinding = WidgetsFlutterBinding.ensureInitialized();
    
  // share preferenceの初期化
  final sharedPreferences = await SharedPreferences.getInstance();
  
  // 起動時にインストール日時や起動回数を保存
  final appRate = AppRate(pref: sharedPreferences);
  appRate.monitor();
  
  // runApp
  runApp(ProviderScope(
    overrides: [
      // 初期化したinstanceで上書き
      appRateProvider.overrideWithValue(appRate),
    ],
    child: const App(),
  ));
}

あとは好きなタイミングで、showRateDialogIfMeetsConditionsを呼び出して、
ダイアログを表示してよいかチェックすればOK

class TopPage extends HookConsumerWidget {
  const MenuPage({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {

    onClick(() {
      if (!ref.read(appRateProvider).showRateDialogIfMeetsConditions()) return;
      showDialog(
        context: context,
        useRootNavigator: false,
        builder: (_) => const ReviewDialog(),
      );
    });
    
    return // ...
  }
}

ダイアログ側でもクリックに応じで各メソッドを呼び出しておく

// レビューするを選択時
ref.read(appRateProvider).selectOk();

// 閉じる or あとでを選択時
ref.read(appRateProvider).selectLater();

// 結構ですを選択時
ref.read(appRateProvider).selectNever();

AppRateの中身

インストール日時などをShared Preferencesに保存しておいて、
指定した日数/回数などでチェックする形

import 'package:flutter/foundation.dart';
import 'package:shared_preferences/shared_preferences.dart';

const String prefKeyInstallDate = "android_rate_install_date";
const String prefKeyLaunchTimes = "android_rate_launch_times";
const String prefKeyIsAgreeShowDialog = "android_rate_is_agree_show_dialog";
const String prefKeyRemindInterval = "android_rate_remind_interval";

class AppRate {
  const AppRate({
    required this.pref,
    this.installDate = 10, // days
    this.launchTimes = 10, // times
    this.remindInterval = 1, // days
    this.isDebug = false,
  });
  final SharedPreferences pref;
  final int installDate;
  final int launchTimes;
  final int remindInterval;
  final bool isDebug;

  // アプリ起動時に呼び出す。起動回数の更新
  Future<void> monitor() async {
    // 初回起動の場合は、インストール日時を保存
    if (_isFirstLaunch()) await _setInstallDate();

    // 起動回数をインクリメント
    await _setLaunchTimes(_getLaunchTimes() + 1);

    debugLog();
  }

  // 表示可能かのチェック。isDebug=trueの場合は常に表示
  bool showRateDialogIfMeetsConditions() {
    bool isMeetsConditions = isDebug || shouldShowRateDialog();
    return isMeetsConditions;
  }

  // レビューするを選択
  Future<void> selectOk() async {
    await _setAgreeShowDialog(false);
  }

  // あとでを選択
  Future<void> selectLater() async {
    await _setRemindInterval();
  }

  // しないを選択
  Future<void> selectNever() async {
    await _setAgreeShowDialog(false);
  }

  // 表示するかの判定
  bool shouldShowRateDialog() =>
      _getIsAgreeShowDialog() &&
      isOverLaunchTimes() &&
      isOverInstallDate() &&
      isOverRemindDate();
  bool isOverLaunchTimes() => _getLaunchTimes() >= launchTimes;
  bool isOverInstallDate() => _isOverDate(_getInstallDate(), installDate);
  bool isOverRemindDate() => _isOverDate(_getRemindInterval(), remindInterval);

  // ********************************************************
  // * private
  // ********************************************************
  // 初回起動のフラグ
  bool _isFirstLaunch() {
    return pref.getInt(prefKeyInstallDate) == null;
  }

  // 保存: インストール日時
  Future<void> _setInstallDate() async {
    await pref.setInt(prefKeyInstallDate, _getTime());
  }

  // 取得: インストール日時
  int _getInstallDate() => pref.getInt(prefKeyInstallDate) ?? 0;

  // 取得: 起動回数
  int _getLaunchTimes() => pref.getInt(prefKeyLaunchTimes) ?? 0;

  // 保存: 起動回数
  Future<void> _setLaunchTimes(int times) async {
    await pref.setInt(prefKeyLaunchTimes, times);
  }

  // 取得: あとでのインターバル時間
  int _getRemindInterval() => pref.getInt(prefKeyRemindInterval) ?? 0;

  // 保存: あとでのインターバル時間
  Future<void> _setRemindInterval() async {
    await pref.remove(prefKeyRemindInterval);
    await pref.setInt(prefKeyRemindInterval, _getTime());
  }

  // 取得: 表示を許可するかのフラグ
  bool _getIsAgreeShowDialog() =>
      pref.getBool(prefKeyIsAgreeShowDialog) ?? true;

  // 保存: 表示を許可するかのフラグ
  Future<void> _setAgreeShowDialog(bool isAgree) async {
    await pref.setBool(prefKeyIsAgreeShowDialog, isAgree);
  }

  int _getTime() => DateTime.now().millisecondsSinceEpoch;

  bool _isOverDate(int targetDate, int threshold) {
    return _getTime() - targetDate >= threshold * 24 * 60 * 60 * 1000;
  }
}

これに追加・拡張して、なにかをおこなった回数なども加えてもOK

in_app_reviewパッケージと組み合わせて使うとよりレビューしてもらいやすいかも


以上!! これでだいぶ楽になったぞ...(*´ω`*)