🎯
Flutterでインタラクティブなチュートリアルを実装!tutorial_coach_markの使い方
はじめに
記事の概要
アプリに適切なチュートリアルを導入すれば、ユーザーはアプリの操作をスムーズに理解できます。
アプリのチュートリアルを画像やGIFで説明することもできますが、ユーザーの操作に応じてステップごとに説明を加えたい場合は、インタラクティブなチュートリアルが効果的です。
本記事では、tutorial_coach_markを使って、Flutterでインタラクティブなチュートリアルを導入する方法を解説します。
環境
$ flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.29.1, on macOS 15.3.2 24D81 darwin-arm64 (Rosetta), locale ja-JP)
[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
[✓] Xcode - develop for iOS and macOS (Xcode 16.2)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2024.2)
[✓] VS Code (version 1.98.1)
[✓] Connected device (6 available)
[✓] Network resources
• No issues found!
インタラクティブなチュートリアルの実装
完成形
Flutterでデフォルトで用意されているカウンターアプリを例にインタラクティブなチュートリアルの導入方法を解説します。
チュートリアルは最終的に以下のようになります。
+ボタンにフォーカスさせる
まずはアプリを初めて開いた後で+ボタンを押すようユーザーを促すステップを追加するために、下記のコードを修正します。
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('You have pushed the button this many times:'),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
インタラクティブなチュートリアルを実装するためのパッケージを追加します。
flutter pub add tutorial_coach_mark
チュートリアルを実装する流れは以下の通りです。
- チュートリアルの設定を作成する。
- チュートリアルを表示する。
アプリの初回起動時にチュートリアルを実施したいので、initStateメソッドにチュートリアルの設定と表示の処理を書きます。
チュートリアルの設定の作成をメソッド_createTutorial、チュートリアルの表示をメソッド_showTutorialに任せます。
import 'package:tutorial_coach_mark/tutorial_coach_mark.dart';
class _MyHomePageState extends State<MyHomePage> {
// ...
void initState() {
super.initState();
final tutorial = _createTutorial(); // チュートリアルの設定を作成
_showTutorial(tutorial); // チュートリアルを表示
}
TutorialCoachMark _createTutorial() {
return TutorialCoachMark(targets: _createTargets());
}
void _showTutorial(TutorialCoachMark tutorial) {
tutorial.show(context: context);
}
// ...
}
次は、+ボタンをタップするよう促すチュートリアルを下記のように定義します。
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
final plusButtonKey = GlobalKey(); // +ボタンをキーを定義
// ...
TutorialCoachMark _createTutorial() {
return TutorialCoachMark(
targets: _createTargets(),
hideSkip: true, // スキップできないようにする
);
}
List<TargetFocus> _createTargets() {
List<TargetFocus> targets = [];
targets.add(
TargetFocus(
keyTarget: plusButtonKey, // +ボタンが説明対象であることを明示する
color: Colors.purple,
contents: [
TargetContent(
align: ContentAlign.top,
builder: (context, controller) {
return Text(
'ボタンをタップすると、カウンターが増えます。',
style: TextStyle(fontSize: 20, color: Colors.white),
);
},
),
],
),
);
return targets;
}
// ...
Widget build(BuildContext context) {
return Scaffold(
// ...
floatingActionButton: FloatingActionButton(
key: plusButtonKey, // +ボタンにkeyを設定
// ...
),
);
}
}
チュートリアルの表示は出来ていますが、タップしてもカウンターの値が増加していません。
カウンターの値が増加するように、問題を以下のように修正します。
TutorialCoachMark _createTutorial() {
return TutorialCoachMark(
targets: _createTargets(),
hideSkip: true,
onClickTarget: (target) {
_incrementCounter(); // +ボタンをタップしたときにカウンターを増加させる
},
);
}
最終的なコードは下記のようになります。
import 'package:flutter/material.dart';
import 'package:tutorial_coach_mark/tutorial_coach_mark.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
final plusButtonKey = GlobalKey();
void initState() {
super.initState();
final tutorial = _createTutorial();
_showTutorial(tutorial);
}
TutorialCoachMark _createTutorial() {
return TutorialCoachMark(
targets: _createTargets(),
hideSkip: true,
onClickTarget: (target) {
_incrementCounter();
},
);
}
List<TargetFocus> _createTargets() {
List<TargetFocus> targets = [];
targets.add(
TargetFocus(
keyTarget: plusButtonKey,
color: Colors.purple,
contents: [
TargetContent(
align: ContentAlign.top,
builder: (context, controller) {
return Text(
'ボタンをタップすると、カウンターが増えます。',
style: TextStyle(fontSize: 20, color: Colors.white),
);
},
),
],
),
);
return targets;
}
void _showTutorial(TutorialCoachMark tutorial) {
tutorial.show(context: context);
}
void _incrementCounter() {
setState(() {
_counter++;
});
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('You have pushed the button this many times:'),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
key: plusButtonKey,
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
おわりに
最後まで記事を読んでくださり、ありがとうございます!
本記事の内容を応用して、より良いアプリを作ることの手助けになれば幸いです😌
Discussion