Flutter Bad State No Element - flutter

I am trying to delete my data from a database, and proceed to navigate to my homepage if it succeeds.
Below are my code:
StatelessWidget that consist of deleteFromDatabase method which passed an Id(String), an a context:
Consumer<SettingsProvider>(
builder: (context, settingsProvider, child) {
final exerciseSettings = settingsProvider.findById(id);
if (exerciseSettings == null) {
return Center(
child: CircularProgressIndicator(),
);
}
return PreviewExerciseItem(
exerciseSettings: exerciseSettings,
id: id,
navigateToEdit: () =>
_navigateToEditPage(exerciseSettings, context),
deleteFromDatabase: () => _deleteFromDatabase(id, context),
navigateToCountDown: () =>
navigateToCountDownPage(exerciseSettings, context),
);
},
),
_deleteFromDatabase method called from StatelessWidget and shows an AlertDialog to confirm deletion:
void _deleteFromDatabase(String id, context) async {
await showDialog(
context: context,
builder: (context) => new AlertDialog(
title: new Text("Are you sure you want to delete?"),
actions: <Widget>[
new FlatButton(
onPressed: () => Navigator.of(context).pop(false),
child: new Text('No'),
),
new FlatButton(
onPressed: () async {
try {
Navigator.of(context).pop(true);
await Provider.of<SettingsProvider>(context, listen: false)
.deleteFromList(id);
Navigator
.pushNamedAndRemoveUntil(context,HomePage.routeName, (Route route) => route.isFirst);
} catch (e) {
print(e);
}
},
child: new Text('Yes'),
),
],
),
);
}
deleteFromList method From My Provider class:
Future<void> deleteFromList(String id) async{
try{
final _itemIndex = _items.indexWhere((item) => item.id == id);
await _databaseHelper.deleteExercises(id);
_items.removeAt(_itemIndex);
notifyListeners();
}catch(e){
print(e);
}
}
findById from Provider Class:
CustomExercise findById(String id) {
return _items.firstWhere((prod) => prod.id == id);
}
Note: I am able to delete my data successfully from my database, however right before it navigates to my HomePage, an error pops out for a split second as a form of Red Screen: Bad State: No Element
Below are the full error message from my Log:
The following StateError was thrown building Consumer(dirty, dependencies: [_InheritedProviderScope, _InheritedTheme, _LocalizationsScope-[GlobalKey#5ce12]]):
Bad state: No element
The relevant error-causing widget was:
Consumer<SettingsProvider>
When the exception was thrown, this was the stack:
#0 ListMixin.firstWhere (dart:collection/list.dart:150:5)
#1 SettingsProvider.findById (package:workoutapp/providers/settings_provider.dart:12:19)
#2 PreviewExercisePage.build.<anonymous closure> (package:workoutapp/pages/preview_exercise_page.dart:68:55)
#3 Consumer.buildWithChild (package:provider/src/consumer.dart:175:19)
#4 SingleChildStatelessWidget.build (package:nested/nested.dart:260:41)

This is happens when the list is empty or maybe the first element is empty, so you should check the list is not empty.
List list = [];
print(list[0])
is sure you'll receive like this message:
Unhandled exception:
Bad state: No element
#0 List.first (dart:core-patch/growable_array.dart:332:5)
#1 main (file:///C:/Users/onbody/AndroidStudioProjects/nmae/bin/name.dart:9:14)
#2 _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:281:32)
#3 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)
the solution is:
List list = [];
if (list.isNotEmpty) {
print(list[0]);
} else {
print('the list is empty'!);
}
I hope this is helpful for someone Thanks!

As previously mentioned in the comments, it's likely that checking the values of an empty List causes the error. A workaround for this is to have a checker if the List is empty on both CustomExercise findById(String) and deleteFromList(String).
i.e.
if(_items != null && _items.length > 0)

We were using Drift (formerly Moor) and its watchSingle() method. That will throw this error, if no matching database row is found.
It was very hard to track down since a stream was emitting the error and it had no stack trace attached to it.
The fix was to use watch() with limit(1) instead and skip processing if the result is empty.

Related

How to solve this error "setState() or markNeedsBuild() called during build."?

So I have a table in moor which is returning a Future<List<..>> with that data I am trying to make pie chart
class Stats extends StatefulWidget {
#override
_StatsState createState() => _StatsState();
}
class _StatsState extends State<Stats> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_buildTaskList(context)
],
);
}
}
FutureBuilder<List<Account>> _buildTaskList(BuildContext context) {
Map<String, double> dataMap = Map();
dataMap.putIfAbsent("SNACKS", () => Provider.of<Amount>(context,listen: false).snack_amount);
dataMap.putIfAbsent("ENTERTAINMENT", () => Provider.of<Amount>(context,listen: false).entertainment_amount);
dataMap.putIfAbsent("STATIONARY", () => Provider.of<Amount>(context,listen: false).stationary_amount);
dataMap.putIfAbsent("OTHERS", () => Provider.of<Amount>(context,listen: false).others_amount);
final dao = Provider.of<AccountDao>(context);
return FutureBuilder(
future: dao.getAllAccounts(),
builder: (context, AsyncSnapshot<List<Account>> snapshot) {
final accounts = snapshot.data ?? List();
Provider.of<Amount>(context,listen: false).add(Calculate(accounts, Type.SNACKS), Calculate(accounts, Type.ENTERTAINMENT),
Calculate(accounts, Type.STATIONARY), Calculate(accounts, Type.OTHERS));
if (accounts == null) {
return Text('No Accounts Yet');
} else {
return PieChart(
dataMap: dataMap,
);
}
},
);
}
double Calculate(List<Account> list, Type type){
double sum=0;
for(int i=0;i<list.length;i++){
if(list[i].type==type){
sum=sum+list[i].amount;
}
}
return sum;
}
Now everytime I am running this I am getting an error
**The following assertion was thrown while dispatching notifications for Amount:
setState() or markNeedsBuild() called during build.
This _InheritedProviderScope 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
value: Instance of 'Amount'
listening to value
The widget which was currently being built when the offending call was made was: FutureBuilder>
dirty
state: _FutureBuilderState>#37282
When the exception was thrown, this was the stack:
0 Element.markNeedsBuild. (package:flutter/src/widgets/framework.dart:4167:11)
1 Element.markNeedsBuild (package:flutter/src/widgets/framework.dart:4182:6)
2 _InheritedProviderScopeElement.markNeedsNotifyDependents (package:provider/src/inherited_provider.dart:426:5)
3 ChangeNotifier.notifyListeners (package:flutter/src/foundation/change_notifier.dart:207:21)
4 Amount.add (package:moneymanager/main.dart:55:5)
...
The Amount sending notification was: Instance of 'Amount'
**
This is the Amount Class
class Amount extends ChangeNotifier {
double snack_amount=0.0;
double entertainment_amount=0.0;
double stationary_amount=0.0;
double others_amount=0.0;
void add(double snack,double entertainment,double stationary, double others){
snack_amount=snack;
entertainment_amount=entertainment;
stationary_amount=stationary;
others_amount=others;
notifyListeners();
}
}
The function to get the Future<List<..>>
```Future> getAllAccounts()=>select(accounts).get();``
EDIT
I edited my answer as suggested
return FutureBuilder(
future: dao.getAllAccounts(),
builder: (context, AsyncSnapshot<List<Account>> snapshot) {
if(snapshot.connectionState==ConnectionState.done){
final accounts = snapshot.data ?? List();
if (accounts == null) {
return Text('No Accounts Yet');
} else {
Provider.of<Amount>(context,listen: false).add(Calculate(accounts, Type.SNACKS), Calculate(accounts, Type.ENTERTAINMENT),
Calculate(accounts, Type.STATIONARY), Calculate(accounts, Type.OTHERS));
dataMap.putIfAbsent("SNACKS", () => Provider.of<Amount>(context,listen: false).snack_amount);
dataMap.putIfAbsent("ENTERTAINMENT", () => Provider.of<Amount>(context,listen: false).entertainment_amount);
dataMap.putIfAbsent("STATIONARY", () => Provider.of<Amount>(context,listen: false).stationary_amount);
dataMap.putIfAbsent("OTHERS", () => Provider.of<Amount>(context,listen: false).others_amount);
return PieChart(
dataMap: dataMap,
);
}
}else if(snapshot.connectionState==ConnectionState.waiting){
return Container();
}else{
return Container();
}
},
but still the same error
The following assertion was thrown while dispatching notifications for Amount: setState() or markNeedsBuild() called during build.
This error means that you are calling setState during the build phase
And from you logs it explicitly states
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
you can implement ConnectionState.waiting ConnectionState.done

Unhandled Exception: No MaterialLocalizations found

I am trying to show alert dialog for force update my app on my app version change using firebase remote config, calling versionCheck(context) from initState() everything is fine but getting error when I am calling showVersionDialog() method, here is my code
void main() => runApp(UniApp());
class UniApp extends StatefulWidget {
#override
State<StatefulWidget> createState() => _UniappMainState();
}
class _UniappMainState extends State<UniApp> {
AppTranslationsDelegate _newLocaleDelegate;
#override
void initState() {
super.initState();
setlocaleFromSharedPreference();
_newLocaleDelegate = AppTranslationsDelegate(newLocale: null);
UAAppContext.getInstance().onLocaleChanged = onLocaleChange;
//calling versionCheck
versionCheck(context);
}
versionCheck(context) async {
//Get Current installed version of app
final PackageInfo info = await PackageInfo.fromPlatform();
double currentVersion = double.parse(info.version.trim().replaceAll(".", ""));
//Get Latest version info from firebase config
final RemoteConfig remoteConfig = await RemoteConfig.instance;
try {
// Using default duration to force fetching from remote server.
await remoteConfig.fetch(expiration: const Duration(seconds: 0));
remoteConfig.setConfigSettings(RemoteConfigSettings(debugMode: true));
await remoteConfig.activateFetched();
remoteConfig.getString('force_update_current_version');
double newVersion = double.parse(remoteConfig
.getString('force_update_current_version')
.trim()
.replaceAll(".", ""));
print("cv-"+currentVersion.toString()+"nv--"+newVersion.toString());
if (newVersion > currentVersion) {
_showVersionDialog(context);
}
} on FetchThrottledException catch (exception) {
// Fetch throttled.
print(exception);
} catch (exception) {
print('Unable to fetch remote config. Cached or default values will be '
'used');
}
}
//Show Dialog to force user to update
_showVersionDialog(context) async {
await showDialog<String>(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
String title = "New Update Available";
String message =
"There is a newer version of app available please update it now.";
String btnLabel = "Update Now";
String btnLabelCancel = "Later";
return new AlertDialog(
title: Text(title),
content: Text(message),
actions: <Widget>[
FlatButton(
child: Text(btnLabel),
onPressed: () => _launchURL(CommonConstants.PLAY_STORE_URL),
),
FlatButton(
child: Text(btnLabelCancel),
onPressed: () => Navigator.pop(context),
),
],
);
},
);
}
_launchURL(String url) async {
if (await canLaunch(url)) {
await launch(url);
} else {
throw 'Could not launch $url';
}
}
void onLocaleChange(Locale locale) {
setState(() {
UAAppContext.getInstance().changeLanguage(locale.languageCode);
_newLocaleDelegate = AppTranslationsDelegate(newLocale: locale);
});
}
setlocaleFromSharedPreference() {
UAAppContext.getInstance().getLocale().then((locale) {
if (locale == 'en') return;
setState(() {
_newLocaleDelegate = AppTranslationsDelegate(newLocale: Locale(locale));
});
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
initialRoute: '/',
routes: {
'/': (context) => SplashScreen(),
CommonConstants.homeRoute: (context) { RouteParameters r = ModalRoute.of(context).settings.arguments;
if (r != null && r.appId != null && r.appId.isNotEmpty) {
return HomeScreen(
parentAppId: r.appId
);
} else return HomeScreen();},
CommonConstants.loginRoute: (context) => LoginScreen(),
CommonConstants.projectGroupRoute: (context) {
RouteParameters r = ModalRoute.of(context).settings.arguments;
if (r != null && r.appId != null && r.appId.isNotEmpty) {
return ProjectGroupScreen(
appId: r.appId,
attributes: r.groupingAttributes,
sortType: r.sortType,
);
} else
return SplashScreen();
},
CommonConstants.projectListRoute: (context) {
RouteParameters r = ModalRoute.of(context).settings.arguments;
if (r != null && r.appId != null && r.appId.isNotEmpty) {
return ProjectListScreen(
appId: r.appId,
sortType: r.sortType,
groupingKey: r.groupingKey,
groupingValue: r.groupingValue,
projectMasterDataTableList: r.projectMasterDataTableList,
);
} else
return SplashScreen();
},
CommonConstants.projectFormRoute: (context) {
RouteParameters r = ModalRoute.of(context).settings.arguments;
if (r != null && r.appId != null && r.appId.isNotEmpty) {
return ProjectFormScreen(
appId: r.appId,
projectId: r.projectId,
formActiontype: r.formActionType,
projectMasterDataKeyToValueMap: r.projectFieldsKeyToValue,
);
} else
return SplashScreen();
},
CommonConstants.getOTPRoute: (context) => GetOTPScreen(),
CommonConstants.changePasswordRoute: (context) =>
ChangePasswordScreen(),
CommonConstants.userRegistrationRoute: (context) =>
UserRegisterScreen(),
CommonConstants.downloadsRoute: (context) => DownloadScreen(),
CommonConstants.filterRoute: (context) {
RouteParameters r = ModalRoute.of(context).settings.arguments;
if (r != null && r.appId != null && r.appId.isNotEmpty) {
return FilterScreen(
appId: r.appId,
projectList: UAAppContext.getInstance().projectList,
filterKeyToValue:
UAAppContext.getInstance().filterSelectedValueMap,
);
} else
return SplashScreen();
},
CommonConstants.geoTaggingRoute: (context) {
RouteParameters r = ModalRoute.of(context).settings.arguments;
if (r != null &&
r.geoTaggingWidgetId != null &&
r.geoTaggingWidgetId.isNotEmpty) {
return GeotaggingWidget(
ctxt: r.context,
id: r.geoTaggingWidgetId,
gpsValidation: r.gpsValidation,
projLat: r.projLat,
projLon: r.projLon,
);
} else
return SplashScreen();
},
CommonConstants.profileRoute: (context) => UserProfileScreen(),
},
debugShowCheckedModeBanner: false,
// theme: UniappColorTheme.defaultTheme,
theme: UniappColorTheme.getTheme(),
localizationsDelegates: [
_newLocaleDelegate,
//provides localised strings
GlobalMaterialLocalizations.delegate,
//provides RTL support
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: [
const Locale("en", ""),
const Locale("hi", ""),
const Locale("or", "")
],
);
}
}
These are error msg I am getting when showing showVersionDialog() method, not getting what actually means it.
E/flutter (12951): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: No MaterialLocalizations found.
E/flutter (12951): UniApp widgets require MaterialLocalizations to be provided by a Localizations widget ancestor.
E/flutter (12951): Localizations are used to generate many different messages, labels, and abbreviations which are used by the material library.
E/flutter (12951): To introduce a MaterialLocalizations, either use a MaterialApp at the root of your application to include them automatically, or add a Localization widget with a MaterialLocalizations delegate.
E/flutter (12951): The specific widget that could not find a MaterialLocalizations ancestor was:
E/flutter (12951): UniApp
E/flutter (12951): The ancestors of this widget were:
E/flutter (12951): [root]
E/flutter (12951): #0 debugCheckHasMaterialLocalizations.<anonymous closure> (package:flutter/src/material/debug.dart:72:7)
E/flutter (12951): #1 debugCheckHasMaterialLocalizations (package:flutter/src/material/debug.dart:92:4)
E/flutter (12951): #2 showDialog (package:flutter/src/material/dialog.dart:843:10)
E/flutter (12951): #3 _UniappMainState._showVersionDialog (package:Uniapp/main.dart:80:11)
E/flutter (12951): #4 _UniappMainState.versionCheck (package:Uniapp/main.dart:67:9)
E/flutter (12951): <asynchronous suspension>
E/flutter (12951): #5 _UniappMainState.initState (package:Uniapp/main.dart:44:5)
I just solve this issue by creating a singleton class (MySingletonClass) with a variable
BuildContext get context => _context;
got this variable
MySingletonClass.getInstance().context;
Passing singleton class context to showdialog contex
final context = MySingletonClass.getInstance().context;
//calling showVersionDialong
_showVersionDialog(context);
I had a similar problem when using rflutter_alert package for showing pop-up alert dialog.
My code structure was :
void main () {
runApp(QuizPage)
}
inside QuizPage , the build method returns:
return MaterialApp(
home: Scaffold())
I was able to solve it by following the second approche in the instructions in this article: https://www.fluttercampus.com/guide/70/how-to-solve-no-materiallocalizations-found-error-exception-in-flutter/
Now my code structure is:
runApp(MaterialApp(home: MyApp()))
MyApp returns QuizPage

Race condition in Stateful widget with parameter

I'm having a race condition of data from a BehaviorSubject's stream not populating/updating the state before the build function returns.
content-detail.dart
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:domain_flutter/application-bloc.dart';
import 'package:domain_flutter/content.dart';
import 'package:domain_flutter/tag_chips.dart';
import 'package:cached_network_image/cached_network_image.dart';
class ContentDetail extends StatefulWidget {
final String slug;
ContentDetail({Key key, this.slug}) : super(key: key);
#override
State<StatefulWidget> createState() => _ContentDetailState();
}
class _ContentDetailState extends State<ContentDetail> {
Content _content = Content();
_getContent() {
print(widget.slug);
applicationBloc.contentOutput.map( (contents) =>
contents.where((item) => item.slug == widget.slug).toList())
.listen((data) => {
if (this.mounted) {
setState(() => _content = data.first)
}
});
}
#override
void initState() {
super.initState();
_getContent();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: _content != null ? Text(_content.title) : Text(''),
),
body:
SafeArea(
child: Card(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Container(
child: ListTile(
contentPadding: EdgeInsets.all(0),
title: Text(_content.title,style: TextStyle(fontWeight: FontWeight.bold)),
subtitle: Text(
DateFormat('dd.MM.yyyy').format(_content.changed),
style: TextStyle(fontSize: 12),
),
)),
TagChips(_content.tags),
CachedNetworkImage(
placeholder: (context, url) => CircularProgressIndicator(),
imageUrl: 'https://domain.tld/files/${_content.image}'),
],
),
),
),
);
}
}
The widget renders, but before it's rendered I receive an error.
If content isn't initialized I receive a different error.
What I mean by content initialized is
Content _content = Content();
content initialized:
The following assertion was thrown building ContentDetail(dirty, state: _ContentDetailState#15727):
A non-null String must be provided to a Text widget.
'package:flutter/src/widgets/text.dart':
Failed assertion: line 269 pos 10: 'data != null'
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
User-created ancestor of the error-causing widget was:
MaterialApp file:///home/darko/AndroidStudioProjects/domain_flutter/lib/main.dart:24:12
When the exception was thrown, this was the stack:
#2 new Text (package:flutter/src/widgets/text.dart:269:10)
#3 _ContentDetailState.build (package:domain_flutter/content_detail.dart:41:35)
#4 StatefulElement.build (package:flutter/src/widgets/framework.dart:4047:27)
#5 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3941:15)
#6 Element.rebuild (package:flutter/src/widgets/framework.dart:3738:5)
...
content uninitialized:
The following NoSuchMethodError was thrown building ContentDetail(dirty, state: _ContentDetailState#2be0b):
The getter 'title' was called on null.
Receiver: null
Tried calling: title
User-created ancestor of the error-causing widget was:
MaterialApp file:///home/darko/AndroidStudioProjects/domain_flutter/lib/main.dart:24:12
When the exception was thrown, this was the stack:
#0 Object.noSuchMethod (dart:core-patch/object_patch.dart:51:5)
#1 _ContentDetailState.build (package:domain_flutter/content_detail.dart:53:38)
#2 StatefulElement.build (package:flutter/src/widgets/framework.dart:4047:27)
#3 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3941:15)
#4 Element.rebuild (package:flutter/src/widgets/framework.dart:3738:5)
...
So yeah, there is no data when it's trying to render the component... or Widget/s rather.
I should probably show my applicationBloc
import 'dart:async';
import 'package:domain_flutter/application-entities.dart';
import 'package:domain_flutter/content.dart';
import 'package:domain_flutter/tag.dart';
import 'package:rxdart/rxdart.dart';
class ApplicationBloc {
final _applicationEntities = ApplicationEntities();
Sink<List<Content>> get contentInput => _contentInputController.sink;
Sink<List<Tag>> get tagInput => _tagInputController.sink;
Stream<List<Content>> get contentOutput => _contentOutputSubject.stream;
Stream<List<Tag>> get tagOutput => _tagOutputSubject.stream;
final _contentInputController = StreamController<List<Content>>();
final _tagInputController = StreamController<List<Tag>>();
final _contentOutputSubject = BehaviorSubject<List<Content>>();
final _tagOutputSubject = BehaviorSubject<List<Tag>>();
ApplicationBloc() {
_contentInputController.stream.listen(_handleContentInput);
_tagInputController.stream.listen(_handleTagInput);
}
void dispose() {
_contentInputController.close();
_contentOutputSubject.close();
_tagInputController.close();
_tagOutputSubject.close();
}
void _handleContentInput(List<Content> contentList) {
_applicationEntities.updateContent(contentList);
_contentOutputSubject.add(contentList);
}
void _handleTagInput(List<Tag> tagList) {
_applicationEntities.updateTags(tagList);
_tagOutputSubject.add(tagList);
}
}
final applicationBloc = ApplicationBloc();
You probably guessed, the idea is to load the JSON from a web service then provide it application wide via global variable.
That works without errors for everything but the ContentDetail class.
This ContentDetail class is almost a copy of another component that does almost the same, it filters by a tag slug and renders a list of Content.
This here only wants 1 item from the stream, Content with a specific slug property.
class Content {
// ...
final String slug;
}
As you can see I'm passing the slug in the ContentDetail constructor and my _ContentDetailState is accessing its property via widget.slug.
The _getContent() function of the ContentListByTagSlug class in comparison:
void _getContent() {
applicationBloc.contentOutput.map(
(contents) => contents.where(
(item) => item.tags.any((tag) => tag.slug == widget.tag.slug)
).toList()
)
.listen((data) => {
if (this.mounted) {
setState(() => {content = data})
}
});
}
This works, while only getting 1 item from it doesn't (see 1st code snippet).
This is how I define the FlatButton to open the ContentDetail page:
FlatButton(
child: Text('Read more'),
onPressed: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
ContentDetail(slug: content[index].slug))),
),
It's valid, the slug is passed as the result of the print function indicates.
I'm not sure what to do to ascertain that the _content variable is populated before the build function is executed.
In Angular one creates a resolver, which then populates data before the component is created/initialized.
In Flutter?
Note I'm not using the bloc package, but put the streams in a dedicated class, then use setState to update the state in the listening classes, as the bloc package seems overkill for this scenario.
3 widgets (4 if you count the drawer)
1 displaying an unfiltered list
2 displaying a list filtered by tag slug
3 displaying one item filtered by tag slug
only the latter has errors.
update1: I even removed the if (this.mounted) check in _getContent() and I'm calling _getContent() again in the builder if _content is null.
Then I have changed _content to be a List and am getting _content.first.title which results in
Bad state: No element
But the widget is still rendered correctly.
So it seems that there are 2 invocations of the widget. One that throws errors, which is discarded and one that doesn't which is kept. I'm not familiar with the internals so that's my best guess.
This is answered in this answer.
What I'm taking away from it is
don't initialize variables that are meant to be filled by a stream
provide a check in the builder whether the value to be filled in null
if null render a loading screen else render the desired widget with populated state

"setState() or markNeedsBuild() called during build" error trying to push a replacement in Navigator inside a Consumer widget (provider package)

This week I've began developing in flutter and i'm not able to solve this problem.
I'm building a login page that calls an API to login and after redirects to an homepage.
This is the exception generated by Navigator.pushReplacement in the first code bloc.
In that moment apiCall.isFetching is false cause fetching ended and apiCall.response contains the required data.
Exception details:
════════ Exception caught by widgets library ═══════════════════════════════════════════════════════
The following assertion was thrown building Consumer<ApiCallChangeNotifier>(dirty, dependencies: [InheritedProvider<ApiCallChangeNotifier>]):
setState() or markNeedsBuild() called during build.
This Overlay 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: Overlay-[LabeledGlobalKey<OverlayState>#4dc85]
state: OverlayState#bd97e(tickers: tracking 1 ticker, entries: [OverlayEntry#2941b(opaque: false; maintainState: false), OverlayEntry#37814(opaque: false; maintainState: true), OverlayEntry#f92c0(opaque: false; maintainState: false), OverlayEntry#da26d(opaque: false; maintainState: true)])
The widget which was currently being built when the offending call was made was: Consumer<ApiCallChangeNotifier>
dirty
dependencies: [InheritedProvider<ApiCallChangeNotifier>]
User-created ancestor of the error-causing widget was:
Expanded file:///C:/flutter_test/lib/screens/login/LoginScreen.dart:153:37
When the exception was thrown, this was the stack:
#0 Element.markNeedsBuild.<anonymous closure> (package:flutter/src/widgets/framework.dart:3687:11)
#1 Element.markNeedsBuild (package:flutter/src/widgets/framework.dart:3702:6)
#2 State.setState (package:flutter/src/widgets/framework.dart:1161:14)
#3 OverlayState.insertAll (package:flutter/src/widgets/overlay.dart:346:5)
#4 OverlayRoute.install (package:flutter/src/widgets/routes.dart:43:24)
...
Here is my function to create the login button, it's called from build function of LoginScreen (StatelessWidget)
Widget loginButton(BuildContext context) {
return Consumer<ApiCallChangeNotifier>(
builder: (context, apiCall, child) => apiCall.isFetching
? CircularProgressIndicator()
: apiCall.response != null
? Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => HomeScreen(
(apiCall.response as LoginResponse).email)))
: RaisedButton(
...
onPressed: () {
attemptLogin(context);
},
...
));
}
The attemptLogin funtion:
void attemptLogin(BuildContext context) {
Provider.of<ApiCallChangeNotifier>(context, listen: false).callApi(
MyApiServices().attemptLogin,
{
'email': emailController.value.text,
'password': passwordController.value.text,
},
urlController.value.text
);
}
ApiCallChangeNotifier
class ApiCallChangeNotifier extends ChangeNotifier {
bool isFetching = false;
Object response;
Future<LoginResponse> callApi(apiFunction, bodyParams, customUrl) async {
isFetching = true;
notifyListeners();
response = await apiFunction(bodyParams, customUrl);
isFetching = false;
notifyListeners();
return response;
}
}
MyApiServices.attemptLogin is a function that handles the API call and returns an Object LoginResponse
Hope I've given enough info!
For me, It was when I use navigator before the build finished!
Just put your navigation code in here:
WidgetsBinding.instance.addPostFrameCallback((_) {
// Do everything you want here...
});
Instead of trying to push the new route from LoginResponse Consumer I modified attemptLogin() to wait the result and to navigate to the new route!
void attemptLogin(BuildContext context) async {
LoginResponse _apiResponse =
await Provider.of<ApiCallChangeNotifier>(context, listen: false)
.callApi(
MyApiServices().attemptLogin,
{
'email': emailController.value.text,
'password': passwordController.value.text,
},
urlController.value.text);
if (_apiResponse != null) {
if (_apiResponse.email != null) {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => HomeScreen(_apiResponse.email)));
} else if (_apiResponse.errorMessage != null) {
Scaffold.of(context)
.showSnackBar(SnackBar(content: Text(_apiResponse.errorMessage)));
} else {
Scaffold.of(context).showSnackBar(
SnackBar(content: Text(KanbanBOXApi().unknownErrorMessage)));
}
}
}

dismiss database data with listview

now i made the code like this
FutureBuilder(
future: getData2(),
builder: (context, snapshot) {
if (snapshot.hasData) {
List data = snapshot.data;
return ListView.builder(
shrinkWrap: true,
itemCount: data.length,
itemBuilder: (context, index) {
final item = data[index];
return Dismissible(
key: Key(item['loc3']),
onDismissed: (direction) {
setState(() async {
await openData2().then((value) {
value.delete(
'test2',
where: 'loc3 = ?',
whereArgs: ['loc3'],
);
});
});
},
child: ListTile(
title: Text(item['loc3']),
),
);
},
);
but when i dismiss one item
there's some note in console 'Another exception was thrown: setState() callback argument returned a Future.'
in setState i don't return any variable why they say like this?
and when i first delete one item there is more detail error
The following assertion was thrown while notifying listeners for AnimationController:
I/flutter ( 6018): setState() callback argument returned a Future.
I/flutter ( 6018): The setState() method on PositionedTilesState#6d110 was called with a closure or method that
I/flutter ( 6018): returned a Future. Maybe it is marked as "async".
I/flutter ( 6018): Instead of performing asynchronous work inside a call to setState(), first execute the work (without
I/flutter ( 6018): updating the widget state), and then synchronously update the state inside a call to setState().
I/flutter ( 6018): When the exception was thrown, this was the stack:
I think the 1 problem is that you have an async function inside setState. Change it to this:
...
onDismissed: (direction) async {
await openData2().then((value) {
value.delete(
'test2',
where: 'loc3 = ?',
whereArgs: ['loc3'],
);
});
setState(() {});
},
'''
or this (more readable):
...
onDismissed: (direction) {
Future<void> delete() async {
await openData2().then((value) {
value.delete(
'test2',
where: 'loc3 = ?',
whereArgs: ['loc3'],
);
});
}
setState(() {
delete();
});
},
'''
The point is, you can't have async operations performed inside setState.