Flutter plugin just_audio example error then dispose - flutter

I've just copied the example of just_audio library and put in into my project, and then I'm doing a hot reload or pressing back, I'm getting an error
[VERBOSE-2:shell.cc(209)] Dart Error: Unhandled exception:
Bad state: You cannot close the subject while items are being added from addStream
#0 Subject.close (package:rxdart/src/subjects/subject.dart:152:7)
#1 AudioPlayer.dispose (package:just_audio/just_audio.dart:611:30)
<asynchronous suspension>
#2 _LessonPractiseScreenState.dispose (package:wbh/ui/pages/lesson/lesson_practise_screen.dart:90:13)
#3 StatefulElement.unmount (package:flutter/src/widgets/framework.dart:4773:12)
#4 _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1935:13)
#5 _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1933:7)
#6 ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:4600:14)
#7 _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1931:13)
#8 _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1933:7)
#9 SingleChildRenderObjec<…>
The causing widget as I tested is:
Widget get _seekBar => StreamBuilder<Duration>(
stream: _player.durationStream,
builder: (context, snapshot) {
final duration = snapshot.data ?? Duration.zero;
return StreamBuilder<Duration>(
stream: _player.positionStream,
builder: (context, snapshot) {
if (snapshot.hasData) {
var position = snapshot.data ?? Duration.zero;
if (position > duration) {
position = duration;
}
return SeekBar(
duration: duration,
position: position,
onChanged: (newPosition) {
_player.seek(newPosition);
},
);
} else
return Container();
},
);
},
);
But I can't fix it by myself and have got no info on the net, so will appreciate any help.

This was reported as a bug via GitHub and a fix was rolled out soon after (see this issue for the discussion). Newer releases (>= 0.4.4) should resolve this issue.

Related

How to get firebase data back in simple array

I have two collections::
group
group users
The group users collection has documents ...
Each document has two fields
Group id
User id
The group collection has documents ...
Each document has two fields
Group manager id
Group name
Using the currently logged-in user, I want to do the following:
query the group users collection to get the groups id,
query the groups collection to get the groups name.
Pretty simple I think... BUT I CANT DO IT
This is the function that I am using:
printData() {
var arr = [];
groupUser.snapshots().listen((data) {
data.docs.forEach((element) {
print(element['Group id']);
arr.add(element['Group id']);
});
});
return arr.first;
}
I get the error below, but the print statement works:
The following StateError was thrown building TestView(dirty, state: _TestViewState#38814):
Bad state: No element
The relevant error-causing widget was
TestView
lib/views_and_widgets/navigator_view.dart:36
When the exception was thrown, this was the stack
#0 List.first (dart:core-patch/growable_array.dart:343:5)
#1 FirebaseCloudStorage.printData
package:ijob_clone_app/…/cloud/firebase_cloud_storage.dart:87
#2 _TestViewState.build
package:ijob_clone_app/views_and_widgets/test_view.dart:35
#3 StatefulElement.build
package:flutter/…/widgets/framework.dart:4992
#4 ComponentElement.performRebuild
package:flutter/…/widgets/framework.dart:4878
#5 StatefulElement.performRebuild
package:flutter/…/widgets/framework.dart:5050
#6 Element.rebuild
package:flutter/…/widgets/framework.dart:4604
#7 ComponentElement._firstBuild
package:flutter/…/widgets/framework.dart:4859
#8 StatefulElement._firstBuild
package:flutter/…/widgets/framework.dart:5041
#9 ComponentElement.mount
package:flutter/…/widgets/framework.dart:4853
... Normal element mounting (25 frames)
#34 Element.inflateWidget
package:flutter/…/widgets/framework.dart:3863
#35 MultiChildRenderObjectElement.inflateWidget
package:flutter/…/widgets/framework.dart:6435
#36 MultiChildRenderObjectElement.mount
package:flutter/…/widgets/framework.dart:6447
... Normal element mounting (113 frames)
#149 Element.inflateWidget
package:flutter/…/widgets/framework.dart:3863
#150 Element.updateChild
package:flutter/…/widgets/framework.dart:3586
#151 ComponentElement.performRebuild
package:flutter/…/widgets/framework.dart:4904
#152 StatefulElement.performRebuild
package:flutter/…/widgets/framework.dart:5050
#153 Element.rebuild
package:flutter/…/widgets/framework.dart:4604
#154 BuildOwner.buildScope
package:flutter/…/widgets/framework.dart:2667
#155 WidgetsBinding.drawFrame
package:flutter/…/widgets/binding.dart:882
#156 RendererBinding._handlePersistentFrameCallback
package:flutter/…/rendering/binding.dart:378
#157 SchedulerBinding._invokeFrameCallback
package:flutter/…/scheduler/binding.dart:1175
#158 SchedulerBinding.handleDrawFrame
package:flutter/…/scheduler/binding.dart:1104
#159 SchedulerBinding._handleDrawFrame
package:flutter/…/scheduler/binding.dart:1015
#160 _invoke (dart:ui/hooks.dart:148:13)
#161 PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:318:5)
This is the screen I am printing from:
class TestView extends StatefulWidget {
const TestView({Key? key}) : super(key: key);
#override
_TestViewState createState() => _TestViewState();
}
class _TestViewState extends State<TestView> {
late final FirebaseCloudStorage _groupUsersService;
String get userId => AuthService.firebase().currentUser!.id; // this is how I get the current logged in users userId
#override
void initState() {
_groupUsersService = FirebaseCloudStorage();
super.initState();
}
#override
Widget build(BuildContext context) {
print(_groupUsersService.printData()); // THIS IS THE PRINT STATEMENT
return Scaffold(
appBar: AppBar(
title: const Text('All users in my group'),
// ignore: prefer_const_literals_to_create_immutables
actions: [
// ignore: prefer_const_constructors
IconButton(
onPressed: null,
icon: const Icon(Icons.add),
),
],
),
body: StreamBuilder(
stream: _groupUsersService.getAllUsersInMyGroup(userId: userId),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
case ConnectionState.active:
if (snapshot.hasData) {
final allUsers = snapshot.data as Iterable<CloudGroupUser>;
return GroupUserListView(
cloudUsers: allUsers,
// onDeleteJob: (job) async {
// await _groupUsersService.deleteJob(documentId: job.documentId);
// },
onTap: (job) {
Navigator.of(context).pushNamed(
newJobRoute,
arguments: job,
);
},
);
} else {
return const CircularProgressIndicator();
}
default:
return const CircularProgressIndicator();
}
},
),
);
}
}
i removed the print statment from the view for the info below ( it still dont work)
I even tried to make it async ::
printData() async {
var arr = [];
await groupUser.snapshots().listen((data) {
data.docs.forEach((element) {
//print(element['Group id']);
arr.add(element['Group id']);
});
});
print(arr.first);
}
Restarted application in 525ms.
D/EGL_emulation( 4267): app_time_stats: avg=763.88ms min=17.12ms max=2249.43ms count=3
E/flutter ( 4267): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: Bad state: No element
E/flutter ( 4267): #0 List.first (dart:core-patch/growable_array.dart:343:5)
E/flutter ( 4267): #1 FirebaseCloudStorage.printData
package:ijob_clone_app/…/cloud/firebase_cloud_storage.dart:88
E/flutter ( 4267): <asynchronous suspension>
E/flutter ( 4267):
W/DynamiteModule( 4267): Local module descriptor class for com.google.android.gms.providerinstaller.dynamite not found.
I/DynamiteModule( 4267): Considering local module com.google.android.gms.providerinstaller.dynamite:0 and remote module com.google.android.gms.providerinstaller.dynamite:0
W/ProviderInstaller( 4267): Failed to load providerinstaller module: No acceptable module com.google.android.gms.providerinstaller.dynamite found. Local version is 0 and remote version is 0.
D/TrafficStats( 4267): tagSocket(141) with statsTag=0xffffffff, statsUid=-1
==============+======================
I tried the following as well and got this error
printData() async {
var arr = [];
await groupUser.snapshots().listen((data) {
data.docs.forEach((element) {
//print(element['Group id']);
arr.add(element.data()['Group id']);
});
});
print(arr.first);
}
the code on the view was changed to the following::
#override
Widget build(BuildContext context) {
_groupUsersService.printData();
this is the error I got
Restarted application in 558ms.
D/EGL_emulation( 4267): app_time_stats: avg=600.59ms min=20.48ms max=1754.64ms count=3
E/flutter ( 4267): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: Bad state: No element
E/flutter ( 4267): #0 List.first (dart:core-patch/growable_array.dart:343:5)
E/flutter ( 4267): #1 FirebaseCloudStorage.printData
package:ijob_clone_app/…/cloud/firebase_cloud_storage.dart:98
E/flutter ( 4267): <asynchronous suspension>
E/flutter ( 4267):
W/DynamiteModule( 4267): Local module descriptor class for com.google.android.gms.providerinstaller.dynamite not found.
I/DynamiteModule( 4267): Considering local module com.google.android.gms.providerinstaller.dynamite:0 and remote module com.google.android.gms.providerinstaller.dynamite:0
W/ProviderInstaller( 4267): Failed to load providerinstaller module: No acceptable module com.google.android.gms.providerinstaller.dynamite found. Local version is 0 and remote version is 0.
D/TrafficStats( 4267): tagSocket(123) with statsTag=0xffffffff, statsUid=-1
W/System ( 4267): Ignoring header X-Firebase-Locale because its value was null.
D/TrafficStats( 4267): tagSocket(121) with statsTag=0xffffffff, statsUid=-1
D/FirebaseAuth( 4267): Notifying id token listeners about user ( U8EZAjrCWQRvll6CVmI6OpGZwcH3 ).
You should be using element.data()['Group id'] instead of element['Group id'].
Happy coding :)
.onSnapshot() is an asynchronous observer. It returns a generic object with connection. First check are data he requests exits, then assign it somewhere or don't use snapshot, use simple .get() method. Unfortunately, I cannot help you more because I'm not a swift/flutter programmer.
I was looking for something like this
Future getAllUsersInManagersGroup(String userId) async {
//* managersGroupId: group id to which manager belongs to
//* allGroupUsersDocs: all GroupUser docs
//* allUserGroups: all GroupUsers in a List form
//* usersInManagersGroup: all users in managers group
// assumption: manager can only be in one group
String? managersGroupId;
final allGroupUsersDocs = await groupUser.get();
final allUserGroups = allGroupUsersDocs.docs.map((e) => e.data()).toList();
for (var user in allUserGroups) {
if (user['User id'] == userId) {
managersGroupId = user['Group id'];
break;
}
}
if (managersGroupId == null) return null;
var usersInManagersGroup = [];
for (var user in allUserGroups) {
if (user['Group id'] == managersGroupId) {
usersInManagersGroup.add(user['User id']);
}
}
return usersInManagersGroup;
}

