🦔

Flutterでコーチマークを導入する:tutorial_coach_markの使い方

に公開

こんにちは@ヒラメです。
今回は、シャドーイングアプリ「シャドマス」のv1.4.0でコーチマークを導入しました。その実装方法や活用のポイントを紹介します。

https://shadomas.com/

❓️ コーチマークとは

コーチマーク(Coach Mark)は、アプリの画面上で特定のUI要素を強調表示し、使い方や意味を説明するためのガイドUIで次の特徴があります。

  • 画面全体を暗くして、説明したいボタンやアイコンをハイライトする(注目感が出る)
  • 吹き出しやテキストで「ここを押すと〇〇できます」と案内する
  • ステップ形式で複数の操作を順番に説明できる

使われるシーンの例:

  • 初回起動時のチュートリアル
    • 「このボタンを押すとホームに戻れます」などを案内
  • 新機能追加のお知らせ
    • 「ここに新しく検索機能が追加されました」などを強調
  • 操作が複雑な画面のガイド
    • ユーザーが迷わず操作できるようにサポート

メリット:

  • ユーザーが「自分で探す」必要がなく直感的に理解できる
  • 長いマニュアルやヘルプを読む必要が減る
  • 新しい機能を自然に利用してもらいやすい

コーチマークは アプリ内のインタラクティブな説明書 のような存在になりますね。

🐸 シャドマスでの導入ポイント

シャドマスではビギナー用の学習にコーチマークを導入しました。次の3ステップでコーチマークを表示し、迷わず学習を進められるようにしました。

  • 1️⃣ 画面遷移直後:「お手本スピーカー」と「マイク」
  • 2️⃣ 音声録音後:「あなたの音声」
  • 3️⃣ 採点後:「採点結果」と「メモ」

🛠️ 実装方法

コーチマークの導入は大まかに次の流れで実装します。

  1. どのWidgetにコーチマークを適用するか
  2. コーチマークの定義(フォーカスの形、表示位置、表示内容)
  3. コーチマークの表示

シャドマスは hooks_riverpod のHookConsumerWidget を使って画面を構築しているのでそれをベースに説明していきます。

tutorial_coach_markはこちらです。
https://pub.dev/packages/tutorial_coach_mark

1. どのWidgetにコーチマークを適用するか

BeginnerWarmUpTutorialクラスを作成し、適用するだけのGlobalKeyを定義します。この定義が被ってしまうとうまくいかないので一つ一つ作成する必要があります。シャドマスでは5つのWidgetに対してコーチマークするためその分用意しています。

class BeginnerWarmUpTutorial {
  final GlobalKey tutorial1 = GlobalKey(); // お手本スピーカー
  final GlobalKey tutorial2 = GlobalKey(); // マイク
  final GlobalKey tutorial3 = GlobalKey(); // あなたの音声
  final GlobalKey tutorial4 = GlobalKey(); // 採点結果
  final GlobalKey tutorial5 = GlobalKey(); // メモ
}

2. コーチマークの定義(フォーカスの形、表示位置、表示内容)

コーチマークを定義するためにTargetFocusTargetContentを用意します。1回の表示で連続のコーチマークする場合はその分TargetFocusを定義すればOKです。

  • TargetFocus: コーチマークで「どのウィジェットを強調表示するか」を指定するクラス

    • keyTarget: tutorial1 → 「お手本スピーカー」のWidgetをターゲットにします。
    • shape: ShapeLightFocus.Circle → フォーカスを円形に指定します。他にもShapeLightFocus.RRect(角丸長方形)があり、ターゲットのWidgetの形に合わせて選択するのが良いです。
  • TargetContent: 強調したウィジェットの周囲に「説明文や画像などの内容」を表示するクラス

    • align: ContentAlign.bottom → 「お手本スピーカー」のWidgetの下側に表示します。
    • builder: → ターゲットのWidgetを定義します。
