Flutter onStretchTrigger not working. Build scheduled during frame - flutter

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());
});

Related

flutter run firebase storage and firestore in new process or isolate causes binding issue

Im building an app where user upload a logo, I have activated resizer extension in firebase storage. hence, all images got uploaded, will be replaced with resized image with same reference but ends with "_600x600.png" instead of ".png".
Taking into account that in the upload function, if I wait for the original file to be uploaded, it does not guarantee that the new "600x600.png" done uploading.
My end goal is to get the download URL of the new file. My best method was to run from the client side an isolate function which try to get the new file path, if did not succeed (since it may take a while to upload the resized file) it will wait for 1 second, and re-try and so on...
Hence, if I make it in "new" process, I should not see any harm.
Here is what I did:
the global function for uploading:
Future updateResizedProfileUrl2(Map<String, dynamic> args) async {
/// Updating profile cover and logo images urls as individual process
/// imageType: logo, cover
String imageType = args['type'];
print('got into update function');
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
FirebaseFirestore _fs = FirebaseFirestore.instance;
FirebaseStorage _fst = FirebaseStorage.instance;
while (true) {
Reference fileRef =
_fst.ref(profileStorageDir).child('${imageType}_600x600.png');
try {
print('checking fileref $fileRef');
final refStr = await fileRef.getDownloadURL();
print('got url $refStr');
_fs.collection(settingsCollection).doc('profile').update(
{'media.$imageType': refStr},
);
print('updating media strrefStr');
return;
} catch (e) {
print('waaiting....');
print(e);
sleep(const Duration(seconds: 2));
}
}
}
here my main uploader function
MyClass... {
Future<void> uploadLogoImage(File image) async {
Reference ref = _fst.ref(profileStorageDir).child('logo.png');
await ref.putFile(image);
Map<String, dynamic> args = {
'type': 'logo',
};
print('sent update logo');
compute(updateResizedProfileUrl2, args);
}
}
but this does not work, with following error:
ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: Binding has not yet been initialized.
E/flutter (23103): The "instance" getter on the ServicesBinding binding mixin is only available once that binding has been initialized.
E/flutter (23103): Typically, this is done by calling "WidgetsFlutterBinding.ensureInitialized()" or "runApp()" (the latter calls the former). Typically this call is done in the "void main()" method. The "ensureInitialized" method is idempotent; calling it multiple times is not harmful. After calling that method, the "instance" getter will return the binding.
E/flutter (23103): In a test, one can call "TestWidgetsFlutterBinding.ensureInitialized()" as the first line in the test's "main()" method to initialize the binding.
E/flutter (23103): If ServicesBinding is a custom binding mixin, there must also be a custom binding class, like WidgetsFlutterBinding, but that mixes in the selected binding, and that is the class that must be constructed before using the "instance" getter.
E/flutter (23103): #0 BindingBase.checkInstance.<anonymous closure> (package:flutter/src/foundation/binding.dart:284:9)
E/flutter (23103): #1 BindingBase.checkInstance (package:flutter/src/foundation/binding.dart:366:6)
E/flutter (23103): #2 ServicesBinding.instance (package:flutter/src/services/binding.dart:54:54)
E/flutter (23103): #3 BasicMessageChannel.binaryMessenger (package:flutter/src/services/platform_channel.dart:166:45)
E/flutter (23103): #4 BasicMessageChannel.send (package:flutter/src/services/platform_channel.dart:180:38)
E/flutter (23103): #5 FirebaseCoreHostApi.initializeCore (package:firebase_core_platform_interface/src/pigeon/messages.pigeon.dart:201:23)
E/flutter (23103): #6 MethodChannelFirebase._initializeCore (package:firebase_core_platform_interface/src/method_channel/method_channel_firebase.dart:29:54)
E/flutter (23103): #7 MethodChannelFirebase.initializeApp (package:firebase_core_platform_interface/src/method_channel/method_channel_firebase.dart:73:13)
E/flutter (23103): #8 Firebase.initializeApp (package:firebase_core/src/firebase.dart:43:47)
E/flutter (23103): #9 updateResizedProfileUrl2 (package:appointments/providers/settings_mgr.dart:33:18)
E/flutter (23103): #10 _IsolateConfiguration.applyAndTime.<anonymous closure> (package:flutter/src/foundation/_isolates_io.dart:108:21)
E/flutter (23103): #11 Timeline.timeSync (dart:developer/timeline.dart:160:22)
E/flutter (23103): #12 _IsolateConfiguration.applyAndTime (package:flutter/src/foundation/_isolates_io.dart:106:21)
E/flutter (23103): #13 _spawn (package:flutter/src/foundation/_isolates_io.dart:127:67)
E/flutter (23103): #14 _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:300:17)
E/flutter (23103): #15 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:192:12)
E/flutter (23103):
I have initialized firebase since I need it and I understand that isolate has different memory that the main...
Writing: "WidgetsFlutterBinding.ensureInitialized()" in the global function does not work as well...
How can I achieve the above? or how I fix it ?
Thanks!!
to keep it short, you can not run native code from isolate while firebase storage plugin depends on native code.
what you need is to spawn an isolate with https://pub.dev/packages/isolate_handler or https://pub.dev/packages/flutter_isolate . Those support plugins, tho not all pluging may work. I can confirm that firebase storage works with isolate_handler.
keep in mind that even with this workaround native code will still run on a main thread, thats the limitation of dart
flutter uses platform channels to communicate with native code, and WidgetsBinding.ensureInitialized() is what it needs to use those channels, thats why you are getting this error.
there was also a problem https://github.com/firebase/flutterfire/issues/9790 which prevented to firebase storage from working correctly in isolates, but it got fixed recently

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

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?

