Flutter Socket IO can't get data after tab is changed - sockets

So, When I open the tab for the first time, data from Socket.IO appears. But when I change the tab and go back, I can't get data from Socket.IO.
This is my Code:
Map <String,dynamic> list;
IO.Socket socket = IO.io('http://localhost:3000', <String, dynamic>{
'transports': ['websocket']
});
#override
initState(){
socket.on('connect',(_){
socket.on('stockQuote',(jsonData){
setState(() {
list = jsonData;
isLoading = false;
});
});
});
super.initState();
}
dispose(){
super.dispose();
}
I get these errors:
Unhandled Exception: setState() called after dispose(): _StocksState#0faab(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()

So apparently I found a solution to make the Socket.io load again.
Fix Code:
IO.Socket socket = IO.io('http://localhost:3000', <String, dynamic>{
'transports': ['websocket'],
'forceNew':true
});
So apparently I need to add 'forceNew': true so that the page loads the socket.io like new.
Source: How to close a socket.io connection

Related

Getx setState() or markNeedsBuild() called during build

im using Getx for my state management so the case is i have a screen with detail of the video and a list tile for another suggested video. in the screen i call initstate to call Getx function to fetch the video detail data like this
the point is what is the proper way to call getx function on screen build is it only with initState? if i use onInit in the controller it only initialize upon the controller build not the screen build
// video detail screen
#override
void initState() {
super.initState();
videoC.getVideoDetail(videoId: widget.data);
}
// get video detail
var videoDetailLoading = false.obs;
var videoDetail = {}.obs;
getVideoDetail({videoId}) {
videoDetail.clear();
videoDetailLoading.value = true;
String endpoint = 'video/$videoId';
http.get(endpoint).then(
(res) {
log('$endpoint item === $res');
if (res['status']) {
videoDetail.value = res['data'];
videoDetail.refresh();
} else {}
videoDetailLoading.value = false;
},
).catchError((onError) {
videoDetailLoading.value = false;
// Get.back();
// Helper().snakbar(isSuccess: false, message: 'Layar tidak tersedia');
log('$endpoint onError === $onError');
});
}
but after i navigate to the detail screen by tapping the suggested video
(basically it just navigate to the same screen but different data) then it throws this error
This Obx 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: Obx
has builder
state: _ObxState#45cea
The widget which was currently being built when the offending call was made was: Builder
The relevant error-causing widget was
GetMaterialApp
lib/main.dart:16
When the exception was thrown, this was the stack
what is the proper way to call getx function on screen build is it only with initState? if i use onInit in the controller it only initialize upon the controller build not the screen build
Your screen is refreshing before it's not loaded fully so getting this error, Please try with below code.
video detail screen
#override
void initState() {
super.initState();
WidgetBinding.instance.addPostFrameCallBack((timeStamp){
videoC.getVideoDetail(videoId: widget.data);
});
}

Prevent memory leak in Flutter

I was trying to make my function works with a Timer to fetch a list of actions from Database and display them without leaving the screen and re-enter again.
I tried the following :
void initState() {
super.initState();
Timer.periodic(timeDelay, (Timer t) => fetchFournisseurs());
}
It's working now , but my debug console is showing some infos :
State.setState.<anonymous closure> (package:flutter/src/widgets/fr<…>
[VERBOSE-2:ui_dart_state.cc(209)] Unhandled Exception: setState() called after dispose(): _listeDocumentState#c0f9f(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().
Is there a better way to make it and prevent memory leak or anything bad ?
Thank you.
All of the information is in the error message itself.
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).
For example if you navigate to a page containing this widget, and then close that page, the widget would no longer be in the widget tree, but the Timer you created is still running periodically.
This error can occur when code calls setState() from a timer or an animation callback.
Presumably fetchFournisseurs is calling setState.
The preferred solution is to cancel the timer or stop listening to the animation in the dispose() callback.
In other words your state class should do something along these lines.
class _ExampleState extends State<Example> {
final Duration timeDelay = ...;
// Create a variable to hold the timer.
Timer? _timer;
#override
void initState() {
super.initState();
// Assign timer to the variable.
_timer = Timer.periodic(timeDelay, (Timer t) => fetchFournisseurs());
}
#override
void dispose() {
// Cancel the timer in the dispose() callback.
_timer?.cancel();
super.dispose();
}
void fetchFournisseurs() {
// presumably fetchFournisseurs calls setState at some point.
setState(() {
...
});
}
#override
Widget build(BuildContext context) {
return ...;
}
}
Another solution is to check the "mounted" property of this object before calling setState() to ensure the object is still in the tree.
Which means to do the following.
void fetchFournisseurs() {
// check if the widget is mounted before proceeding
if (!mounted) return;
setState(() {
...
});
}
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().
Another reminder to cancel the Timer in dispose to avoid memory leaks.

How to dispose of an unmounted Widget in Flutter?

