Flutter initState not being called - flutter

When I open a chat page as a pop up the initState gets called the first time, but when I use Navigator.pop(context) and then open the chat page again the initState does not get called and I get an error for my StreamSubscription -> The method 'cancel' was called on null. But I do initialize it in the initState.
Why isn't the initState being called THE SECOND time I open the chat page, WHEN THE FIRST TIME I OPEN IT IT WORKS PERFECTLY?
// ignore: cancel_subscriptions
StreamSubscription _streamSubscription;
#override
void initState() {
if (this.chat != null) if (widget.chat.messages.isEmpty)
this._prevMessages().then((value) {
this._initMessages(); // <-- WHERE I'M INITIALIZING THE StreamBuilder
this._scrollToBtm();
});
super.initState();
}
#override
void dispose() {
this._streamSubscription.cancel(); // <-- THE ERROR
this._scrollController.dispose();
this._msgTEC.dispose();
this._msgFN.dispose();
super.dispose();
}
_initMessages() {
Funcs.log('Init Live Messages...');
this._streamSubscription = APIs().chats.messagesLive(...);
}
The exact log:
════════ Exception caught by widgets library ═══════════════════════════════════════════════════════
The method 'cancel' was called on null.
Receiver: null
Tried calling: cancel()

Make sure that you initialize _streamSubscription before invoking it asynchronosuly. There must be some default value set. What's happening is that where you are calling _initMessages(), it is being invoked inside a Future,which means it is invoked after (or not, it could be at anytime) initialization completes.
You can either make sure to await this._prevMessages(), thus:
#override
void initState() async {
if (this.chat != null) if (widget.chat.messages.isEmpty)
await this._prevMessages().then((value) {
this._initMessages(); // <-- WHERE I'M INITIALIZING THE StreamBuilder
this._scrollToBtm();
});
super.initState();
}
Or you can initialize the _streamSubscription object before running the initialization code. There must be some default value set here.
Also the dispose method should not be invoked before initialization. This means that you have code somewhere that is disposing the object the moment that the item gets initialized.

Related

Unhandled Exception: setState() called after dispose() with Firebase Realtime Database chat feature

I am receiving this error:
[VERBOSE-2:dart_vm_initializer.cc(41)] Unhandled Exception: setState() called after dispose(): _EventChatScreenState#7c8b5(lifecycle state: defunct, not mounted)
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.
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.
This error might indicate a memory leak if setState() is being called because another object is retaining a reference to this State object after it has been removed from the tree. To avoid memory leaks, consider breaking the reference to this object during dispose().
#0 State.setState.<anonymous closure> (package:flutter/src/wid<…>
However, the only place I am calling setState is in the initState:
#override
void initState() {
super.initState();
dbRef = dbInstance.ref("/events/");
var query = dbRef!.child(widget.event.event.eventId);
FirebaseList(
query: query,
onChildAdded: (index, snapshot) {
Map<dynamic, dynamic> childMap = snapshot.value as dynamic;
ChatMessage newChatMessage = ChatMessage(
chatMessageId: snapshot.key.toString(),
userId: childMap["userId"],
displayName: childMap["displayName"],
message: childMap["message"],
datetime: childMap["datetime"],
);
setState(() {
chatMessages.add(newChatMessage);
});
},
);
_messageFieldController = TextEditingController();
}
#override
void dispose() {
super.dispose();
_messageFieldController.dispose();
}
I'm not really sure why this is happening, but I included the dispose method since it the error references it.
Worth noting that I am doing this to make the screen scroll to the bottom of the chat messages which are display using a ListView.builder
void scrollToBottom() {
SchedulerBinding.instance.addPostFrameCallback((_) {
_scrollController.jumpTo(_scrollController.position.maxScrollExtent);
});
}
#override
Widget build(BuildContext context) {
final user = ref.watch(userProvider);
if (chatMessages.isNotEmpty) {
scrollToBottom();
}
If I remove the above code the issue seems to go away
instead of this
#override
void dispose() {
super.dispose();
_messageFieldController.dispose();
}
try this
#override
void dispose() {
_messageFieldController.dispose();
super.dispose();
}

Method 'dispose' was called on null. Receiver: null Tried calling: dispose()) - Push.Navigator

I added in last page of my app a button to move back to the previous page - expecting to refresh the complete app or page.
context,
MaterialPageRoute(builder: (context) => SplashPage()), // this mymainpage is your page to refresh
(Route<dynamic> route) => false,
);
I receive the below error message when pressed.
═══════ Exception caught by widgets library ═══════════════════════════════════
The method 'dispose' was called on null.
Receiver: null
Tried calling: dispose()
════════════════════════════════════════════════════════════════════════════════
What do I need to place in the Dispose widget to avoid calling null.
DateFormat format = DateFormat('dd/MM');
List<bool> flips = [false, false, false, false];
List tarots = [];
List unLockCard = [];
BannerAd _bannerAd;
#override
void dispose() {
_bannerAd.dispose();
super.dispose();
}
I looked into this solution Dispose widget when navigating to new route but can not apply.
There may be a situation that the banner has not loaded so tying to dispose it when its null will throw an error. You can add a condition check before you dispose
#override
void dispose() {
if(_bannerAd != null)
{
_bannerAd.dispose();
}
super.dispose();
}
if you are using null safety you can declare banner ad like
BannerAd? _bannerAd;
and dispose it like
_bannerAd?.dispose();

Exception has occurred. Where is the actual error for this problem in Flutter?

when I run the apps, it still fine when i move to screen B from screen A. after i move to screen C and go back to screen A, this error happen:
Exception has occurred.
FlutterError (setState() called after dispose(): _AdminViewListState#e9f3a(lifecycle state: defunct, not mounted)
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.
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.
This error might indicate a memory leak if setState() is being called because another object is retaining a reference to this State object after it has been removed from the tree. To avoid memory leaks, consider breaking the reference to this object during dispose().)
can somebody help me to figure out what is the problem?
The error pointed at this code:
setState(() {
loading = false;
});
and this is the whole coding:
class AdminViewList extends StatefulWidget {
#override
_AdminViewListState createState() => _AdminViewListState();
}
class _AdminViewListState extends State<AdminViewList> {
var loading = false;
final list = new List<AttractionModel>();
final GlobalKey<RefreshIndicatorState> _refresh = GlobalKey<RefreshIndicatorState>();
Future<void> _viewData() async{
list.clear();
setState(() {
loading = true;
});
final response = await http.get(BaseUrl.viewAttraction);
if (response.contentLength == 2) {
} else {
final data = jsonDecode(response.body);
data.forEach((api){
final ab = new AttractionModel(
api['id'],
api['attractionName'],
api['description'],
api['price'],
api['createdData'],
api['idUsers'],
api['name'],
);
list.add(ab);
});
setState(() {
loading = false;
});
}
}
in my debug console :
I/BufferQueueProducer(25860): [SurfaceTexture-0-25860-1](this:0x7b4bcac000,id:1,api:1,p:386,c:-1) disconnect(P): api 1
It's run well after I add this code
if (!mounted) return;
before
setState(() {
loading = false;
});

Flutter: setState() or markNeedsBuild() called during build with Provider

I'm calling one of the ChangeNotifier functions from an initState() method and the notifyListener() call inside the function is throwing
setState() or markNeedsBuild() called during build. exception
void initState(){
Provider.of<MessengerRepository>(context, listen: false).setUnreadCount(0);
super.initState();
}
class MessengerRepository with ChangeNotifier {
int unreadCount;
void setUnreadCount(int value){
unreadCount = value;
notifyListeners();
}
I need to call the notifyListener() inside setUnreadCount() as I'm calling the setUnreadCount() function in multiple places during the execution. So can't remove the notifyListener() only inside initState().
Using provider version 4.0.4
The cause for this issue as mentioned in the log message is that the function inside initState() is requesting a rebuild through notifyListener() (the same will happen if we use setState() as well) even before the first build is completed.
The solution is to add a addPostFrameCallback and execute the rebuilding function inside it so that it will execute only after the first building of the widget
void initState() {
super.initState();
WidgetsBinding
.instance
.addPostFrameCallback((_){
Provider.of<MessengerRepository>(context, listen: false).setUnreadCount(0);
}
);
}
You can move Provider.of<MessengerRepository>(context, listen: false).setUnreadCount(0); from intStateto didChangeDependecies then it'll be called only once page finishes build process.
Or by this way :(less elegant)
void setUnreadCount(int value, {bool shouldUpdate: true}){
unreadCount = value;
if(shouldUpdate) notifyListeners();
Then
void initState(){
Provider.of<MessengerRepository>(context, listen: false).setUnreadCount(0, shouldUpdate:false);
super.initState();
}
When you call it in initState, if the build method finishes before all your variables are (re)assigned they'll not be updated until you call setState or notifyListeners
Learn more here

Flutter has event before entering the router?

I want to determine if the user is logged in before entering the route, so I want to know if there has event or interceptor before entering every routers.
in StatefulWidget's you can check in the initState method this
initState : Called when this object is inserted into the tree.
The framework will call this method exactly once for each [State] object it creates.
if you'r using FirebaseAuth you can check in initState like this:
#override
void initState() {
FirebaseAuth.instance.currentUser().then((user){
if (user == null) {
Navigator.of(context).pop();
// you can navigate back if there is a no user
}
});
super.initState();
}