setState() or markNeedsBuild() called during build from changeNotifier - flutter

I'm getting this error, and i know it's because of a line of code where I listen for data from Provider class, so, first question is, does changeNotifier call setState() when it needs to notifyListeners, or is it Provider calling setState or markNeedsBuild, I'm confused, also, please how to solve it, here is my code
here is where I'm using it
BottomNavigationBarItem(
icon: Badge(
showBadge:
Provider.of<NotificationsModel>(context, listen: false).unSeen > 0, // this is the problem
badgeContent: Text(
Provider.of<NotificationsModel>(context, listen: false).unSeen.toString(), // and this
style: TextStyle(
color: Colors.white,
fontFamily: kFontFamily,
fontWeight: FontWeight.w600,
fontSize: Dimensions.font7,
),
),
child: Icon(dModel.index == 2
? Icons.notifications
: Icons.notifications_paused_outlined),
),
label: 'Notifications'),
and here is unseen in changeNotifier class
int get unSeen {
int notSeen = 0;
for(Notification notification in _notifications) {
if(notification.isSeen == false) {
notSeen++;
notifyListeners();
}
}
return notSeen;
}
so, please, how can i make it stop trying to build or whatever it's doing, Thanks

Wrap your notifyListeners(); inside a Future.delayed(Duration.zero, notifyListeners);
this happens when you notifyListeners(); during the build screen process which will fire this exception because you are trying to update your UI while the initial UI is not built yet

Related

How to dispose Timer Button in flutter?

