Skip to content

Instantly share code, notes, and snippets.

@DaisukeNagata
Created July 17, 2023 14:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DaisukeNagata/3a420821eafac0e96ba17ba854a448e0 to your computer and use it in GitHub Desktop.
Save DaisukeNagata/3a420821eafac0e96ba17ba854a448e0 to your computer and use it in GitHub Desktop.
Example Riverpod Only
import 'package:flutter/material.dart';
/*
ここではflutterとflutter_riverpodをインポートしています。
flutter_riverpod: ^2.3.6を使用してます。
*/
import 'package:flutter_riverpod/flutter_riverpod.dart';
/*
CounterStateクラスはカウンターの状態を保持します。ここでは整数のcounterValueを保持しています。
*/
class CounterState {
int counterValue;
CounterState({required this.counterValue});
}
/*
アプリケーションのエントリーポイントです。
ここではProviderScopeというRiverpodのウィジェットを使ってCounterAppをラップしています。
ProviderScopeはRiverpodのすべてのプロバイダーがアプリケーション内で利用できるようにするためのものです。
*/
void main() => runApp(const ProviderScope(child: CounterApp()));
/*
CounterAppクラスはアプリケーション全体を表すウィジェットです。
buildメソッドでMaterialAppを返し、そのホームページにCounterPageを設定しています。
*/
class CounterApp extends StatelessWidget {
const CounterApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: CounterPage(),
);
}
}
/*
Riverpodのプロバイダーを作成します。
この場合、StateNotifierProviderを使用しています。
このプロバイダーはCounterStateNotifierというStateNotifierを作成し、CounterStateという状態を提供します。
*/
final counterStateNotifierProvider =
StateNotifierProvider<CounterStateNotifier, CounterState>(
(ref) => CounterStateNotifier());
/*
CounterStateNotifierクラスはStateNotifierを拡張します。
ここでの状態はCounterStateとします。初期状態はカウンター値が0のCounterStateです。
incrementメソッドを提供して、カウンター値を増やすことができます。
*/
class CounterStateNotifier extends StateNotifier<CounterState> {
CounterStateNotifier() : super(CounterState(counterValue: 0));
void increment() {
state = CounterState(counterValue: state.counterValue + 1);
}
}
/*
CounterPageクラスはカウンターページを表示するウィジェットです。
ここでは、カウンターの状態を監視(watch)しています。
カウンターの状態が変わると、ウィジェットは自動的に再ビルドされます。
また、FloatingActionButtonが押されるとincrement関数が呼び出され、カウンターの値が増加します。
以上がコードの全体の説明です。このアプリケーションは、Riverpodを使用してカウンターの状態を管理し、それをUIに反映させる基本的な例です。
*/
class CounterPage extends ConsumerWidget {
const CounterPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
/*
watchは、指定したプロバイダーの現在の状態を読み取りますが、それだけではありません。
重要なのは、そのプロバイダーの状態が変わるたびに、watchを呼び出したウィジェットを再ビルドするように設定します。
つまり、この行 final counterState = ref.watch(counterStateNotifierProvider);は、
counterStateNotifierProviderの状態が変わるたびにウィジェットを再ビルドし、最新の状態をcounterStateに代入します。
*/
final counterState = ref.watch(counterStateNotifierProvider);
/*
一方、readは指定したプロバイダーの現在の状態を読み取りますが、そのプロバイダーの状態が変わった場合でもウィジェットを再ビルドしません。
この行 final increment = ref.read(counterStateNotifierProvider.notifier).increment;では、counterStateNotifierProviderのincrementメソッドを参照します。
このincrementメソッドは状態の変化を引き起こしますが、それ自体の変化によってウィジェットが再ビルドされるわけではありません。
*/
final increment = ref.read(counterStateNotifierProvider.notifier).increment;
return Scaffold(
appBar: AppBar(title: const Text('Counter')),
body: Center(
child: Text('You hit me: ${counterState.counterValue} times'),
),
floatingActionButton: FloatingActionButton(
onPressed: increment,
child: const Icon(Icons.add),
),
);
}
}
/*
Riverpodは依存関係の注入(DI)のフレームワークですので、アプリケーションの要件に応じてプロバイダーを追加することがあります。
以下にプロバイダーを追加するいくつかの一般的なシナリオを示します:
1.異なる部分で同じ状態を共有する必要がある場合:Riverpodのプロバイダーはアプリケーションのさまざまな部分間で状態を共有するのに便利です。
複数のウィジェットで同じデータが必要な場合や、アプリケーションの異なるレイヤー間でデータを共有する必要がある場合には、プロバイダーを使用します。
2.サービスやリポジトリのようなオブジェクトのインスタンスを管理するため:RiverpodはDIフレームワークとしても機能します。
アプリケーション全体でアクセスする必要があるサービスやリポジトリのようなオブジェクトのインスタンスを作成し、管理するためにプロバイダーを使用することがあります。
3.状態のロジックを隔離するため:プロバイダーは状態管理のロジックを隔離するために使用することもあります。
これは、状態の変更ロジックをUIから分離し、テストしやすくするために有用です。
ただし、これらは一般的なガイドラインであり、プロバイダーを追加する具体的な決定は、
プロジェクトの要件、コードの維持性、そしてプロジェクトの規模などによるところが大きいです。
プロバイダーを追加することでアプリケーションの複雑さが増す場合は、その利益とコストを慎重に比較することが重要です。
*/
@DaisukeNagata
Copy link
Author

このコードは、Flutterの単体テストを行うためのものです。ここでは、CounterAppというRiverpodを使用したFlutterアプリケーションのカウンター機能が正しく動作するかテストしています。

以下、各部分の説明です:

ProviderScope内でCounterAppをビルドする:この操作により、RiverpodのProviderがテスト内で正しく機能するようになります。

find.text('You hit me: 0 times')により、テキストが'You hit me: 0 times'のウィジェットを探し、
そのウィジェットが画面上に一つだけ存在することを確認しています。

find.text('You hit me: 1 times')により、テキストが'You hit me: 1 times'のウィジェットが存在しないことを確認しています。
tester.tap(find.byIcon(Icons.add))とtester.pump()で、'+'アイコンをタップし、フレームを更新(つまり画面を再描画)しています。

テキストが'You hit me: 0 times'のウィジェットがなく、テキストが'You hit me: 1 times'のウィジェットが一つ存在することを確認しています。
これにより、'+'アイコンをタップすることでカウンターが正しく増加していることが確認できます。
以上のように、このコードはFlutterアプリケーションの挙動をテストするためのもので、
特定の操作後のウィジェットの状態をチェックすることでアプリケーションが期待通りに機能しているかを確認します。
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_application/main.dart'; <- main.dartのある階層
import 'package:flutter_riverpod/flutter_riverpod.dart';

void main() {
  testWidgets('カウンターの増分に関するテスト', (WidgetTester tester) async {
    // テスターにウィジェットをビルドするよう指示することで、ウィジェットを作成します。
    await tester.pumpWidget(
      const ProviderScope(
        child: CounterApp(),
      ),
    );

    // カウンターが0から始まることを確認します。
    expect(find.text('You hit me: 0 times'), findsOneWidget);
    expect(find.text('You hit me: 1 times'), findsNothing);

    // '+' アイコンをタップし、フレームをトリガーします。
    await tester.tap(find.byIcon(Icons.add));
    await tester.pump();

    // カウンターが増加したことを確認します。
    expect(find.text('You hit me: 0 times'), findsNothing);
    expect(find.text('You hit me: 1 times'), findsOneWidget);
  });
}
コマンド操作です。
flutter test test/<your_test_file.dart>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment