flutter `tester.scrollUntilVisible` throws "Bad state: Too many elements" exception - flutter

I have the following elements encapsulated into a single ListView in my material app:
home: Scaffold(
appBar: AppBar(title: const Text("Flutter Layout")),
body: ListView(children: [
fibonacciSection,
// a ListView supports app body scrolling when the app is run on a small device.
Image.asset("images/lake.jpg",
width: 600,
height: 240,
fit: BoxFit
.cover), // BoxFit.cover tells the framework that the image should be as small as possible but cover its entire render box.
titleSection,
buttonsSection,
textSection,
statesSection
])));
And when I run the unit tests which contain the following code snippet:
await tester.pumpWidget(const MyApp(key: Key("StateManagemetTests")));
final listFinder = find.byType(Scrollable);
final itemFinder = find.byType(TapboxB);
// Scroll until the item to be found appears.
await tester.scrollUntilVisible(
itemFinder,
500.0,
scrollable: listFinder,
);
It throws the following exception:
══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following StateError was thrown running a test:
Bad state: Too many elements
When the exception was thrown, this was the stack:
#0 Iterable.single (dart:core/iterable.dart:656:24)
#1 WidgetController.widget (package:flutter_test/src/controller.dart:69:30)
#2 WidgetController.scrollUntilVisible.<anonymous closure> (package:flutter_test/src/controller.dart:1190:15)
#3 WidgetController.scrollUntilVisible.<anonymous closure> (package:flutter_test/src/controller.dart:1188:39)
#6 TestAsyncUtils.guard (package:flutter_test/src/test_async_utils.dart:71:41)
#7 WidgetController.scrollUntilVisible (package:flutter_test/src/controller.dart:1188:27)
#8 main.<anonymous closure> (file:///usr/src/flutter/flutter_app_layout/test/widget_test.dart:50:18)
<asynchronous suspension>
<asynchronous suspension>
(elided 3 frames from dart:async and package:stack_trace)
Any advice and insight is appreciated!

The error seems to occur when there are more than one Scrollable in the widget tree. Then Flutter doesn't know which one to scroll. You can solve that by first finding the correct Scrollable and telling scrollUntilVisible to use it:
// Scroll Save button into view
final listFinder = find.byType(Scrollable).last; // take last because the tab bar up top is also a Scrollable
expect(listFinder, findsOneWidget);
await tester.scrollUntilVisible(acceptButtonFinder, 100, scrollable: listFinder);
Enjoy!

Replacing scrollUntilVisible() with dragUntilVisible() solves the problem!
I don't find anything at all for scrollUntilVisible(). Is that an outdated API which should be removed from the framework?

Related

Proper use of Flutter globalKey getting null value on currentState

