How to trigger setState() of any other Stateful Class? - flutter

I have two stateful states, ABCState and XYZState, I want to call setState() on ABCState from XYZState so that I can trigger a redraw of ABCState
class ABCState extends State<ABC> {
}
&
Class XYZState extends State<XYZ> {
// Call .setState() of ABCState
}
I tried to play around with keys, but somehow not able to do so. Appreciate your help

This is a bad idea.
But if you want to do it, you can :
Use GlobalKey, which allows to get state/context/widget
Use BuildContext of the children of your desired stateful widget. Using context.ancestorStateOfType(const TypeMatcher<MyStatefulWidget>());

Related

Avoid no_logic_in_create_state warning when saving reference to State of StatefulWidget in Flutter

Using Flutter, I display a list of elements in an app.
I have a StatefulWidget (ObjectList) that holds a list of items in its State (ObjectListState).
The state has a method (_populateList) to update the list of items.
I want the list to be updated when a method (updateList) is called on the widget.
To achieve this, I save a reference (_state) to the state in the widget. The value is set in createState. Then the method on the state can be called from the widget itself.
class ObjectList extends StatefulWidget {
const ObjectList({super.key});
static late ObjectListState _state;
#override
State<ObjectList> createState() {
_state = ObjectListState();
return _state;
}
void updateList() {
_state._populateList();
}
}
class ObjectListState extends State<ObjectList> {
List<Object>? objects;
void _populateList() {
setState(() {
// objects = API().getObjects();
});
}
// ... return ListView in build
}
The problem is that this raises a no_logic_in_create_state warning. Since I'm not "passing data to State objects" I assume this is fine, but I would still like to avoid the warning.
Is there a way to do any of these?
Saving the reference to the state without violating no_logic_in_create_state.
Accessing the state from the widget, without saving the reference.
Calling the method of the state from the outside without going through the widget.
It make no sense to put the updateList() method in the widget. You will not be able to call it anyway. Widgets are part of a widget tree, and you do not store or use a reference to them (unlike in other frameworks, such as Qt).
To update information in a widget, use a StreamBuilder widget and create the widget to be updated in the build function, passing the updated list to as a parameter to the widget.
Then, you store the list inside the widget. Possibly this may then be implemented as a stateless widget.

Flutter GetX: Where Does Get.put Go in a Widget?

