From 91ffce286938b209b6a24de114dd93f0e04be168 Mon Sep 17 00:00:00 2001 From: Siahlooei Date: Sun, 14 Jul 2024 18:57:07 +0330 Subject: [PATCH] Until step 2.6: :) Read steps for more details. --- Steps | 68 ++++++++++++ analysis_options.yaml | 4 + lib/generated/assets.dart | 5 + lib/main.dart | 125 ---------------------- lib/mains/gt.dart | 49 +++++++++ lib/outerapi/auth/call/getRandomData.dart | 24 +++++ lib/outerapi/auth/model/my_random.dart | 22 ++++ lib/outerapi/auth/model/time.dart | 17 +++ lib/widgets/simples/random.dart | 32 ++++++ pubspec.lock | 112 ++++++++++++------- pubspec.yaml | 12 ++- test/widget_test.dart | 30 ------ 12 files changed, 307 insertions(+), 193 deletions(-) create mode 100644 lib/generated/assets.dart delete mode 100644 lib/main.dart create mode 100644 lib/mains/gt.dart create mode 100644 lib/outerapi/auth/call/getRandomData.dart create mode 100644 lib/outerapi/auth/model/my_random.dart create mode 100644 lib/outerapi/auth/model/time.dart create mode 100644 lib/widgets/simples/random.dart delete mode 100644 test/widget_test.dart diff --git a/Steps b/Steps index 50f7b7d..68bec96 100644 --- a/Steps +++ b/Steps @@ -15,4 +15,72 @@ << 1.3. Add Links.txt file. 1.4. Checkout in other station ==> Must re run 1.1. steps again. (I don't Know why) + 1.5. After a while, i read the document. Now updated some dependencies and starting + use riverpod again. + Now I know many things about it, but riverpod learning curve is not good. + Flutter was better but not good at result. I will stick on learning curve before + developing the app. + +2. https://riverpod.dev/docs/essentials/first_request + 2.1. Install freezed using this commands: + (using https://github.com/rrousselGit/freezed?tab=readme-ov-file#install) + >> + flutter pub add freezed_annotation + flutter pub add dev:build_runner + flutter pub add dev:freezed + flutter pub add json_annotation + flutter pub add dev:json_serializable + << + install flutterX & FlutterAssetsGenerator & freezed plugins. + 2.2. Add model for utc and random endpoints. + Do these steps for any JSON, request or response. + Use snippet freezedClass, then use @freezed menu, then "Json From Freezed" + then paste,then click the button --> then change class name, file name, path then save. + Now paste in this + import 'package:flutter/foundation.dart'; + to generated file. + Now use @freezed menu, then "Run Build Runner". + 2.3. Run command + >> + flutter pub add http + << + 2.4. New file then paste this code. For random API. (GET http://localhost:3000/p/random) + >> + import 'dart:convert'; + import 'package:http/http.dart' as http; + import 'package:riverpod_annotation/riverpod_annotation.dart'; + import '../model/my_random.dart'; + + // Necessary for code-generation to work + part 'provider.g.dart'; + + /// This will create a provider named `activityProvider` + /// which will cache the result of this function. + @riverpod + Future getMyRandom(GetMyRandomRef ref) async { + // Using package:http, we fetch a random activity from the Bored API. + final response = await http.get(Uri.https('localhost:3000', '/p/random')); + // Using dart:convert, we then decode the JSON payload into a Map data structure. + final json = jsonDecode(response.body) as Map; + // Finally, we convert the Map into an Activity instance. + return MyRandom.fromJson(json); + } + << + Check the pattern should be with this format: + @riverpod + Result myFunction(MyFunctionRef ref) { + + } + myFunction and MyFunctionRef + xabcd and XabcdRef + + 2.5. Create a widget with data. + it is simple using these line of code + final AsyncValue myRandom = ref.watch(getMyRandomProvider); + that serves data as a aysncvalue. + there is a sample in widgets/simples folder. + + Note: Continue from "Going further" in "https://riverpod.dev/docs/essentials/first_request" + + diff --git a/analysis_options.yaml b/analysis_options.yaml index 0d29021..5ddbf8e 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -9,6 +9,10 @@ # packages, and plugins designed to encourage good coding practices. include: package:flutter_lints/flutter.yaml +analyzer: + errors: + invalid_annotation_target: ignore + linter: # The lint rules applied to this project can be customized in the # section below to disable rules from the `package:flutter_lints/flutter.yaml` diff --git a/lib/generated/assets.dart b/lib/generated/assets.dart new file mode 100644 index 0000000..5c591ce --- /dev/null +++ b/lib/generated/assets.dart @@ -0,0 +1,5 @@ +///This file is automatically generated. DO NOT EDIT, all your changes would be lost. +class Assets { + Assets._(); + +} diff --git a/lib/main.dart b/lib/main.dart deleted file mode 100644 index 8e94089..0000000 --- a/lib/main.dart +++ /dev/null @@ -1,125 +0,0 @@ -import 'package:flutter/material.dart'; - -void main() { - runApp(const MyApp()); -} - -class MyApp extends StatelessWidget { - const MyApp({super.key}); - - // This widget is the root of your application. - @override - Widget build(BuildContext context) { - return MaterialApp( - title: 'Flutter Demo', - theme: ThemeData( - // This is the theme of your application. - // - // TRY THIS: Try running your application with "flutter run". You'll see - // the application has a purple toolbar. Then, without quitting the app, - // try changing the seedColor in the colorScheme below to Colors.green - // and then invoke "hot reload" (save your changes or press the "hot - // reload" button in a Flutter-supported IDE, or press "r" if you used - // the command line to start the app). - // - // Notice that the counter didn't reset back to zero; the application - // state is not lost during the reload. To reset the state, use hot - // restart instead. - // - // This works for code too, not just values: Most code changes can be - // tested with just a hot reload. - colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), - useMaterial3: true, - ), - home: const MyHomePage(title: 'Flutter Demo Home Page'), - ); - } -} - -class MyHomePage extends StatefulWidget { - const MyHomePage({super.key, required this.title}); - - // This widget is the home page of your application. It is stateful, meaning - // that it has a State object (defined below) that contains fields that affect - // how it looks. - - // This class is the configuration for the state. It holds the values (in this - // case the title) provided by the parent (in this case the App widget) and - // used by the build method of the State. Fields in a Widget subclass are - // always marked "final". - - final String title; - - @override - State createState() => _MyHomePageState(); -} - -class _MyHomePageState extends State { - int _counter = 0; - - void _incrementCounter() { - setState(() { - // This call to setState tells the Flutter framework that something has - // changed in this State, which causes it to rerun the build method below - // so that the display can reflect the updated values. If we changed - // _counter without calling setState(), then the build method would not be - // called again, and so nothing would appear to happen. - _counter++; - }); - } - - @override - Widget build(BuildContext context) { - // This method is rerun every time setState is called, for instance as done - // by the _incrementCounter method above. - // - // The Flutter framework has been optimized to make rerunning build methods - // fast, so that you can just rebuild anything that needs updating rather - // than having to individually change instances of widgets. - return Scaffold( - appBar: AppBar( - // TRY THIS: Try changing the color here to a specific color (to - // Colors.amber, perhaps?) and trigger a hot reload to see the AppBar - // change color while the other colors stay the same. - backgroundColor: Theme.of(context).colorScheme.inversePrimary, - // Here we take the value from the MyHomePage object that was created by - // the App.build method, and use it to set our appbar title. - title: Text(widget.title), - ), - body: Center( - // Center is a layout widget. It takes a single child and positions it - // in the middle of the parent. - child: Column( - // Column is also a layout widget. It takes a list of children and - // arranges them vertically. By default, it sizes itself to fit its - // children horizontally, and tries to be as tall as its parent. - // - // Column has various properties to control how it sizes itself and - // how it positions its children. Here we use mainAxisAlignment to - // center the children vertically; the main axis here is the vertical - // axis because Columns are vertical (the cross axis would be - // horizontal). - // - // TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint" - // action in the IDE, or press "p" in the console), to see the - // wireframe for each widget. - mainAxisAlignment: MainAxisAlignment.center, - children: [ - 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), - ), // This trailing comma makes auto-formatting nicer for build methods. - ); - } -} diff --git a/lib/mains/gt.dart b/lib/mains/gt.dart new file mode 100644 index 0000000..04e6ca2 --- /dev/null +++ b/lib/mains/gt.dart @@ -0,0 +1,49 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +import '../widgets/simples/random.dart'; + +part 'gt.g.dart'; + +// We create a "provider", which will store a value (here "Hello world"). +// By using a provider, this allows us to mock/override the value exposed. +@riverpod +String helloWorld(HelloWorldRef ref) { + return 'Hello world'; +} + +void main() { + runApp( + // For widgets to be able to read providers, we need to wrap the entire + // application in a "ProviderScope" widget. + // This is where the state of our providers will be stored. + ProviderScope( + child: MyApp(), + ), + ); +} + +// Extend ConsumerWidget instead of StatelessWidget, which is exposed by Riverpod +class MyApp extends ConsumerWidget { + @override + Widget build(BuildContext context, WidgetRef ref) { + final String value = ref.watch(helloWorldProvider); + + return MaterialApp( + home: Scaffold( + appBar: AppBar(title: const Text('Example')), + body: Center( + child: Column( + children: [ + Expanded(child: Text('1')), + Expanded(child: Text(value)), + Expanded(child: RandomWidget()) + ], + ) + + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/outerapi/auth/call/getRandomData.dart b/lib/outerapi/auth/call/getRandomData.dart new file mode 100644 index 0000000..5db6b74 --- /dev/null +++ b/lib/outerapi/auth/call/getRandomData.dart @@ -0,0 +1,24 @@ + +// GET http://localhost:3000/p/random + +import 'dart:convert'; +import 'dart:io'; +import 'package:http/http.dart' as http; +import 'package:riverpod_annotation/riverpod_annotation.dart'; +import '../model/my_random.dart'; + +// Necessary for code-generation to work +part 'getRandomData.g.dart'; + +/// This will create a provider named `activityProvider` +/// which will cache the result of this function. +@riverpod +Future getMyRandom(GetMyRandomRef ref) async { + // Using package:http, we fetch a random activity from the Bored API. + var ur = Uri.http('192.168.160.207:3000', '/p/random'); + final response = await http.get(ur); + // Using dart:convert, we then decode the JSON payload into a Map data structure. + final json = jsonDecode(response.body) as Map; + // Finally, we convert the Map into an Activity instance. + return MyRandom.fromJson(json); +} diff --git a/lib/outerapi/auth/model/my_random.dart b/lib/outerapi/auth/model/my_random.dart new file mode 100644 index 0000000..86ede06 --- /dev/null +++ b/lib/outerapi/auth/model/my_random.dart @@ -0,0 +1,22 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:flutter/foundation.dart'; + +part 'my_random.freezed.dart'; +part 'my_random.g.dart'; + +@freezed +class MyRandom with _$MyRandom { + const MyRandom._(); + + const factory MyRandom({ + @JsonKey(name: 'complexV') @Default('') String complexv, + @JsonKey(name: 'floatV') @Default(0) num floatv, + @JsonKey(name: 'intV') @Default(0) num intv, + @JsonKey(name: 'nowV') @Default(0) num nowv + }) = _MyRandom; + + factory MyRandom.fromJson(Map json) => _$MyRandomFromJson(json); + +} + + diff --git a/lib/outerapi/auth/model/time.dart b/lib/outerapi/auth/model/time.dart new file mode 100644 index 0000000..4f5cb49 --- /dev/null +++ b/lib/outerapi/auth/model/time.dart @@ -0,0 +1,17 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:flutter/foundation.dart'; + +part 'time.freezed.dart'; +part 'time.g.dart'; + + +@freezed +class ServerTime with _$ServerTime { + const ServerTime._(); + + const factory ServerTime({@JsonKey(name: 'utc') @Default('') String utc}) = + _ServerTime; + + factory ServerTime.fromJson(Map json) => + _$ServerTimeFromJson(json); +} diff --git a/lib/widgets/simples/random.dart b/lib/widgets/simples/random.dart new file mode 100644 index 0000000..730ab9e --- /dev/null +++ b/lib/widgets/simples/random.dart @@ -0,0 +1,32 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:riverpod_flutter_tuts/outerapi/auth/call/getRandomData.dart'; + +import '../../outerapi/auth/model/my_random.dart'; + + +/// The homepage of our application +class RandomWidget extends StatelessWidget { + const RandomWidget({super.key}); + + @override + Widget build(BuildContext context) { + return Consumer( + builder: (context, ref, child) { + + final AsyncValue myRandom = ref.watch(getMyRandomProvider); + + return Center( + child: switch (myRandom) { + AsyncData(:final MyRandom value ) => Text( + 'myRandom: ${value}' + ), + AsyncError() => const Text('Oops, something unexpected happened'), + _ => const CircularProgressIndicator(), + + }, + ); + }, + ); + } +} \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index 1d71a2f..4633907 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -69,10 +69,10 @@ packages: dependency: transitive description: name: build_daemon - sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1" + sha256: "79b2aef6ac2ed00046867ed354c88778c9c0f029df8a20fe10b5436826721ef9" url: "https://pub.dev" source: hosted - version: "4.0.1" + version: "4.0.2" build_resolvers: dependency: transitive description: @@ -85,18 +85,18 @@ packages: dependency: "direct dev" description: name: build_runner - sha256: "3ac61a79bfb6f6cc11f693591063a7f19a7af628dc52f141743edac5c16e8c22" + sha256: "644dc98a0f179b872f612d3eb627924b578897c629788e858157fa5e704ca0c7" url: "https://pub.dev" source: hosted - version: "2.4.9" + version: "2.4.11" build_runner_core: dependency: transitive description: name: build_runner_core - sha256: "4ae8ffe5ac758da294ecf1802f2aff01558d8b1b00616aa7538ea9a8a5d50799" + sha256: e3c79f69a64bdfcd8a776a3c28db4eb6e3fb5356d013ae5eb2e52007706d5dbe url: "https://pub.dev" source: hosted - version: "7.3.0" + version: "7.3.1" built_collection: dependency: transitive description: @@ -258,10 +258,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 + sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c" url: "https://pub.dev" source: hosted - version: "2.0.3" + version: "4.0.0" flutter_riverpod: dependency: "direct main" description: @@ -275,14 +275,22 @@ packages: description: flutter source: sdk version: "0.0.0" - freezed_annotation: - dependency: transitive + freezed: + dependency: "direct dev" description: - name: freezed_annotation - sha256: c3fd9336eb55a38cc1bbd79ab17573113a8deccd0ecbbf926cca3c62803b5c2d + name: freezed + sha256: a434911f643466d78462625df76fd9eb13e57348ff43fe1f77bbe909522c67a1 url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.5.2" + freezed_annotation: + dependency: "direct main" + description: + name: freezed_annotation + sha256: f9f6597ac43cc262fa7d7f2e65259a6060c23a560525d1f2631be374540f2a9b + url: "https://pub.dev" + source: hosted + version: "2.4.3" frontend_server_client: dependency: transitive description: @@ -315,6 +323,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.2.0" + http: + dependency: "direct main" + description: + name: http + sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" + url: "https://pub.dev" + source: hosted + version: "1.2.1" http_multi_server: dependency: transitive description: @@ -348,45 +364,53 @@ packages: source: hosted version: "0.7.1" json_annotation: - dependency: transitive + dependency: "direct main" description: name: json_annotation sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" url: "https://pub.dev" source: hosted version: "4.9.0" + json_serializable: + dependency: "direct dev" + description: + name: json_serializable + sha256: ea1432d167339ea9b5bb153f0571d0039607a873d6e04e0117af043f14a1fd4b + url: "https://pub.dev" + source: hosted + version: "6.8.0" leak_tracker: dependency: transitive description: name: leak_tracker - sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" url: "https://pub.dev" source: hosted - version: "10.0.0" + version: "10.0.4" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.3" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.1" lints: dependency: transitive description: name: lints - sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "4.0.0" logging: dependency: transitive description: @@ -415,10 +439,10 @@ packages: dependency: transitive description: name: meta - sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.12.0" mime: dependency: transitive description: @@ -527,10 +551,10 @@ packages: dependency: transitive description: name: shelf_web_socket - sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" + sha256: "073c147238594ecd0d193f3456a5fe91c4b0abbcc68bf5cd95b36c4e194ac611" url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "2.0.0" sky_engine: dependency: transitive description: flutter @@ -544,6 +568,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.5.0" + source_helper: + dependency: transitive + description: + name: source_helper + sha256: "6adebc0006c37dd63fe05bca0a929b99f06402fc95aa35bf36d67f5c06de01fd" + url: "https://pub.dev" + source: hosted + version: "1.3.4" source_span: dependency: transitive description: @@ -612,10 +644,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.7.0" timing: dependency: transitive description: @@ -636,10 +668,10 @@ packages: dependency: transitive description: name: uuid - sha256: "814e9e88f21a176ae1359149021870e87f7cddaf633ab678a5d2b0bff7fd1ba8" + sha256: "83d37c7ad7aaf9aa8e275490669535c8080377cfa7a7004c24dfac53afffaa90" url: "https://pub.dev" source: hosted - version: "4.4.0" + version: "4.4.2" vector_math: dependency: transitive description: @@ -652,10 +684,10 @@ packages: dependency: transitive description: name: vm_service - sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 + sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" url: "https://pub.dev" source: hosted - version: "13.0.0" + version: "14.2.1" watcher: dependency: transitive description: @@ -672,14 +704,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.5.1" + web_socket: + dependency: transitive + description: + name: web_socket + sha256: "24301d8c293ce6fe327ffe6f59d8fd8834735f0ec36e4fd383ec7ff8a64aa078" + url: "https://pub.dev" + source: hosted + version: "0.1.5" web_socket_channel: dependency: transitive description: name: web_socket_channel - sha256: "58c6666b342a38816b2e7e50ed0f1e261959630becd4c879c4f26bfa14aa5a42" + sha256: a2d56211ee4d35d9b344d9d4ce60f362e4f5d1aafb988302906bd732bc731276 url: "https://pub.dev" source: hosted - version: "2.4.5" + version: "3.0.0" yaml: dependency: transitive description: @@ -689,5 +729,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.3.0 <4.0.0" - flutter: ">=3.0.0" + dart: ">=3.4.0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/pubspec.yaml b/pubspec.yaml index ce8bdd9..2e0e10d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -37,6 +37,9 @@ dependencies: cupertino_icons: ^1.0.2 flutter_riverpod: ^2.5.1 riverpod_annotation: ^2.3.5 + freezed_annotation: ^2.4.3 + json_annotation: ^4.9.0 + http: ^1.2.1 dev_dependencies: flutter_test: @@ -47,11 +50,13 @@ dev_dependencies: # activated in the `analysis_options.yaml` file located at the root of your # package. See that file for information about deactivating specific lint # rules and activating additional ones. - flutter_lints: ^2.0.0 + flutter_lints: ^4.0.0 riverpod_generator: ^2.4.0 - build_runner: ^2.4.9 + build_runner: ^2.4.11 custom_lint: ^0.6.4 riverpod_lint: ^2.3.10 + freezed: ^2.5.2 + json_serializable: ^6.8.0 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec @@ -94,3 +99,6 @@ flutter: # # For details regarding fonts from package dependencies, # see https://flutter.dev/custom-fonts/#from-packages + assets: + - assets/image/ + diff --git a/test/widget_test.dart b/test/widget_test.dart deleted file mode 100644 index 181fc7b..0000000 --- a/test/widget_test.dart +++ /dev/null @@ -1,30 +0,0 @@ -// This is a basic Flutter widget test. -// -// To perform an interaction with a widget in your test, use the WidgetTester -// utility in the flutter_test package. For example, you can send tap and scroll -// gestures. You can also use WidgetTester to find child widgets in the widget -// tree, read text, and verify that the values of widget properties are correct. - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import 'package:riverpod_flutter_tuts/main.dart'; - -void main() { - testWidgets('Counter increments smoke test', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); - - // Verify that our counter starts at 0. - expect(find.text('0'), findsOneWidget); - expect(find.text('1'), findsNothing); - - // Tap the '+' icon and trigger a frame. - await tester.tap(find.byIcon(Icons.add)); - await tester.pump(); - - // Verify that our counter has incremented. - expect(find.text('0'), findsNothing); - expect(find.text('1'), findsOneWidget); - }); -}