Getx setState() or markNeedsBuild() called during build - flutter

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);
});
}

Related

Open dialog box in Flutter Stateless widget using ChangeNotifierProvider

I have a Stateless flutter widget which shows a list of TODOs. The list is shown using a ChangeNotifierProvider (of TodoList object, which contains a list of todos). Now when I load the page, I want to show a dialog box asking user to enter a new TODO if and only if the existing todos is empty. Inside the builder of the ChangeNotifierProvider, i tried below logic
if (todoList.todos.length == 0) {
_showDialog(context);
return Column...;
} else {
return ListBuilder...;
}
But its showing 2 dialog box (probably due to the build method executing twice). I have to pass context to dialog box because I'm updating the todoList inside it, which should trigger a rebuild.
How do I handle this scenario. I've tried using flag (_isDialogOpen) but its not still working?
make the widget Stateful in order to use it's lifecycle methods, you can use then initState() for showing the dialog when the page widgets are inserted in the widget tree, but you will need to use an addPostFrameCallback() to schedule showing it 1 frame after the initState's code gets executed:
First, import:
import package:flutter/scheduler.dart.
Then use this:
#override
void initState() {
// ...
SchedulerBinding.addPostFrameCallback((_) => _showDialog(context),);
}

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.

Flutter: CupertinoTabScaffold tab content does not refresh after closing PageRoute

Problem: CupertinoTabScaffold tab content does not refresh after closing new screen from CupertinoPageRoute
App:
There is a list of data in a Listview.
I open one data set and remove the favourite status (checked=false).
I close the data set and the listview should refresh (FutureBuilder to an SQL database).
Details:
I've got a Flutter App with two layouts (Material and Cupertino).
In each layout i have four tabs and the content widgets are the same.
On the Cupertino App every tab consists of a CupertinoPageScaffold.
When i press on an element it navigates to a new screen with CupertinoPageRoute.
After closing the CupertinoPageRoute the tab content is not refreshing.
The tab content is refreshing when i switch between the four tabs.
If one CupertinoPageRoute is open and i open a new one from here and close it again
then the content of the opened CupertinoPageScaffold is refreshing.
The data is refreshing without a callback etc., just by calling the future (i think).
Means there should be a problem with the CupertinoTabScaffold.
There is no problem on the Material App.
The tab contents of the Material App are refreshing when closing a MaterialPageRoute.
Question:
Is there a problem at this position?
Do you have the same issue or maybe a solution or workaround?
Code:
My code is not really representative so i will create a new clean project for you.
please confirm if code is necessary.
What i tried:
I tried a lot of solutions from the internet like callbacks, setState, .then, .whencomplete for the route.
nothing seems to work.
Thank you in advance.
EDIT 03.10.2021: the tabcontroller with SingleTickerProviderStateMixin reloads the open tab when i open or close an new screen. if i just use DefaultTabController Widget than it doesn't. So i have to edit the CupertinoTabController.
class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
late TabController _tabController;
final CupertinoTabController _cuptabController = CupertinoTabController();
Future<void> _refreshdata() async {
downloadJSON1();
setState(() {
});
}
callback() {
_refreshdata();
}
#override
void initState() {
_refreshdata();
super.initState();
_tabController = TabController(vsync: this, length: 2, initialIndex: 0);
_cuptabController.index = widget.starttab;
}
#override
void dispose() {
_tabController.dispose();
super.dispose();
}
...
}
I can suggest a few options:
Option 1:
change your FutureBuilder into a StreamBuilder.
FutureBuilder does not refresh automatically when you save the data which StreamBuilder does very well.
Option 2:
Use a setState but not on the context of the route you are closing, but rather on the context of the original page you want to refreh
setState(() { });
for the moment i solved it with a .then-callback from PageRoute. i just passed the callback to the deepest Widget/PageRoute. But if you have a solution/idea for the tabcontroller would be great.
Souce:
how to refresh state on Navigator.Pop or Push in flutter
Navigator.push(context,
CupertinoPageRoute(
builder: (BuildContext context) => MyWidget(
'myparameters'),),).then((value) => setState(() {callback();}));

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

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

Flutter: display dynamic list

I've got a widget that displays a classes' list, but that list changes (the content changes) over time by other elements and interactions of the program. How can the DisplayListWidget detect this? Do the elements that change this list have to communicate with the displaylistwidget?
EDIT: I'm familiair with stateful widgets and setState () {}. It's just that the data changes in the background (e.g. by a timer) so there's no reference from bussiness logic classes to widgets to even call setState.
If you would like to notify (rebuild) widgets when data changes, check out provider package.
There are some other options:
BLoC (Business Logic Component) design pattern.
mobx
Redux
Good luck
Try wrapping the display widget in a state widget, and then whenever you update the list, call setState(() { //update list here })
ex.
// this is the widget that you'd nest directly into the rest of your tree
class FooList extends StatefulWidget {
FooListState createState() => FooListState();
}
// this is the state you will want to call setState on
class FooListState extends State<FooList> {
Widget build(BuildContext context) {
return DisplayListWidget [...]
}
}
I would recommend following this flutter tutorial where they dynamically update a list
And to learn more about StatefulWidgets and States check this out: https://api.flutter.dev/flutter/widgets/StatefulWidget-class.html