/// 1️⃣ 画面遷移直後に「お手本スピーカー」と「マイク」にコーチマークする
void show1(BuildContext context) => _showTutorial(context, [
  TargetFocus(
    keyTarget: tutorial1,             // お手本スピーカーを対象
    shape: ShapeLightFocus.Circle,    // 円形
    contents: [
      TargetContent(
        align: ContentAlign.bottom,   // 下側
        builder: (context, controller) {
          return Text(
            'まずは、お手本の音声を再生して一緒にシャドーイングしてみよう!',
            style: TextStyle(color: Colors.white),
          );
        },
      ),
    ],
  ),
  TargetFocus(
    keyTarget: tutorial2,
    shape: ShapeLightFocus.Circle,
    contents: [
      TargetContent(
        align: ContentAlign.top,
        builder: (context, controller) {
          return Text(
            '次に、あなたの声を録音して採点してみよう!',
            style: TextStyle(color: Colors.white),
          );
        },
      ),
    ],
  ),
]);

TargetContentは、表示するWidgetを定義できるため画像などの他Widgetも一緒に定義することが可能です。

また、TargetFocuscontentsList<TargetFocus>型のため、ターゲットの周辺に対して同時にWidgetの定義も可能です。

3. コーチマークの表示

最後にGlobalKeyを割り当てた対象Widgetに対してTutorialCoachMarkを呼び出して表示します。
ここでは 「表示するタイミング」 と 「状態ごとの切り替え」 がポイントです。

  • 表示タイミングの制御
    WidgetsBinding.instance.addPostFrameCallbackを使い、画面描画が完了してからコーチマークを表示しています。
    (これをしないと、まだ存在しない Widget を参照してエラーになる場合があります)

  • 状態ごとにコーチマークを切り替え
    アプリの進行状況に応じて、表示するコーチマークを変えています。

    • state.isReady → 画面直後:「お手本スピーカー」「マイク」
    • state.isPending → 録音後:「あなたの音声」
    • state.isEvaluated → 採点後:「採点結果」「メモ」
  • 2回目以降はコーチマーク表示しない
    毎回コーチマークが表示されるのもよくないため、shared_preferencesを使って2回目以降は表示しないように制御するのが望ましいです。

class _WarmUpOngoingView extends HookConsumerWidget {
  
  Widget build(BuildContext context, WidgetRef ref) {
    final tutorial = useMemoized(() => BeginnerWarmUpTutorial(colorShadow: Colors.green));

    // コーチマークの表示
    useEffect(() {
      WidgetsBinding.instance.addPostFrameCallback((_) {
        if (state.isReady) {
          tutorial.show1(context);
        } else if (state.isPending) {
          tutorial.show2(context);
        } else if (state.isEvaluated) {
          tutorial.show3(context);
        }
      });
      return null;
    }, [state]);

実際に表示を行う処理は _showTutorialにまとめます。targets(表示対象のリスト)を渡してshowを呼ぶだけのシンプルな関数です。

void _showTutorial(BuildContext context, List<TargetFocus> targets) {
  Future.delayed(const Duration(milliseconds: 300)).then((_) {
    if (!context.mounted) return;

    TutorialCoachMark(
      targets: targets,
      hideSkip: true,           // スキップボタン非表示
      colorShadow: colorShadow, // 背景シャドウの色
    ).show(context: context);
  });
}

📌 まとめ

  • コーチマークはUI要素を強調して操作を案内できる便利な仕組み
  • シャドマスでは「ビギナーが迷わず学習を進められる」ことを目的に導入
  • tutorial_coach_markパッケージを使えば簡単に実装可能

新機能の紹介や初回チュートリアルに活用すると、ユーザー体験を大きく改善できますので試してみてください!

🚀 アプリのダウンロードはこちら

🍎 App Store(iOS):
https://itunes.apple.com/jp/app/id6745143950?mt=8

🤖 Google Play(Android):
https://play.google.com/store/apps/details?id=com.eleven.shadomas

いくつかのシャドーイングは無料で試せるので、ぜひダウンロードして使ってみてください!

@ヒラメ

Discussion