I am following along a youtube video to use the plugin CurvedNavigationBar here: https://www.youtube.com/watch?v=TX2x41h47fE
In the video around 3:30 time in video he shows how to use globalkey to dynamically change the tab and page. The problem I am having is that hes doing this all from one file. I am trying to get this to work from a different file and maybe I'm misunderstanding as any other tutorials or learning articles about globalkeys are all done using a single file example. I'm obvioulsy confused about the proper usage of it as the term global makes me think I can use this in other widgets.
In my home_screen.dart file I have the curved nav bar setup like so..
final GlobalKey<CurvedNavigationBarState> navigationKey = GlobalKey<CurvedNavigationBarState>();
bottomNavigationBar: Theme(
data: Theme.of(context).copyWith(
iconTheme: const IconThemeData(color: kPrimaryColor),
),
child: CurvedNavigationBar(
key: navigationKey,
animationDuration: const Duration(milliseconds: 300),
animationCurve: Curves.easeInOut,
color: kSecondaryColor,
backgroundColor: Colors.transparent,
items: items,
height: 60.h,
index: selectedIndex,
onTap: (selectedIndex) => setState(() => this.selectedIndex = selectedIndex),
),
and in a different widget in a different file I have a few buttons. Like the guy in the youtube video I try to use this key to access the method to setPage but it says undefined like so..
onPressed: () {
final navigationState = navigationKey.currentState!;
navigationState.setPage(1);
},
At first I was getting the error that navigationKey was undefined. So I added another declaration at the top of the 2nd file like so...
final GlobalKey<CurvedNavigationBarState> navigationKey = GlobalKey<CurvedNavigationBarState>();
When I declare this globalkey in the second file when I click the button I get this log error...
I/flutter (26457): ══╡ EXCEPTION CAUGHT BY GESTURE ╞═══════════════════════════════════════════════════════════════════
I/flutter (26457): The following _CastError was thrown while handling a gesture:
I/flutter (26457): Null check operator used on a null value
I/flutter (26457):
I/flutter (26457): When the exception was thrown, this was the stack:
I/flutter (26457): #0 NumbersWidget.buildButton.<anonymous closure> (package:vext/screens/profilepageviews/Widgets/numbers_widget.dart:73:67)
I/flutter (26457): #1 _InkResponseState.handleTap (package:flutter/src/material/ink_well.dart:1077:21)
I guess I'm wondering if I'm using this wrong or if it cant be used outside the initial file... Any insight would be most appreciated.

Flutter Testing: How to target (specific) widgets

I am testing an application created by the mobile apps division.
Hardly any of the widgets have any keys or labels to distinguish themselves from each other.
I'm having a hard timing even targeting a single widget, let alone 2 similar widgets on the same page; example: 2 text field widgets: username, password.
Right now, the only test I have is this:
testWidgets('Empty Login Box', (WidgetTester tester) async {
app.main();
await tester.pumpAndSettle();
final emailText = find.text("EMAIL");
expect(emailText, findsOneWidget);
});
And even this doesn't work. Here's the response:
00:40 +0: ...\EndevStudios\MedicalApp\gshDevWork\medical-app-frontend\integration_test\mock_image_upload_test.dart I00:43 +0: ...\EndevStudios\MedicalApp\gshDevWork\medical-app-frontend\integration_test\mock_image_upload_test.d 2,854ms
00:47 +0: Login Page Tests Empty Login Box
══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following TestFailure was thrown running a test:
Expected: exactly one matching node in the widget tree
Actual: _TextFinder:<zero widgets with text "EMAIL" (ignoring offstage widgets)>
Which: means none were found but one was expected
When the exception was thrown, this was the stack:
#4 main.<anonymous closure>.<anonymous closure> (file:///D:/WEBDEV/EndevStudios/MedicalApp/gshDevWork/medical-app-frontend/integration_test/mock_image_upload_test.dart:29:7)
<asynchronous suspension>
<asynchronous suspension>
(elided one frame from package:stack_trace)
This was caught by the test expectation on the following line:
file:///D:/WEBDEV/EndevStudios/MedicalApp/gshDevWork/medical-app-frontend/integration_test/mock_image_upload_test.dart line 29
The test description was:
Empty Login Box
════════════════════════════════════════════════════════════════════════════════════════════════════
00:47 +0 -1: Login Page Tests Empty Login Box [E]
Test failed. See exception logs above.
The test description was: Empty Login Box
00:48 +0 -1: Some tests failed.
I've been trying to use these CommonFinders class, but I can't seem to utilize them effectively.
https://docs.flutter.dev/cookbook/testing/widget/finders
https://api.flutter.dev/flutter/flutter_driver/CommonFinders-class.html
https://api.flutter.dev/flutter/flutter_test/CommonFinders-class.html
To anyone who can, please help!
You need to first pump your widget, if you don't do that, the Finder is not going to find your widget and will error:
The following TestFailure was thrown running a test: Expected: exactly
one matching node in the widget tree Actual: _TextFinder:<zero
widgets with text "EMAIL" (ignoring offstage widgets)> Which: means
none were found but one was expected
Try the following code:
testWidgets('Empty Login Box', (WidgetTester tester) async {
/// Pump your widget first:
await tester.pumpWidget(const LoginBoxWidget(
title: 'My Title',
message: 'Another parameter...',
)); // Parameters depend on your widget
final emailText = find.text("EMAIL");
expect(emailText, findsOneWidget);
});

Flutter onStretchTrigger not working. Build scheduled during frame

This code will work completely fine if I use Refresh indicator OR Button tap.
But wont work if use it inside SliverAppBar for onStretchTrigger function.
onStretchTrigger: () async {
setState(() {
myVariable = myFutureData();
});
},
The error I get:
I/flutter (31102): Build scheduled during frame.
I/flutter (31102): While the widget tree was being built, laid out,
and painted, a new frame was scheduled to rebuild the widget tree.
I/flutter (31102): This might be because setState() was called from a
layout or paint callback. If a change is needed to the widget tree, it
should be applied as the tree is being built. Scheduling a change for
the subsequent frame instead results in an interface that lags behind
by one frame. If this was done to make your build dependent on a size
measured at layout time, consider using a LayoutBuilder,
CustomSingleChildLayout, or CustomMultiChildLayout.
If, on the other hand, the one frame delay is the desired effect, for
example because this is an animation, consider scheduling the frame in
a post-frame callback using SchedulerBinding.addPostFrameCallback or
using an AnimationController to trigger the animation. I/flutter
(31102): I/flutter (31102): #0
WidgetsBinding._handleBuildScheduled.
package:flutter/…/widgets/binding.dart:747
I/flutter (31102): #1 WidgetsBinding._handleBuildScheduled
package:flutter/…/widgets/binding.dart:770
I/flutter (31102): #2 BuildOwner.scheduleBuildFor
package:flutter/…/widgets/framework.dart:2476
I/flutter (31102): #3 Element.markNeedsBuild
package:flutter/…/widgets/framework.dart:4324
I/flutter (31102): #4 State.setState
package:flutter/…/widgets/framework.dart:1108
I/flutter (31102): #5 _HomeScreenState.build.
package:fesale/screens/screen_home.dart:197
I/flutter (31102): #6 _HomeScreenState.build.
package:fesale/screens/screen_home.dart:195
I/flutter (31102): #7 RenderSliverPersistentHeader.layoutChild
package:flutter/…/rendering/sliver_persistent_header.dart:257
I/flutter (31102): #8
RenderSliverFloatingPersistentHeader.performLayout
package:flutter/…/rendering/sliver_persistent_header.dart:708
I/flutter (31102): #9 RenderObject.layout
package:flutter/…/rendering/object.dart:1858
I/flutter (31102): #10 RenderViewportBase.layoutChildSeq
Please help could not find any solution for that :(
INITIAL: First of all: you don't need to put an async there since you are not making use of the await keyword, it's just a function call which return value is being assigned to your variable.
UPDATE: onStretchTrigger actually expects an AsyncCallback so for this use case the async keyword has to be used - sorry!
Second: your error message states what causes this and offers several solution:
[...] This might be because setState() was called from a layout or paint callback. [...] consider scheduling the frame in a post-frame callback using SchedulerBinding.addPostFrameCallback [...]
So for now you can do:
SchedulerBinding.instance?.addPostFrameCallback((_) {
setState(() => myVariable = myFutureData());
});

Issue with scanning QR codes using flutter

Hi I'm trying to read a QR code and send the data in the QR code to my server. But when running flutter build ios I get the error in xcode when launching the app:
LateInitializationError: Field '_channel#598294394' has not been initialized.
#0 _QRViewState._channel (package:qr_code_scanner/src/qr_code_scanner.dart)
#1 _QRViewState.updateDimensions (package:qr_code_scanner/src/qr_code_scanner.dart:91:57)
#2 LifecycleEventHandler.didChangeAppLifecycleState (package:qr_code_scanner/src/lifecycle_event_handler.dart:15:29)
#3 WidgetsBinding.handleAppLifecycleStateChanged (package:flutter/src/widgets/binding.dart:692:16)
#4 ServicesBinding._handleLifecycleMessage (package:flutter/src/services/binding.dart:192:5)
#5 BasicMessageChannel.setMessageHandler.<anonymous closure> (package:flutter/src/services/platform_channel.dart:73:49)
#6 BasicMessageChannel.setMessageHandler.<anonymous closure> (package:flutter/src/services/platform_channel.dart:72:47)
#7 _DefaultBinaryMessenger.handlePlatformMessage (package:flutter/src/services/binding.dart:284:33)
#8 _invoke3.<anonymous closure> (dart:ui/hooks.dart:223:15)
#9 _rootRun (dart:async/zone.dart:1354:13)
#10 _CustomZone.run (dart:async/zone.dart:1258:19)
#11 _CustomZone.runGuarded (dart:async/zone.dart:1162:7)
#12 _invoke3 (dart:ui/hooks.dart:222:10)
#13 PlatformDispatcher._dispatchPlatformMessage (dart:ui/platform_dispatcher.dart:520:7)
#14 _dispatchPlatformMessage (dart:ui/hooks.dart:90:31)
The same thing happens when running flutter run
But that is fixed by doing a hot restart. Does anybody know why this is happening?
When this happens the app refuses to read any QR codes.
You can find my code on github here: https://github.com/maxall41/Package-Scanner
This is happening because you are calling setState before your widget has fully initialized itself. You can't call set state before the build method has finished because there is nothing to set the state of.
When you do a hot restart the phone (or emulator) keeps the state of the page or widget and rebuilds the ui. At that point, the build method of the widget gets called again and because your set state is in your build method, it is also getting called again, but this time on a state that was already initialized.
As a side note: Please post the relevant code instead of a link to your github repo. It helps you get better answers and it makes this a more useful question/ answer to the community
Edit: Here is the block of code causing problems.
Widget _buildQrView(BuildContext context) {
return QRView(
key: qrKey,
onQRViewCreated: _onQRViewCreated,
);
}
void _onQRViewCreated(QRViewController controller) {
//Here's the setState in the build method
setState(() {
this.controller = controller;
});
controller.scannedDataStream.listen((scanData) async {
setState(() {
result = "Scanned: " + scanData.code;
});
});
}
Removing the setState surrounding this.controller = controller should solve the problem

issues with consumers and providers

Hello I am facing an issue with my flutter app using providers and consumer.
everything works fine how ever I have this issue in my debugger:
════════ Exception caught by foundation library ════════════════════════════════
The following assertion was thrown while dispatching notifications for CatergoryProvider:
setState() or markNeedsBuild() called during build.
This _InheritedProviderScope<CatergoryProvider> widget cannot be marked as needing to build because the framework is already in the process of building widgets. A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase.
The widget on which setState() or markNeedsBuild() was called was: _InheritedProviderScope<CatergoryProvider>
value: Instance of 'CatergoryProvider'
listening to value
The widget which was currently being built when the offending call was made was: Consumer<CatergoryProvider>
dirty
dependencies: [_InheritedProviderScope<CatergoryProvider>]
When the exception was thrown, this was the stack
#0 Element.markNeedsBuild.<anonymous closure>
package:flutter/…/widgets/framework.dart:4138
#1 Element.markNeedsBuild
package:flutter/…/widgets/framework.dart:4153
#2 _InheritedProviderScopeElement.markNeedsNotifyDependents
package:provider/src/inherited_provider.dart:496
#3 ChangeNotifier.notifyListeners
package:flutter/…/foundation/change_notifier.dart:243
#4 CatergoryProvider.setLoading
package:quizz_app/provider/catergoryProvider.dart:43
...
The CatergoryProvider sending notification was: Instance of 'CatergoryProvider'
here is the consumer in question:
Consumer<CatergoryProvider>(
builder: (context, catergory, child) {
if (!catergory.isLoading() && !catergory.isList()) {
catergory.fetchCatergory();
}
catergories = catergory.getList();
return catergory.isLoading()
? ModalProgressHUD(
opacity: 0,
inAsyncCall: catergory.isLoading(),
child: Container())
: catergoryPage(catergories, context);
},
),
and this is where the stack trace leads me to:
void setLoading(value) {
loading = value;
notifyListeners();
}
It looks like you are probably calling setLoading(value) inside of a build method.. This would try to trigger a rebuild of the widget when notifyListeners() is invoked during the build of the widget.
Consider using a FutureBuilder or a StreamBuilder to change state depending when waiting on an async action.