I am writing a flutter app and if you already started it once, I dont want the user to see the Intro Screen again
In my MaterialApp:
home: firstStart ? DeviceSelection() : StartScreen()
firstStart is a boolean, if it is the first start, it should start the App with the StartScreen() and if its not, it should go straight to DeviceSelection(). That all works fine, but my problem is the following error:
Error: This widget has been unmounted, so the State no longer has a context (and should be considered defunct).
Consider canceling any active work during "dispose" or using the "mounted" getter to determine if the State is still active.
I think it starts StartScreen too, even if firstStart is false, because it has to get its value out of the shared preferences, because I see it pop up shortly sometimes.
I already tried some stuff I found, like writing a dispose method:
#override
void dispose() {
super.dispose();
}
or
if (!mounted) {
Navigator.pop(context);
}
in a method that I call after the screen starts, but it doesnt work either. Any ideas what I could do to get rid of this error? Thanks
mb set a flag, like:
bool lookedIntro = false; // if user look it first
and then use:
#override
void initState() {
// take data flag from database or somewhere else (file)
// and check it
}
and also read about initState() more

Flutter: Consider canceling any active work during "dispose" when internet changes its state

I am getting the following message when internet goes off.
E/flutter (26162): [ERROR:flutter/lib/ui/ui_dart_state.cc(186)] Unhandled Exception: This widget has been unmounted, so the State no longer has a context (and should be considered defunct).
E/flutter (26162): Consider canceling any active work during "dispose" or using the "mounted" getter to determine if the State is still active.
It is showing the message from this section of my code.
#override
void initState() {
super.initState();
try {
InternetAddress.lookup('google.com').then((result) {
if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
// internet conn available
Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (context) =>
(Constants.prefsMobile.getString("mobile") == null
? Login()
// : SignupPayoutPassword(signupdata: [])),
: Home(signindata: signinData)),
));
} else {
// no conn
_showdialog();
}
}).catchError((error) {
// no conn
_showdialog();
});
} on SocketException catch (_) {
// no internet
_showdialog();
}
Connectivity()
.onConnectivityChanged
.listen((ConnectivityResult connresult) {
if (connresult == ConnectivityResult.none) {
} else if (previous == ConnectivityResult.none) {
// internet conn
Navigator.of(context).pop();
Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (context) =>
(Constants.prefsMobile.getString("mobile") == null
? Login()
: Home(signindata: signinData)),
));
}
previous = connresult;
});
}
I have not used any dispose method for this. If any one know please let me know how can I solve this problem. How to dispose. I am getting a crash report after my app close as follows
E/AndroidRuntime( 8064): java.lang.RuntimeException: Unable to destroy activity {com.example.aa_store/com.example.aa_store.MainActivity}: java.lang.IllegalArgumentException: Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkParameterIsNotNull, parameter activity
is this crash message for the above problem? Please help.
Please use.
#override
void dispose() {
Connectivity().onConnectivityChanged.cancel();
super.dispose();
}
Better, define your stream outside the initState:
Stream _connectivityStream = Connectivity().onConnectivityChanged;
and in dispose use _connectivityStream.cancel();.
The error means that you instantiated a stream, which on changes of events, triggers build changes. This stream is setup during initState, meaning when the widget is first created. Connectivity().onConnectivityChanged.listen(....etc).
But you never tell flutter to cancel listening to this stream when the widget is disposed.
This is the role of the dispose method. Similar to how you want logic to be performed when the widget is built, you use initState, you should also tell it when you are no longer interested in these changes in logic.
Failing to do so, will result in the error you are having, aside from memory leaks also.
This is the translation of the error This widget has been unmounted, so the State no longer has a context (and should be considered defunct). which you posted. "Hey, this widget isn't in the tree anymore, its state is not mounted, I can't rebuild it, and you need to pay attention to it.
Please consider using the dispose method for these Flutter elements, not to mention all of them, but from the top of my mind:
AnimationControllers.
Timers.
Streams listeners.

How to perform setState() while listening to a Stream

I hope you are doing well. I was looking for a way where I can perform setState within a Stream. Basically what I am doing here is, I am getting the data from the collection, assigning it to a List variable called currentDocuments and I am displaying it in a ListView.builder(). I did not use StreamBuilder because, the documents can be deleted, but have to be deleted locally and not from firestore.
The problem is when I add a new document to 'mycollection' collection, I get this message:
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().
I have tried a lot of ways using 'mounted' field, but still, I get the message mentioned above. Any help regarding this issue would be greatly appreciated. Thank you in advance.
Code:
_subscription = Firestore.instance.collection('mycollection').snapshots().listen((QuerySnapshot querySnapshot) {
for(var i in querySnapshot.documentChanges){
if(i.type == DocumentChangeType.added){
if(!mounted){
setState((){});
}
setState(() {
currentDocuments = querySnapshot.documents;
});
}
}
});
_subscription = Firestore.instance.collection('mycollection').snapshots().listen((QuerySnapshot querySnapshot) {
for(var i in querySnapshot.documentChanges){
if(i.type == DocumentChangeType.added){
if(mounted){
setState(() {
currentDocuments = querySnapshot.documents;
});
}
}
}
});
here mounted will be true if the widget exists in the tree, but in your code there are two setState calls which will fire either the mounted is true or not!!, use/call setState only when mounted is true,