Flutter Riverpod WidgetTest not exceeding past loading state

UPDATE: Rewrote Test in a same way I test another consumer widget, which actually works, but it still doesn't work for my test and I can't get my head around why :(
Current approach:
void main() {
final List<Exercise> _mockExercises = [
Exercise(
userID: 'uid123',
title: 'Übung1',
repetitions: 10,
isCompleted: false,
isVideo: false,
unit: 'Sekunden',
description: 'Some description',
exerciseID: '42',
imageUrl: 'https://via.placeholder.com/150',
thumbUrl: 'https://via.placeholder.com/150'),
Exercise(
userID: 'uid123',
title: 'Übung2',
repetitions: 20,
isCompleted: false,
isVideo: false,
unit: 'Sekunden',
description: 'Some description2',
exerciseID: '43',
imageUrl: 'https://via.placeholder.com/150',
thumbUrl: 'https://via.placeholder.com/150'),
];
testWidgets('user exercise list - pump', (WidgetTester tester) async {
await mockNetworkImagesFor(() async {
await tester.pumpWidget(ProviderScope(
child: MyApp(),
/// overrides: provide fake asyncvalue data to stream
overrides: [
exerciseCollectionStream
.overrideWithValue(AsyncValue.data(_mockExercises))
],
));
await tester.pump();
// The first frame is a loading state.
expect(find.byType(Loading), findsOneWidget);
await tester.pumpAndSettle();
// await Future.delayed(Duration(seconds: 2));
// await tester.pumpAndSettle();
// No-longer loading
expect(find.byType(Loading), findsNothing);
});
});
}
I'm trying to write a widget test for a Listview Widget and I never get past the loading state of the "Asyncvalue".when state in my test and it is stuck in loading.
I tried to approach the test like in the Riverpod docs or as explained here: https://codewithandrea.com/videos/flutter-state-management-riverpod/
But I got stuck :-/
We have Loading() Widget, that we test against to see if it disappears, but it doesn't..
That's the code of the test:
class ExerciseRepo {
// ignore: missing_return
Future<List<Exercise>> exerciseList() {
// should get data from database
}
}
final exerciseRepoProvider = Provider((ref) => ExerciseRepo());
final exerciseListProvider = FutureProvider<List<Exercise>>((ref) {
final repo = ref.watch(exerciseRepoProvider);
return repo.exerciseList();
});
class MockExercisesRepository extends Mock implements ExerciseRepo {
#override
Future<List<Exercise>> exerciseList() {
return Future.value([
Exercise(
title: 'Übung1',
repetitions: 10,
isCompleted: false,
isVideo: false,
unit: 'Sekunden',
description: 'Some description',
exerciseID: '42',
imageUrl: 'https://via.placeholder.com/150',
thumbUrl: 'https://via.placeholder.com/150'),
Exercise(
title: 'Übung2',
repetitions: 20,
isCompleted: false,
isVideo: false,
unit: 'Sekunden',
description: 'Some description2',
exerciseID: '43',
imageUrl: 'https://via.placeholder.com/150',
thumbUrl: 'https://via.placeholder.com/150'),
]);
}
}
void main() {
testWidgets('override repositoryProvider', (WidgetTester tester) async {
await mockNetworkImagesFor(() async {
await tester.pumpWidget(
ProviderScope(
overrides: [
exerciseListProvider.overrideWithProvider(
Provider((ref) => MockExercisesRepository))
],
child: MaterialApp(
home: Builder(builder: (context) {
return UserExerciseList();
}),
),
),
);
// The first frame is a loading state.
expect(find.byType(Loading), findsOneWidget);
await tester.pump();
await tester.pumpAndSettle();
// await Future.delayed(Duration(seconds: 3));
await tester.pumpAndSettle();
// No-longer loading
expect(find.byType(Loading), findsNothing);
});
});
}
The error message is:
The following TestFailure object was thrown running a test:
Expected: no matching nodes in the widget tree
Actual: _WidgetTypeFinder:<exactly one widget with type "Loading" (ignoring offstage widgets):
Loading>
Which: means one was found but none were expected
When the exception was thrown, this was the stack:
#4 main.<anonymous closure>.<anonymous closure> (file:///.../test/widget_exercise_list_test.dart:77:7)
<asynchronous suspension>
#5 main.<anonymous closure>.<anonymous closure> (file:///.../test/widget_exercise_list_test.dart)
#10 HttpOverrides.runZoned (dart:_http/overrides.dart:55:26)
#11 mockNetworkImagesFor (package:network_image_mock/src/network_image_mock.dart:9:24)
#12 main.<anonymous closure> (file:///.../test/widget_exercise_list_test.dart:54:11)
#13 testWidgets.<anonymous closure>.<anonymous closure> (package:flutter_test/src/widget_tester.dart:146:29)
<asynchronous suspension>
#14 testWidgets.<anonymous closure>.<anonymous closure> (package:flutter_test/src/widget_tester.dart)
#15 TestWidgetsFlutterBinding._runTestBody (package:flutter_test/src/binding.dart:784:19)
<asynchronous suspension>
#18 TestWidgetsFlutterBinding._runTest (package:flutter_test/src/binding.dart:764:14)
#19 AutomatedTestWidgetsFlutterBinding.runTest.<anonymous closure> (package:flutter_test/src/binding.dart:1173:24)
#20 FakeAsync.run.<anonymous closure>.<anonymous closure> (package:fake_async/fake_async.dart:178:54)
#25 withClock (package:clock/src/default.dart:48:10)
#26 FakeAsync.run.<anonymous closure> (package:fake_async/fake_async.dart:178:22)
#31 FakeAsync.run (package:fake_async/fake_async.dart:178:7)
#32 AutomatedTestWidgetsFlutterBinding.runTest (package:flutter_test/src/binding.dart:1170:15)
#33 testWidgets.<anonymous closure> (package:flutter_test/src/widget_tester.dart:138:24)
#34 Declarer.test.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/declarer.dart:175:19)
<asynchronous suspension>
#35 Declarer.test.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/declarer.dart)
#40 Declarer.test.<anonymous closure> (package:test_api/src/backend/declarer.dart:173:13)
#41 Invoker.waitForOutstandingCallbacks.<anonymous closure> (package:test_api/src/backend/invoker.dart:231:15)
#46 Invoker.waitForOutstandingCallbacks (package:test_api/src/backend/invoker.dart:228:5)
#47 Invoker._onRun.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/invoker.dart:383:17)
<asynchronous suspension>
#48 Invoker._onRun.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/invoker.dart)
#53 Invoker._onRun.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/invoker.dart:370:9)
#54 Invoker._guardIfGuarded (package:test_api/src/backend/invoker.dart:415:15)
#55 Invoker._onRun.<anonymous closure> (package:test_api/src/backend/invoker.dart:369:7)
#62 Invoker._onRun (package:test_api/src/backend/invoker.dart:368:11)
#63 LiveTestController.run (package:test_api/src/backend/live_test_controller.dart:153:11)
#64 RemoteListener._runLiveTest.<anonymous closure> (package:test_api/src/remote_listener.dart:256:16)
#69 RemoteListener._runLiveTest (package:test_api/src/remote_listener.dart:255:5)
#70 RemoteListener._serializeTest.<anonymous closure> (package:test_api/src/remote_listener.dart:208:7)
#88 _GuaranteeSink.add (package:stream_channel/src/guarantee_channel.dart:125:12)
#89 new _MultiChannel.<anonymous closure> (package:stream_channel/src/multi_channel.dart:159:31)
#93 CastStreamSubscription._onData (dart:_internal/async_cast.dart:85:11)
#127 new _WebSocketImpl._fromSocket.<anonymous closure> (dart:_http/websocket_impl.dart:1145:21)
#135 _WebSocketProtocolTransformer._messageFrameEnd (dart:_http/websocket_impl.dart:338:23)
#136 _WebSocketProtocolTransformer.add (dart:_http/websocket_impl.dart:232:46)
#146 _Socket._onData (dart:io-patch/socket_patch.dart:2044:41)
#155 new _RawSocket.<anonymous closure> (dart:io-patch/socket_patch.dart:1580:33)
#156 _NativeSocket.issueReadEvent.issue (dart:io-patch/socket_patch.dart:1076:14)
(elided 115 frames from dart:async and package:stack_trace)
This was caught by the test expectation on the following line:
file:///.../test/widget_exercise_list_test.dart line 77
The test description was:
override repositoryProvider
════════════════════════════════════════════════════════════════════════════════════════════════════
Test failed. See exception logs above.
The test description was: override repositoryProvider
That's the relevant part of the UserExerciseList()
#override
Widget build(BuildContext context, ScopedReader watch) {
AsyncValue<List<Exercise>> userExercisesList =
watch(exerciseCollectionStream);
return userExercisesList.when(
error: (error, stack) => ErrorInfo(error, stack),
loading: () => Loading(),
data: (List<Exercise> exercises) {
I also replaced Future with stream in my tests, didn't work either :-/ Any help is highly appreciated!
Many thanks!
I had to try out using several pumps to get a my mock data to return and change the state.
await tester.pumpWidget(ProviderScope(child: const MyApp()));
await tester.pump(Duration(seconds: 1));
await tester.pump(Duration(seconds: 1));
await tester.pump(Duration(seconds: 1));
expect(find.text("Sample Text"), findsOneWidget);
pumpAndSettle did not work.

Flutter tester..tap not working. How to solve "Bad state: No element" error?

I'm writing a widget test in my Flutter app. I'able to find the button using key or text or widget type but when I tap it, it gives Bad State No element error. Below is my test source code. I can also see the element in tester object while debugging under allWidgets item.
class MockSpeechRecognizer extends Mock implements SpeechRecognizer {}
void main() {
final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized();
setUpAll(() {
const MethodChannel('plugins.flutter.io/shared_preferences')
.setMockMethodCallHandler((MethodCall methodCall) async {
if (methodCall.method == 'getAll') {
return <String, dynamic>{}; // set initial values here if desired
}
return null;
});
setupLocator();
});
group('DashboardView Test | ', () {
testWidgets('Build Dashboard view and change keyword', (WidgetTester tester) async {
final Preferences preferences=Preferences();
preferences.keyword='hello';
final MockSpeechRecognizer speechService = MockSpeechRecognizer();
when(speechService.isServiceRunning).thenReturn(true);
locator.unregister<SpeechRecognizer>();
locator.registerLazySingleton<SpeechRecognizer>(() => speechService);
locator<LocalStorageService>().setUserPreferences(preferences);
// Build our app and trigger a frame.
binding.window.physicalSizeTestValue = Size(600, 300);
await tester.pumpWidget(MaterialApp(home:Dashboard()));
await tester.pumpAndSettle();
expect(find.byType(Dashboard),findsOneWidget);
await tester.tap(find.byKey(const Key('ChangeKeyword')));
});
});
}
══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following StateError was thrown running a test:
Bad state: No element
When the exception was thrown, this was the stack:
#0 Iterable.single (dart:core/iterable.dart:554:25)
#1 WidgetController._getElementPoint (package:flutter_test/src/controller.dart:646:47)
#2 WidgetController.getCenter (package:flutter_test/src/controller.dart:618:12)
#3 WidgetController.tap (package:flutter_test/src/controller.dart:256:18)
#4 main.<anonymous closure>.<anonymous closure> (file:///E:/Siak/Meow/meow-phone-finder/test/Widgets/dashboardView_test.dart:52:20)
<asynchronous suspension>
#5 main.<anonymous closure>.<anonymous closure> (file:///E:/Siak/Meow/meow-phone-finder/test/Widgets/dashboardView_test.dart)
#6 testWidgets.<anonymous closure>.<anonymous closure> (package:flutter_test/src/widget_tester.dart:140:29)
<asynchronous suspension>
#7 testWidgets.<anonymous closure>.<anonymous closure> (package:flutter_test/src/widget_tester.dart
Where do you define the Button, have you set the Key parameter?
await tester.pumpWidget(MaterialApp(home:
Container(child:
Button(
key: Key("ChangeKeyword"),
onTap: () => {},
child: Text("Press me"),
),
),
));

Flutter widget testing - no keyboard attached

I am trying to test my stateful widget CheckedTextField:
class _CheckedTextFieldState extends State<CheckedTextField> {
TextEditingController _controller = TextEditingController();
bool _checked;
String _valueBackup;
#override
void initState() {
super.initState();
_checked = widget.initialChecked;
_controller.text = widget.initialValue;
_controller.addListener(invokeCallback);
}
invokeCallback() {
widget.callback(_controller.text.trim(), _checked);
}
#override
Widget build(BuildContext context) {
return Row(
children: <Widget>[
Expanded(
child: TextField(
enabled: _checked,
controller: _controller,
decoration: widget.decoration,
),
),
Checkbox(
onChanged: (value) {
if (value == false) {
_valueBackup = _controller.text;
_controller.text = "";
}
if (value == true) {
_controller.text = _valueBackup;
}
setState(() {
_checked = value;
});
invokeCallback();
},
value: _checked,
),
],
);
}
}
When I try to test the widget with the following code, the engine tells me to show the keyboard first:
testWidgets('enter text when not checked', (WidgetTester tester) async {
String value = "";
bool checked = false;
await tester.pumpWidget(
wrapMaterial(
CheckedTextField(
initialValue: value,
initialChecked: checked,
callback: (_value, _checked) {
value = _value;
checked = _checked;
},
),
),
);
await tester.enterText(find.byType(TextField), "newText");
expect(value, "newText");
expect(checked, isFalse);
});
I already tried to show the keyboard manually with await tester.showKeyboard(find.byType(TextField)); before entering the text, but the issue still remains.
I also wrote a finder predicate to make sure the TextField is found, so the issue seems to be somewhere else.
The thrown exception:
══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following TestFailure object was thrown running a test:
Tried to use TestTextInput with no keyboard attached. You must use WidgetTester.showKeyboard() first.
When the exception was thrown, this was the stack:
#0 TestTextInput.updateEditingValue (package:flutter_test/src/test_text_input.dart:133:7)
#1 TestTextInput.enterText (package:flutter_test/src/test_text_input.dart:170:5)
#2 WidgetTester.enterText.<anonymous closure> (package:flutter_test/src/widget_tester.dart:875:21)
<asynchronous suspension>
#3 WidgetTester.enterText.<anonymous closure> (package:flutter_test/src/widget_tester.dart)
#6 TestAsyncUtils.guard (package:flutter_test/src/test_async_utils.dart:72:41)
#7 WidgetTester.enterText (package:flutter_test/src/widget_tester.dart:873:27)
#8 main.<anonymous closure> (file:///home/kevin/Projekte/Blackout/test/widget/checked_text_field/checked_text_field_test.dart:54:18)
<asynchronous suspension>
#9 main.<anonymous closure> (file:///home/kevin/Projekte/Blackout/test/widget/checked_text_field/checked_text_field_test.dart)
#10 testWidgets.<anonymous closure>.<anonymous closure> (package:flutter_test/src/widget_tester.dart:140:29)
<asynchronous suspension>
#11 testWidgets.<anonymous closure>.<anonymous closure> (package:flutter_test/src/widget_tester.dart)
#12 TestWidgetsFlutterBinding._runTestBody (package:flutter_test/src/binding.dart:703:19)
<asynchronous suspension>
#15 TestWidgetsFlutterBinding._runTest (package:flutter_test/src/binding.dart:683:14)
#16 AutomatedTestWidgetsFlutterBinding.runTest.<anonymous closure> (package:flutter_test/src/binding.dart:1083:24)
#17 FakeAsync.run.<anonymous closure>.<anonymous closure> (package:fake_async/fake_async.dart:177:54)
#22 withClock (package:clock/src/default.dart:46:10)
#23 FakeAsync.run.<anonymous closure> (package:fake_async/fake_async.dart:177:22)
#28 FakeAsync.run (package:fake_async/fake_async.dart:177:7)
#29 AutomatedTestWidgetsFlutterBinding.runTest (package:flutter_test/src/binding.dart:1080:15)
#30 testWidgets.<anonymous closure> (package:flutter_test/src/widget_tester.dart:133:24)
#31 Declarer.test.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/declarer.dart:172:27)
<asynchronous suspension>
#32 Declarer.test.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/declarer.dart)
#33 Invoker.waitForOutstandingCallbacks.<anonymous closure> (package:test_api/src/backend/invoker.dart:246:15)
#38 Invoker.waitForOutstandingCallbacks (package:test_api/src/backend/invoker.dart:243:5)
#39 Declarer.test.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/declarer.dart:170:33)
#44 Declarer.test.<anonymous closure> (package:test_api/src/backend/declarer.dart:169:13)
#45 Invoker._onRun.<anonymous closure>.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/invoker.dart:400:30)
(elided 36 frames from class _RawReceivePortImpl, class _Timer, dart:async, dart:async-patch, and package:stack_trace)
The test description was:
enter text when not checked
════════════════════════════════════════════════════════════════════════════════════════════════════
You need attach the textInput to it. Simply use
await tester.showKeyboard(find.byType(TextField));
testWidgets('enter text when not checked', (WidgetTester tester) async {
String value = "";
bool checked = false;
await tester.pumpWidget(
wrapMaterial(
CheckedTextField(
initialValue: value,
initialChecked: checked,
callback: (_value, _checked) {
value = _value;
checked = _checked;
},
),
),
);
await tester.showKeyboard(find.byType(TextField));
await tester.enterText(find.byType(TextField), "newText");
expect(value, "newText"); //it's not part of the question but this WILL not change
expect(checked, isFalse);
});
If you want to check state of this EditableText, ie. controller, widget properties, ...;
final _focusedEditable = tester.state<EditableTextState>(
find.descendant(
of: find.byType(TextField).first,
matching: find.byType(EditableText),
matchRoot: true,
),
);
If you want to check the text input use
tester.testTextInput, ie. keyboard visibility, clients listening

How to test custom SearchDelegate?

I'm trying to test my custom SearchDelegate. Example test would be checking after how many characters it starts giving suggestions.
I wrote two example tests which somehow affect each other. They're both the same, but the test that appears later in the code always fail when I run them together.
While debugging I found out FutureBuilder in buildSuggestions method doesn't wait for searchEngine.search(query) future to finish but it happens only for the second test.
I've already tried adding a test.runAsync with Future.delayed inside after tapping search icon. Also, I simplified the case to make it more readable.
You can find the full code here: https://github.com/pmiara/search-delegate-test-fail
or look at it below.
Application code:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class Entity {
final String value;
Entity.fromJson(Map<String, dynamic> json) : value = json['value'];
}
class MySearchDelegate extends SearchDelegate {
final MySearchEngine searchEngine;
MySearchDelegate({#required this.searchEngine});
#override
List<Widget> buildActions(BuildContext context) {
return [];
}
#override
Widget buildLeading(BuildContext context) {
return IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () {
close(context, null);
},
);
}
#override
Widget buildResults(BuildContext context) {
return FutureBuilder<List<Entity>>(
future: searchEngine.search(query),
builder: (BuildContext context, AsyncSnapshot<List<Entity>> snapshot) {
if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasData) {
final entities = snapshot.data;
return ListView.builder(
itemCount: entities.length,
itemBuilder: (context, index) => ListTile(
title: Text(entities[index].value),
onTap: () => close(context, entities[index]),
),
);
} else {
return Column();
}
},
);
}
#override
Widget buildSuggestions(BuildContext context) {
return FutureBuilder<List<Entity>>(
future: searchEngine.search(query),
builder: (BuildContext context, AsyncSnapshot<List<Entity>> snapshot) {
if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasData) {
final entities = snapshot.data;
return ListView.builder(
itemCount: entities.length,
itemBuilder: (context, index) => ListTile(
title: Text(entities[index].value),
onTap: () {
query = entities[index].value;
showResults(context);
},
),
);
} else {
return Column();
}
},
);
}
}
class MySearchEngine {
Future<List<Entity>> search(String query) async {
final jsonEntities =
await rootBundle.loadString('test_resources/entities.json');
final entities = jsonDecode(jsonEntities)
.map<Entity>((json) => Entity.fromJson(json))
.toList();
return entities;
}
}
class TestHomePage extends StatelessWidget {
final MySearchDelegate delegate;
const TestHomePage({#required this.delegate});
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Builder(
builder: (BuildContext context) {
return Scaffold(
body: Center(
child: IconButton(
icon: Icon(Icons.search),
onPressed: () async {
showSearch(
context: context,
delegate: delegate,
);
},
),
),
);
},
),
);
}
}
/// Run to see what tests should "see"
void main() => runApp(
TestHomePage(
delegate: MySearchDelegate(
searchEngine: MySearchEngine(),
),
),
);
Test file:
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:search_delegate_test/search_delegate_problem.dart';
void main() {
testWidgets('First test', (WidgetTester tester) async {
final delegate = MySearchDelegate(
searchEngine: MySearchEngine(),
);
await tester.pumpWidget(
TestHomePage(
delegate: delegate,
),
);
await tester.tap(find.byIcon(Icons.search));
await tester.pumpAndSettle();
await tester.enterText(find.byType(TextField), 'query');
await tester.pumpAndSettle();
expect(find.byType(ListTile), findsNWidgets(3));
});
testWidgets('Second test', (WidgetTester tester) async {
final delegate = MySearchDelegate(
searchEngine: MySearchEngine(),
);
await tester.pumpWidget(
TestHomePage(
delegate: delegate,
),
);
await tester.tap(find.byIcon(Icons.search));
await tester.pumpAndSettle();
await tester.enterText(find.byType(TextField), 'query');
await tester.pumpAndSettle();
expect(find.byType(ListTile), findsNWidgets(3));
});
}
pubsec.yaml:
name: search_delegate_test
description: A new Flutter application.
version: 1.0.0+1
environment:
sdk: ">=2.2.2 <3.0.0"
dependencies:
flutter:
sdk: flutter
dev_dependencies:
flutter_test:
sdk: flutter
test: any
flutter:
assets:
- test_resources/
uses-material-design: true
test_resources/entities.json:
[
{
"value": "abc"
},
{
"value": "abc123"
},
{
"value": "123def"
}
]
And the result of flutter doctor (I'm using Android Studio):
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, v1.7.8+hotfix.4, on Linux, locale pl_PL.UTF-8)
[✓] Android toolchain - develop for Android devices (Android SDK version 28.0.3)
[✓] Android Studio (version 3.4)
[!] IntelliJ IDEA Ultimate Edition (version 2019.1)
✗ Flutter plugin not installed; this adds Flutter specific functionality.
✗ Dart plugin not installed; this adds Dart specific functionality.
[!] IntelliJ IDEA Community Edition (version 2019.1)
✗ Flutter plugin not installed; this adds Flutter specific functionality.
✗ Dart plugin not installed; this adds Dart specific functionality.
[✓] Connected device (1 available)
! Doctor found issues in 2 categories.
Error that I get:
══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following TestFailure object was thrown running a test:
Expected: exactly 3 matching nodes in the widget tree
Actual: ?:<zero widgets with type "ListTile" (ignoring offstage widgets)>
Which: means none were found but some were expected
When the exception was thrown, this was the stack:
#4 main.<anonymous closure> (file:///path/to/project/search_delegate_test/test/serach_delegate_problem_test.dart:37:5)
<asynchronous suspension>
#5 testWidgets.<anonymous closure>.<anonymous closure> (package:flutter_test/src/widget_tester.dart:118:25)
<asynchronous suspension>
#6 TestWidgetsFlutterBinding._runTestBody (package:flutter_test/src/binding.dart:630:19)
<asynchronous suspension>
#9 TestWidgetsFlutterBinding._runTest (package:flutter_test/src/binding.dart:613:14)
#10 AutomatedTestWidgetsFlutterBinding.runTest.<anonymous closure> (package:flutter_test/src/binding.dart:1010:24)
#16 AutomatedTestWidgetsFlutterBinding.runTest (package:flutter_test/src/binding.dart:1007:15)
#17 testWidgets.<anonymous closure> (package:flutter_test/src/widget_tester.dart:116:22)
#18 Declarer.test.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/declarer.dart:168:27)
<asynchronous suspension>
#19 Invoker.waitForOutstandingCallbacks.<anonymous closure> (package:test_api/src/backend/invoker.dart:250:15)
<asynchronous suspension>
#24 Invoker.waitForOutstandingCallbacks (package:test_api/src/backend/invoker.dart:247:5)
#25 Declarer.test.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/declarer.dart:166:33)
#30 Declarer.test.<anonymous closure> (package:test_api/src/backend/declarer.dart:165:13)
<asynchronous suspension>
#31 Invoker._onRun.<anonymous closure>.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/invoker.dart:400:25)
<asynchronous suspension>
#45 _Timer._runTimers (dart:isolate-patch/timer_impl.dart:382:19)
#46 _Timer._handleMessage (dart:isolate-patch/timer_impl.dart:416:5)
#47 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:172:12)
(elided 28 frames from class _FakeAsync, package dart:async, package dart:async-patch, and package stack_trace)
This was caught by the test expectation on the following line:
file:///path/to/project/search_delegate_test/test/serach_delegate_problem_test.dart line 37
The test description was:
Second test
════════════════════════════════════════════════════════════════════════════════════════════════════
Test failed. See exception logs above.
The test description was: Second test