I have a timer button that waits for 30 seconds then be active, it is used to allow the user resend OTP verification code. there is also a "Verify" button that verifies the entered OTP and completes the authentication and then resets some variabe through setState.
problem is when I press the Verify button while the timer button is still counting, an error happens saying:
Unhandled Exception: setState() called after dispose():
_TimerButtonState#70c61(lifecycle state: defunct, not mounted)
E/flutter ( 3950): This error happens if you call setState() on a State object for a
widget that no longer appears in the widget tree (e.g., whose parent widget no longer
includes the widget in its build). This error can occur when code calls setState() from
a timer or an animation callback.
E/flutter ( 3950): The preferred solution is to cancel the timer or stop listening to
the animation in the dispose() callback. Another solution is to check the "mounted"
property of this object before calling setState() to ensure the object is still in the
tree.
by testing I can see it only happens if setState happens while the counter is still running. but can't find a way to make it stop counting prior to setstate.
Any Idea how can I solve this?
Timer Button
TimerButton(
buttonType: ButtonType.TextButton,
label: "Resend Code".tr,
timeOutInSeconds: 30,
onPressed: () async {
await sendOTP();
},
resetTimerOnPressed: false,
disabledColor: Colors.grey,
color: const Color.fromARGB(255, 245, 91, 165),
disabledTextStyle:
TextStyle(fontSize: 12.sp, color: Colors.white),
activeTextStyle: TextStyle(
fontSize: 13.sp,
color: Colors.white,
),
),
Verify Button:
SizedBox(
height: 35.h,
width: 220.w,
child: ElevatedButton(
onPressed: () async {
if (loading == true) {
} else {
final PhoneAuthCredential phoneAuthCredential =
PhoneAuthProvider.credential(
verificationId: verificationId,
smsCode: otpcontroller.text);
signInWithPhoneAuthCredential(phoneAuthCredential);
}
},
child: Container(
width: 220.w,
alignment: Alignment.center,
child: Text(
"Verify".tr,
style: GoogleFonts.lato(
fontStyle: FontStyle.normal,
color: Colors.white,
fontSize: 20.sp,
),
),
),
style: TextButton.styleFrom(
primary: Colors.white,
backgroundColor: const Color.fromARGB(255, 245, 91, 165)),
),
Verification Function:
void signInWithPhoneAuthCredential(
PhoneAuthCredential phoneAuthCredential) async {
setState(() {
loading = true;
});
try {
// ignore: non_constant_identifier_names
final AuthCredential =
await _auth.signInWithCredential(phoneAuthCredential);
setState(() {
loading = false;
});
if (AuthCredential.user != null) {}
} on FirebaseAuthException catch (e) {
setState(() {
loading = false;
});
Flushbar(
message: e.message,
duration: const Duration(seconds: 4),
).show(context);
}
}
Didn't find good solution to this, instead I replaced the timer button with a normal TextButton and circular_countdown_timer plugin. and contollred the button become active/inactive using a bool that changes its value with when the timer finishes.

Flutter Navigator Page Change within async button press

I'm experimenting with flutter and now I reached a point, where I don't understand, why I get an exception. I already tried a few things.
What am I doing?
I'm pressing a button - this button executes an async function within unpressed handler. Inside, depending on the response, I want to change the page.
TextButton(
child: Text(
_title,
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.w700,
fontSize: 18,
),
),
onPressed: () async {
var response = await authBloc.onLogin(_akey, _password);
if (response.isValid) {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => Container(),
));
}
},
),
For me it looks fine. I even tried to do this within a scheduled binder (SchedulerBinding.instance.addPostFrameCallback((_)), but I'm getting the same exception.
Exception:
Unhandled Exception: Looking up a deactivated widget's ancestor is unsafe.
At this point the state of the widget's element tree is no longer stable.
To safely refer to a widget's ancestor in its dispose() method, save a reference to the ancestor by calling dependOnInheritedWidgetOfExactType() in the widget's didChangeDependencies() method.
Anyone can help?
Thanks in advance!

Flutter Button statemanagement using provider

In my flutter application I have a button for follow and unfollow a user.
buttonstate.isSelected && widget.user!.isFollowing == true
? ElevatedButton(
onPressed: () async {
await unfollowuser(widget.user!.id); //Api Call
},
child: Text(
'Following',
style: TextStyle(
color: Theme.of(context).disabledColor,
),
),
)
: ElevevatedButton(
onPressed: () async {
buttonstate.setButtonState(true);
await followuser(widget.user!.id); //Api Call
},
child: Text(
'Follow',
style: TextStyle(
color: Theme.of(context).accentColor,
),
),
),
My objective is whenever the button is pressed I want the Api call to happen as well as the state of the button should change from 'Follow' to 'Following'. Now the Api gets called, but the button wont change to 'following'. After the page is refreshed, button changes to 'following'. How can i manage state of the button using provider??
Ive created a provider for this purpose as follows
import 'package:flutter/material.dart';
class ButtonProvider with ChangeNotifier {
bool isSelected = false;
void setButtonState(bool value) {
isSelected = value;
notifyListeners();
}
bool? get buttondata {
return isSelected;
}
}

The switch button turns back to on itself when goes to some other tab

I have switch button and when I go to some other tab and come again it turns on itself. I want it to stay On or Off depending on what last selected. Though the value are getting saved in data base correctly but when go to some other tab it is back to On. This is code below.
bool _hideProfile = false;
ListTile(
title: Card(
color: themeProvider.isDarkMode? black :white,
child: Padding(
padding: EdgeInsets.only(left:20,right: 20),
child: SwitchListTile(
title: _hideProfile? Text("Profile hidden",style: TextStyle(fontSize: 18),)
:Text(" Profile visible",style: TextStyle(fontSize: 18)),
secondary: _hideProfile?
Icon(Icons.visibility_off_outlined,color: lRed ):
Icon(Icons.visibility_outlined,color: Colors.green),
activeColor: Colors.blueGrey,
inactiveThumbColor: mRed,
inactiveTrackColor: dRed,
value: _hideProfile,
onChanged:(value){
setState(() {
_hideProfile = value;
});
String userStatus = 'active';
if (value) {
userStatus = 'hidden';
}
CreateAccountData(userStatus: userStatus ,);
_reference.doc(auth.currentUser.uid).update(
{
"userStatus" : userStatus,
}).then((_){
print('Profile hidden: $value');
});
}
),
),
),
),
You are using setState() for the state management so when you will go to some other page then the variable will loose it's state means it will get disposed and when you will come back it will start what ever you had set as default.
Now to over come this use state management package like provider, mobx, bloc etc.

Make a variable in build build context run once

I have a stateful widget which uses a provider to get questions. The question type looks like this:
{
"question": "What...",
"answer: 1829,
"buffer": [1928, 1874, 1825]
}
I have a shuffle method which shuffles the items passed to it. So in my widget, I have this code:
#override
Widget build(BuildContext context) {
var state = context.watch<Services>();
Tion tion;
List<int> shuffled;
int selectedNumber;
if (state.questions != null) {
tion = state.questions[0];
shuffled = shuffle([tion.answer, ...tion.buffer]); // here's my issue
}
return ...
}
Deeper in the widget tree, I render these numbers:
GridView.count(
crossAxisCount: 2,
children: List.generate(4, (index) =>
Center(
child: GestureDetector(
onTap: () => setState(() {
selectedNumber = shuffled[index]; // setstate
}),
child: Container(
width: 100,
height: 100,
decoration: BoxDecoration(
color: selectedNumber == shuffled[index] ? Color(0xff6C63FF) : Colors.grey[200],
borderRadius: BorderRadius.all(
Radius.circular(10)
)
),
child: Center(
child: Text(
'${shuffled[index]}',
style: GoogleFonts.lato(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.grey[800]
)
)
),
),
),
)
),
)
The problem is when I call setState(), the widget rebuilds, and the order of the numbers along with it. Is there any way to prevent this? I tries with initState but it's called outside the scope of context.
If you need a BuildContext for your function you can use didChangeDependencies(): It is called when a dependency of this State object changes and also immediately after initState, it is safe to use BuildContext here. Subclasses rarely override this method because the framework always calls build after a dependency changes. Some subclasses do override this method because they need to do some expensive work (e.g., network fetches) when their dependencies change, and that work would be too expensive to do for every build.
#override
void didChangeDependencies() {
// Your function.
super.didChangeDependencies();
}
Getx package also has variety of ways to insert a Middleware function. You can check them on package page.