Flutter Mobx - setState() or markNeedsBuild() called during build - flutter

When I try to navigate from a page to another, I'm getting the error bellow. After some research i found some solutions as calling the navigation inside a SchedulerBinding.instance.addPostFrameCallback, but even if that solve in some cases, for this specific case none of them are working.
I'm using an IndexedStack with three AnimatedOpacity wrapping one different page. Besides the error, could be the way on that the IndexedStack renders its content?
Seems to be cause it renders all the pages at the same time while shows only the current index. But how i can solve this if a i need switch between these pages with the same state? I tried with PageView but the state is miss when change from one page to another.
Anyone can help me on this?
════════ Exception caught by flutter_mobx ══════════════════════════════════════
The following MobXCaughtException was thrown:
setState() or markNeedsBuild() called during build.
This Observer 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:
Observer
The widget which was currently being built when the offending call was made was:
AnimatedOpacity
When the exception was thrown, this was the stack
#0 Element.markNeedsBuild.<anonymous closure>
package:flutter/…/widgets/framework.dart:4167
#1 Element.markNeedsBuild
package:flutter/…/widgets/framework.dart:4182
#2 ObserverElementMixin.invalidate
package:flutter_mobx/src/observer_widget_mixin.dart:70
#3 ReactionImpl._run
package:mobx/…/core/reaction.dart:119
#4 ReactiveContext._runReactionsInternal
package:mobx/…/core/context.dart:345
...
The build method:
#override
Widget build(BuildContext context) {
return IndexedStack(
index: index,
children: <Widget>[
AnimatedOpacity(
key: Key('animatedOpacity_0'),
opacity: index == 0 ? 1.0 : 0.0,
duration: Duration(milliseconds: 500),
child: Page1(),
),
AnimatedOpacity(
key: Key('animatedOpacity_1'),
opacity: index == 1 ? 1.0 : 0.0,
duration: Duration(milliseconds: 500),
child: Page2(),
),
AnimatedOpacity(
key: Key('animatedOpacity_2'),
opacity: index == 2 ? 1.0 : 0.0,
duration: Duration(milliseconds: 500),
child: Page3(),
),
],
);
}
UPDATE:
The problem occurs on the third AnimatedOpacity, and if i remove the AnimatedOpacity widgets, the problems doesn't occurs but i can't figure it out why. Someone has some explanation about that?

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.

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.

ScaffoldMessenger throws a hero animation error

I am using the new ScaffoldMessenger to show a snackbar if a user successfully creates a project.
While showing the snackbar, i navigate the app to the dashboard. But as soon as it hits the dashboard There are multiple heroes that share the same tag within a subtree error is thrown.
I am not using any Hero widget in my dashbard and I have one FloatingActionButton but its hero parameter is set to null.
Sample code:
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('A SnackBar has been shown.'),
animation: null,
),
);
Navigator.pushReplacementNamed(context, '/dashboard');
Which results in this error:
The following assertion was thrown during a scheduler callback:
There are multiple heroes that share the same tag within a subtree.
Within each subtree for which heroes are to be animated (i.e. a PageRoute subtree), each Hero must have a unique non-null tag.
In this case, multiple heroes had the following tag: <SnackBar Hero tag - Text("A SnackBar has been shown.")>
Within each subtree for which heroes are to be animated (i.e. a PageRoute subtree), each Hero must have a unique non-null tag.
In this case, multiple heroes had the following tag: <SnackBar Hero tag - Text("A SnackBar has been shown.")>
Here is the subtree for one of the offending heroes: Hero
tag: <SnackBar Hero tag - Text("A SnackBar has been shown.")>
state: _HeroState#7589f
When the exception was thrown, this was the stack
#0 Hero._allHeroesFor.inviteHero.<anonymous closure>
#1 Hero._allHeroesFor.inviteHero
package:flutter/…/widgets/heroes.dart:277
#2 Hero._allHeroesFor.visitor
package:flutter/…/widgets/heroes.dart:296
#3 ComponentElement.visitChildren
package:flutter/…/widgets/framework.dart:4729
#4 Hero._allHeroesFor.visitor
package:flutter/…/widgets/heroes.dart:309
...
I had the same problem. This happens if you have nested Scaffolds. The ScaffoldMessenger wants to send the Snackbar to all Scaffolds. To fix this you need to wrap your Scaffold with a ScaffoldMessenger. This ensures you that only one of your Scaffold receives the Snackbar.
ScaffoldMessenger(
child: Scaffold(
body: ..
),
)
I ran into same problem and fixed it by removing SnackBar before any call to Navigator with ScaffoldMessenger.of(context).removeCurrentSnackBar().
Look like this with your Sample code:
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('A SnackBar has been shown.'),
animation: null,
),
);
ScaffoldMessenger.of(context).removeCurrentSnackBar();
Navigator.pushReplacementNamed(context, '/dashboard');
Here's the link that helped me : https://flutter.dev/docs/release/breaking-changes/scaffold-messenger#migration-guide
Hope it'll work for you
I resolved this by having the call run in the next event-loop iteration:
Future.delayed(const Duration(), () =>
ScaffoldMessenger.of(context).showSnackBar(SnackBar(...)));
Had the same problem, turns out I had a scaffold widget returning another scaffold in my subtree (whoops)
If so, then your snackbar is being popped on both scaffolds, and then initiating the transition causes the error.
So this is clearly a bug in Flutter.
The best "official" workaround, is to show the snack bar on the next frame:
WidgetsBinding.instance.addPostFrameCallback((_) {
// ... show the culprit SnackBar here.
});
But we can all agree that it shouldn't happen in the first place.
The answer by #GreenFrog assumes that you're handling navigation on your own, in the case you're facing this problem while using the default back button behavior of the Scaffold widget you'll need to wrap your Scaffold in a WillPopScope widget, this basically vetos requests to Navigator, moreover by using WillPopScope.onWillPop you can essentially call ScaffoldMessenger.of(context).removeCurrentSnackBar(); just before the route is popped.
Example:
...
WillPopScope(
onWillPop: () async {
ScaffoldMessenger.of(context).removeCurrentSnackBar();
return true;
},
child: Scaffold(...,
);

Duplicate Global Key Error in Navigation with Flutter's Animation Package

The Background: I am intending to use Flutter's animation package to run a container transform effect between a material card and another page (a details page). To set this up, the cards are set into a list within a "search" page to which then the container is opened, the DetailsScreen is passed a data object (an object within objectList according to the card's index). See below code:
SEARCHSCREEN
...
child: ListView.builder(
controller: _listController,
itemCount: objectList.length,
itemBuilder: (BuildContext context, int index) {
return OpenContainer(
transitionDuration: Duration(milliseconds: 750),
closedBuilder: (_, openContainer) {
return AnimatedCard(
direction: AnimatedCardDirection.left,
initDelay: Duration(milliseconds: 0),
duration: Duration(milliseconds: 500),
curve: Curves.easeOutBack,
child: ObjectCard(objectList[index],
savedObjectList, openContainer),
);
},
openBuilder: (_, closeContainer) {
return DetailScreen(objectList[index], closeContainer);
},
);
},
),
DETAILSCREEN
class DetailScreen extends StatefulWidget {
const DetailScreen(this.object, this.closeContainer);
final object;
final closeContainer;
#override
_DetailScreenState createState() => _DetailScreenState();
}
class _DetailScreenState extends State<DetailScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
...
The Problem: Whenever a card is clicked upon, the following error is returned:
The widget which was currently being built when the offending call was made was:
DetailScreen-[LabeledGlobalKey<_SearchScreenState>#b16ed]
When the exception was thrown, this was the stack:
...
═════ Exception caught by animation library ═════════════════════════════════
setState() or markNeedsBuild() called during build.
════════════════════════════════════════════════════════════════════════════════
Another exception was thrown: setState() or markNeedsBuild() called during build.
Another exception was thrown: Duplicate GlobalKey detected in widget tree.
════════ Exception caught by widgets library ═══════════════════════════════════
Duplicate GlobalKey detected in widget tree.
Having tried many solutions of adding and passing keys first to the constructors of each object, then to all of them, nothing has worked. The only thing that makes the animation work is if I remove the [index] from "return DetailScreen(objectList[index], closeContainer);". The animation will fire, but obviously is not desirable as now no object has been passed and hence nothing on the DetailScreen will work. What am I missing here? I was under the impression that on each openContainer click, a DetailsScreen was created, populated with the passed object's data and the destroyed when I used the closeContainer callback. Under this assumption, this should work because only one screen will exist at a time and no duplication of "Global Keys" will occur. But this now does not seem to be the case. Is there something up that I need to include higher up the widget chain, such as within my "SearchScreen" itself, which is hosting this code? Any help or insight would be greatly appreciated!

Failed assertion: line 556 pos 15: 'scrollOffsetCorrection != 0.0': is not true

After Upgrading the flutter to the latest version. I'm facing this issue, I've the same code for another application having earlier version of flutter and it is working fine.
With the new ListView add two or more children.
Scroll down the list to the point where the first child is entirely off the screen.
Scroll all the way back up to the initial position. The ListView shows nothing on screen (just white empty space).
Attaching minimal reproducible code:
import 'dart:async';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MYYApp(),
);
}
}
class MYYApp extends StatefulWidget {
#override
_MYYAppState createState() => _MYYAppState();
}
class _MYYAppState extends State<MYYApp> {
final list = [
'BMW',
'Fiat',
'Toyota',
'Fiat',
'Testa',
'Fiat',
'Ford',
'Fiat',
'BMW',
'Fiat',
'Toyota',
'Fiat',
'Testa',
'Fiat',
'Ford',
'Fiat'
];
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: ListView.builder(
itemCount: list.length,
itemBuilder: (context,index){
return list[index]=='Fiat'? //list[index] == 'Fiat' (this condition check is responsible for the issue and earlier it was not an issue)
Container(
height: 300,
child: Center(child: Text(list[index])),
):Container();
})
),
);
}
}
Here's the error:
════════ Exception caught by rendering library ═════════════════════════════════════════════════════
The method '-' was called on null.
Receiver: null
Tried calling: -(223.60756587000844)
The relevant error-causing widget was:
ListView file:///C:/Users/prave/AndroidStudioProjects/for_stackoverflow/lib/main.dart:49:25
════════════════════════════════════════════════════════════════════════════════════════════════════
════════ Exception caught by rendering library ═════════════════════════════════════════════════════
The following assertion was thrown during performLayout():
'package:flutter/src/rendering/sliver.dart': Failed assertion: line 556 pos 15: 'scrollOffsetCorrection != 0.0': is not true.
Either the assertion indicates an error in the framework itself, or we should provide substantially more information in this error message to help you determine and fix the underlying cause.
In either case, please report this assertion by filing a bug on GitHub:
https://github.com/flutter/flutter/issues/new?template=BUG.md
The relevant error-causing widget was:
ListView file:///C:/Users/prave/AndroidStudioProjects/for_stackoverflow/lib/main.dart:49:25
When the exception was thrown, this was the stack:
#2 new SliverGeometry (package:flutter/src/rendering/sliver.dart:556:15)
#3 RenderSliverList.performLayout (package:flutter/src/rendering/sliver_list.dart:180:20)
#4 RenderObject.layout (package:flutter/src/rendering/object.dart:1769:7)
#5 RenderSliverEdgeInsetsPadding.performLayout (package:flutter/src/rendering/sliver_padding.dart:137:11)
#6 RenderSliverPadding.performLayout (package:flutter/src/rendering/sliver_padding.dart:377:11)
This is only a part of error,it produces nearly 10 same kind of errors.
The error vanishes as soon as you give your alternative non-Fiat container a height of non-zero.
I don't know exactly why that is or if it's on purpose, but the list seems to have problems with zero-height elements.
I suggest you actually use a filtering mechanism on your data and not work around that part by making it zero height in the view as an afterthought.
I have same issue after upgrade flutter framework.
For me the description of problem is:
The issue: when you use a Container or Widget without child and or without height property inside ListVIew
The solution: Just give height property to Widget that inside ListView.
This is my code, that's worked for me
.......
body: ListView(
shrinkWrap: true,
children: <Widget>[
Auth ? Container(height: 1): signUpWidget() , // Add height property to Container
.....
......
]
)
Min height should be greater than 0. You can put it 0.1. It may be a bug but it works.
ListView(
shrinkWrap: true,
children: <Widget>[
SizedBox(height: 0.1,);
]