setState() or markNeedsBuild() called during build inside a StreamBuilder

I am using this StreamBuilder to get the current location:
StreamBuilder<UserLocation>(
stream: locationService.locationStream,
builder: (context, snapshot) {
if (snapshot.data != null) {
bool es_actual = ubicacionesProvider.ubicacionActualSeleccionada;
bool es_elegida = ubicacionesProvider.ubicacionElegidaSeleccionada;
if(es_actual){
latitudData = snapshot.data.latitude;
// ubicacionesProvider.setlatitudActual(latitudData);
longitudData = snapshot.data.longitude;
//ubicacionesProvider.setlongitudActual(longitudData);
Coordinates misCoordenadas =
new Coordinates(latitudData, longitudData);
// ubicacionesProvider.setubicacionActual(_miDireccionActual);
getAddress(misCoordenadas);
}
if(es_elegida){
_latitudElegida = ubicacionesProvider.latitudElegida;
_longitudElegida = ubicacionesProvider.longitudElegida;
_miDireccionActual = ubicacionesProvider.ubicacionElegida;
}
}
I want to update a provider called ubicacionesProvider with some changes:
ubicacionesProvider.setlatitudActual(latitudData)
ubicacionesProvider.setlongitudActual(longitudData)
ubicacionesProvider.setubicacionActual(_miDireccionActual)
But I am getting a warning using one or all of them, the app is not exiting but the warning is shown:
======== Exception caught by foundation library ====================================================
The following assertion was thrown while dispatching notifications for UbicacionesProvider:
setState() or markNeedsBuild() called during build.
This _InheritedProviderScope<UbicacionesProvider> 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<UbicacionesProvider>
value: Instance of 'UbicacionesProvider'
listening to value
The widget which was currently being built when the offending call was made was: StreamBuilder<UserLocation>
dirty
dependencies: [MediaQuery]
state: _StreamBuilderBaseState<UserLocation, AsyncSnapshot<UserLocation>>#39568
When the exception was thrown, this was the stack:
#0 Element.markNeedsBuild.<anonymous closure> (package:flutter/src/widgets/framework.dart:4292:11)
#1 Element.markNeedsBuild (package:flutter/src/widgets/framework.dart:4307:6)
#2 _InheritedProviderScopeElement.markNeedsNotifyDependents (package:provider/src/inherited_provider.dart:496:5)
#3 ChangeNotifier.notifyListeners (package:flutter/src/foundation/change_notifier.dart:226:25)
#4 UbicacionesProvider.setlatitudActual (package:flutter_qplan/providers/ubicaciones_provider.dart:50:5)
...
The UbicacionesProvider sending notification was: Instance of 'UbicacionesProvider'
====================================================================================================
I would like to update the provider without getting that warning.
Usually this happens when you setState or notifyListeners before the build has finished building all the widgets. Maybe you can add your update logic like this :
WidgetsBinding.instance!.addPostFrameCallback((_) {
// Add Your Update Code here.
});

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

Flutter get library - Unhandled Exception: NoSuchMethodError: The getter 'overlay' was called on null. E/flutter (29207): Receiver: null

I want to show dialogue and Toast message without context as I am calling those from a method that has no context access. I found get library is appropriate. It mentioned that "you can open dialog from anywhere in your code without context", but when I put any simple code from example in my app code, I get errors. Here is my test code:
import 'package:get/get.dart';
void main() {
runApp(MyApp());
const oneSec = const Duration(milliseconds: 250);
new Timer.periodic(oneSec, (Timer t) => check());
}
void check() {
if (result == PI) { //global variables
//showDialogue();
//Get.snackbar('Hi', 'i am a modern snackbar');//error
Get.dialog(SimpleDialog());//error
}
}
void showDialogue(){
}
error:
[ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: NoSuchMethodError: The getter 'overlay' was called on null.
E/flutter (29207): Receiver: null
E/flutter (29207): Tried calling: overlay
E/flutter (29207): #0 Object.noSuchMethod (dart:core-patch/object_patch.dart:53:5)
E/flutter (29207): #1 Get.overlayContext (package:get/src/get_main.dart:907:62)
E/flutter (29207): #2 Get.dialog (package:get/src/get_main.dart:225:16)
E/flutter (29207): #3 checkVentilator.<anonymous closure> (package:ventilator/main.dart:82:17)
E/flutter (29207): #4 _rootRunUnary (dart:async/zone.dart:1192:38)
.............................
FlutterToast library code runs fine here. But I'm always getting somekind of error with Get library code. Even if this Get code executed from onTapp function of a button built as widget. I do not want to use GetMaterialAPP() but will it fix these errors? How can I solve this problem and show simple dialogue or toast?
Take a look at
https://pub.dev/packages/get#installing
In my case, I simply forgot switching out the MaterialApp.
EDIT: Sorry didn't fully read the question, was just happy that I figured out the solution and wanted to share it. I tried quite a few things, but it doesn't seem like Get can get a hold of the context without GetMaterialApp.
What's the issue with GetMaterialApp anyway?