I am new to GetX and am trying to learn how to use it. I have read different tutorials that inject the controller outside of the widget's build method, and others that put it inside it.
class MyWidget extends StatelessWidget{
const MyWidget({Key? key}) : super(key:key);
//Outside...
final controller = Get.put(Controller()); //<---
#override
Widget build(BuildContext context) {
//Inside...
final controller = Get.put(Controller()); //<---
return Obx(
() => Text(controller.name)
);
}
}
Is there a difference between those two locations? If so, why?
Also, where should it go in a StatefulWidget? It seems it should not go inside the build method because it causes a stack overflow error for me.
Does the location of Get.put() matter inside a widget?
The normal way is to put the controller outside the widget, so it will be created once. if you but it inside the widget a new instance of the controller will be created each time you refresh (update) the widget.
Also, with GetX there is no need to use StatefulWidget.
When you put it inside the build method, it will create a new instance every time the widget is rebuilt, so it is better to but it outside and in most cases I think you do not need to use StatefulWidgetwith GetX even in animation.
The answers shared are correct. Thank you! I wanted to post another answer that I consider to be an even better way to do it as referenced in the documentation.
This method allows for controller access without ever putting Get.put in any of your widgets. This is also really helpful if you have a lot of controllers and need to reference them in widgets as well as from other controllers.
Here is an example:
//ControllerA
class ControllerA extends GetxController {
static ControllerA get to => Get.find();
final name = 'Bob'.obs;
someMethod(){
ControllerB.to.anotherMethod(); //'I am inside ControllerB!'
}
}
//ControllerB
class ControllerB extends GetxController {
//+++
static ControllerB get to => Get.find();
anotherMethod(){
print('I am inside ControllerB!');
}
}
And then inside Widgets:
class MyWidget extends StatelessWidget{
const MyWidget({Key? key}) : super(key:key);
#override
Widget build(BuildContext context) {
return Obx(
() => Text(ControllerA.to.name) //Bob
);
}
}
This requires that you add your Get.put declarations in main.dart so that you make sure they are all ready:
void main() {
Get.put(ControllerA());
Get.put(ControllerB());
runApp(
GetMaterialApp(...)
);
}
In my opinion, it's really clean like this--and very convenient!
If we want to keep a GetX controller in memory forever, then we should indeed instantiate it outside of a Widget's build() function (such as within main() as Clifton shows).
(Note that we can also use GetXService for persistent controllers, which allows manual disposal.)
Placing controllers inside build() functions, is the correct place when we want GetX to free memory & dispose the controller when the widget goes "out of scope". (e.g. when the user has "popped" the route from the stack.) There's also no danger in "recreating" the controller when its instantiated inside build(): Get checks for existence of a controller when Get.put() is called and skips it if the controller is already instantiated.
See this answer (and link to an explanation by one of Get's maintainers) for more info on why we should Get.put() inside build().

How do I call a function within my main page from another file?

I have the following problem:
My Stateful Widget "HomePage" has an Function:
void refresh() {
//not important for now
}
But, my whole save and load functions are located in an extra file (save_load.dart)
It's not a class or whatever, just pure functions that I can call from every other file.
void loadList(String key) {
//not important as well
//The following line is what I basically want to achieve
HomePage.refresh();
}
My Problem now is: I want to call the function refresh() after/within the loadList() function. Do you have an idea on how I can achieve this?
Thank you so much!
Summary
The easiest way to achieve what you're looking to do is to use a GlobalKey<HomePageState> to reference the currentState of your HomePage widget and then call the refresh method.
Solution
First: Create your Global Key
Create your global key and pass it into your HomePage when you create it.
final GlobalKey<HomePageState> homeKey = GlobalKey<HomePageState>();
...
// In one of your build methods where you're creating your `HomePage` widget.
HomePage(key: homeKey, ...)
Second: Get The Current State
Next, you'll need to get access to the current state of your HomePage widget.
final homePageState = homeKey.currentState;
Last: Call Refresh
Finally, you'll want to call the refresh method using the state class you retrieved from the GlobalKey.
// This is how you access it safely
homePageState?.refresh(); // will only call refresh if the homePageState is not null
// OR
homePageState!.refresh(); // will throw if homePageState is null

Can I use RefreshIndicator in Stateless flutter widget?

I'm fetching some json data from the internet and displaying in a listview builder inside a stateless widget
Now I want to refresh the data using RefreshIndicator.
Do I need a stateful widget for this or can I manage to do this with a stateless widget?
My understanding:
If I don't need to refresh, I load the data from internet only on the app start up, So Stateless widget is enough
If I need to refresh, it involves user input, but I don't need to manage a state, I just need to re-run the build method (Which I plan to do by Navigating to the same page, in the onRefresh parameter of RefreshIndicator)
So do I need a stateful widget?
You must change the StatelessWidget to a StatefullWidget and use the function setState (){} to
If you need a RefreshIndicator to get again the jsondata from the internet you must need a StateFullWidget because u need to rebuild the page with the newdata.
There is no point in showing the indicator if you are not going to ask for new information.
Because you want to reduce the StatefulWidget instances, you can delegate the task of fetching data to a parent stateful widget.
You could pass a callback to perform the task of fetching data via the constructor of the StatelessWidget invoked whenever we want to refresh the data.
class _ParentWidgetState extends State<ParentWidget>{
...
Future<dynamic> _fetchData(){..}
...
Widget build(BuildContext){
return Container(
...
ChildWidget(onRefresh:_fetchData)
...
);
}
}
class ChildWidget extends StatelessWidget{
ChildWidget({Key? key,this.onRefresh}):super(key:key);
final Future<dynamic> Function() onRefresh;
...
#override
Widget build(BuildContext context){
return RefreshIndicator(
onRefresh:onRefresh,
child:..
);
...
}
}
The point is to delegate the task of loading the data to a parent StatefulWidget's state so you could use other mechanisms like ChangeNotifier to make the parent widget's state call the API and render after calling setState future in its own state.

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