テストするアプリを揃える
このページはFlutter入門のカウンターを検証します。ほかの発展ページの画面を使っていた場合も、lib/main.dartを以下へ戻してください。プロジェクト名は入門で作ったstudy_appです。まず外部パッケージなしでテストします。
基礎練習で作ったbasics.dartは、エディターやファイル管理画面でstudy_appの外へ移して保管します。コンソール練習のprintがFlutterの解析ルールで指摘されるのを避け、ここではアプリとテストを検査します。
import 'package:flutter/material.dart';
void main() => runApp(const StudyApp());
class StudyApp extends StatelessWidget {
const StudyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(home: CounterPage());
}
}
class CounterPage extends StatefulWidget {
const CounterPage({super.key});
@override
State<CounterPage> createState() => _CounterPageState();
}
class _CounterPageState extends State<CounterPage> {
int _count = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('学習カウンター')),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('学習回数: $_count'),
ElevatedButton(
onPressed: () {
setState(() { _count += 1; });
},
child: const Text('1回追加'),
),
],
),
),
);
}
}画面を操作するテスト
生成済みのtest/widget_test.dartを以下へ全体ごと置き換えます。pumpWidgetでWidgetを表示し、find.textで文字を探し、tapで操作します。pumpは状態変更後のフレームを進めます。
import 'package:flutter_test/flutter_test.dart';
import 'package:study_app/main.dart';
void main() {
testWidgets('追加ボタンを2回押すと学習回数が2になる', (tester) async {
await tester.pumpWidget(const StudyApp());
expect(find.text('学習回数: 0'), findsOneWidget);
await tester.tap(find.text('1回追加'));
await tester.pump();
expect(find.text('学習回数: 1'), findsOneWidget);
await tester.tap(find.text('1回追加'));
await tester.pump();
expect(find.text('学習回数: 2'), findsOneWidget);
expect(find.text('学習回数: 0'), findsNothing);
});
}flutter analyze
flutter test静的解析でエラーがない
1つのWidgetテストが成功するpubspec.yamlのnameを変更した場合、importのstudy_appもその名前に揃えます。試しに期待値2を3に変えるとテストが失敗します。元へ戻して再度成功することを確認しましょう。
設定保存のパッケージを追加
ここからは別の練習です。shared_preferencesは小さなキーと値の保存に使えます。プロジェクト直下で次を実行すると、依存関係がpubspec.yamlに追加され、取得されます。ネットワーク接続が必要です。
flutter pub add shared_preferences次の例はSharedPreferencesAsyncを使います。このAPIはshared_preferences 2.3.0以降で提供されています。SDKの制約で古い版が選ばれた場合は、対応SDKを確認して更新します。pubspec.lockには解決された版が記録されます。
設定を保存して読み戻す
この節でlib/main.dartを設定画面へ置き換えた後は、flutter test ではなく flutter analyze だけを実行してください。前半のtest/widget_test.dartはカウンター画面を前提としているため、設定画面のまま実行するとテストは失敗します。前半のテストを再実行する場合は、lib/main.dartとtest/widget_test.dartの両方を前半のコードに戻します。
lib/main.dartを以下へ全体ごと置き換えます。initStateで初回の読み込みを始めます。保存が成功した後だけ画面のスイッチを確定し、失敗時は案内を表示します。await後にはmountedを確認します。
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() => runApp(const StudyApp());
class StudyApp extends StatelessWidget {
const StudyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(home: SettingsPage());
}
}
class SettingsPage extends StatefulWidget {
const SettingsPage({super.key});
@override
State<SettingsPage> createState() => _SettingsPageState();
}
class _SettingsPageState extends State<SettingsPage> {
final _prefs = SharedPreferencesAsync();
bool _enabled = false;
bool _busy = true;
String _message = '読み込み中';
@override
void initState() {
super.initState();
_load();
}
Future<void> _load() async {
try {
final value = await _prefs.getBool('study_enabled') ?? false;
if (!mounted) return;
setState(() { _enabled = value; _message = '読み込み完了'; });
} catch (error) {
if (!mounted) return;
setState(() { _message = '設定を読み込めませんでした'; });
} finally {
if (mounted) setState(() { _busy = false; });
}
}
Future<void> _save(bool value) async {
setState(() { _busy = true; _message = '保存中'; });
try {
await _prefs.setBool('study_enabled', value);
if (!mounted) return;
setState(() { _enabled = value; _message = '保存しました'; });
} catch (error) {
if (!mounted) return;
setState(() { _message = '保存できませんでした'; });
} finally {
if (mounted) setState(() { _busy = false; });
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('学習設定')),
body: Column(
children: [
SwitchListTile(
title: const Text('学習モード'),
value: _enabled,
onChanged: _busy ? null : (value) { _save(value); },
),
Text(_message),
],
),
);
}
}初回は学習モードがオフ(未保存の場合)
オンにすると「保存しました」
同じブラウザー・同じオリジンで再読み込みしてもオンのままWebの保存を比較するため、実行を一度終了し、次のようにポートを固定して起動します。まず同じ実行セッション内でブラウザーを再読み込みし、保存を確認してください。
flutter run -d chrome --web-port=7357flutter runで起動するChromeは一時プロファイルを使うことがあります。コマンド終了後の別セッションでは保存領域も変わり得るため、ブラウザーの再読み込みと、新しいプロファイルでの起動を区別します。カウンター用のWidgetテストは画面が変わったので、この設定画面には適用できません。カウンターのコードへ戻してテストするか、設定画面用に保存先を差し替えられる構成とテストを追加します。
練習:保存と解除を確認
学習モードをオン・オフにし、それぞれ再読み込み後も値が保たれるかを確認してください。初期値だけで成功と判断しないため、両方の値を試します。
確認手順と期待結果
オンに変更 → 保存しました → 再読み込み → オン
オフに変更 → 保存しました → 再読み込み → オフ最後に flutter analyze を実行します。アプリとして共有するときはlib、test、pubspec.yaml、pubspec.lockなどを管理し、buildなどの生成物を直接編集しません。テストページ前半へ戻る際は、main.dartとwidget_test.dartを対応する組に揃えます。
公式ドキュメントで確認する:Widgetテスト / shared_preferences / 設定の保存
