月謝袋アプリに「プレミアム案内ポップアップ」を実装しました

目次

はじめに

月謝袋アプリに、プレミアムプランの案内ポップアップを実装しました。
ユーザーに自然な形でサブスクの価値を伝えるための工夫についてお話しします。


どんな機能?

レッスン日管理タブを開いたとき、画面下からスッとボトムシートが登場します。


設計のこだわり

「しつこくない」表示ルール

条件動作
プレミアム加入済み表示しない(永久)
前回表示から90日未満表示しない
90日以上経過再表示

shared_preferences でローカルに表示日時を保存しています。

static Future<bool> shouldShow(bool isPremium) async {
  if (isPremium) return false;
  final prefs = await SharedPreferences.getInstance();
  final lastMs = prefs.getInt(_keyLastShown);
  if (lastMs == null) return true;
  final lastShown = DateTime.fromMillisecondsSinceEpoch(lastMs);
  return DateTime.now().difference(lastShown).inDays >= 90;
}

先生にだけ表示

is_admin: true のユーザーのみに表示します。
生徒アカウントには一切表示されません。

final isAdmin = data?['is_admin'] as bool? ?? false;
final isPremium = data?['is_premium'] as bool? ?? false;
if (!isAdmin || isPremium) return;

比較表で価値を明確に

「何ができるようになるか」を一目で伝えるために
無料 vs プレミアムの比較表形式を採用しました。
テキストより表の方が、差が視覚的に伝わりやすいです。


実装のポイント

showModalBottomSheet でスッと登場

await showModalBottomSheet(
  context: context,
  isScrollControlled: true,
  backgroundColor: Colors.transparent,
  builder: (_) => _PromoSheetContent(...),
);

backgroundColor: Colors.transparent にして、
コンテンツ側で BorderRadius.vertical(top: Radius.circular(20)) を指定すると
角丸のボトムシートになります。

タイミングは initState + addPostFrameCallback

タブが描画された直後に表示するため:

@override
void initState() {
  super.initState();
  WidgetsBinding.instance.addPostFrameCallback((_) {
    _checkAndShowPromo();
  });
}

addPostFrameCallback を使うことで、
ウィジェットが完全に描画された後にダイアログを表示できます。


iOS・Android両対応確認済み

プラットフォーム比較表表示購入フロー遷移閉じるボタン
iOS✅✅✅
Android✅✅✅

おわりに

サブスクの案内は「しつこすぎず、でも確実に届ける」バランスが大切です。
3ヶ月ルールと比較表の組み合わせで、ユーザー体験を損なわない設計ができました。

次はいよいよApp Storeへの審査提出です 🚀


開発環境:Flutter / Firebase / RevenueCat / shared_preferences
GitHub:sinqwell-app/monthly-fee-stamp-app

目次