How to force initState every time the page is rendered in flutter? - flutter

I am adding some data into the SharedPreferenceson page2 of my app and I am trying to retrieve the data on the homepage. I have used an init function on page 1 as follows:
#override
void initState() {
super.initState();
_getrecent();
}
void _getrecent() async {
final prefs = await SharedPreferences.getInstance();
// prefs.clear();
String b = prefs.getString("recent").toString();
Map<String, dynamic> p = json.decode(b);
if (b.isNotEmpty) {
print("Shared pref:" + b);
setState(() {
c = Drug.fromJson(p);
});
cond = true;
} else {
print("none in shared prefs");
cond = false;
}
}
Since the initState() loads only once, I was wondering if there was a way to load it every time page1 is rendered. Or perhaps there is a better way to do this. I am new to flutter so I don't have a lot of idea in State Management.

you can override didChangeDependencies method. Called when a dependency of the [State] object changes as you use the setState,
#override
void didChangeDependencies() {
// your codes
}
Also, you should know that using setState updates the whole widget, which has an overhead. To avoid that you should const, declaring a widget const will only render once so it's efficient.

First thing is you can't force initState to rebuild your widget.
For that you have setState to rebuild your widget. As far as I can
understand you want to recall initState just to call _getrecent()
again.
Here's what you should ideally do :
A simple solution would be to use FutureBuilder. You just need to use _getrecent() in FutureBuilder as parent where you want to use the data you get from _getrecent(). This way everytime your Widget rebuilds it will call _getrecent().

You simply use setState() methode if you want to update widget. Here's documentation for this https://api.flutter.dev/flutter/widgets/State/setState.html

init function will render it only once initially, to avoid that -
You can use setState( ) method to re-render your whole widget.

Related

Flutter Difference between InitState and just putting inside Widget Build function

I had an error every time I restarted my App: This widget has been unmounted, so the State no longer has a context (and should be considered defunct). and saw that something was not correct with my initstate. The initState was:
#override
void initState() {
SchedulerBinding.instance.addPostFrameCallback((_) {
BlocProvider.of<TutSkippedCubit>(context).load();
});
super.initState();
}
the methods loads the data from sharedprefs if I have already skipped the tut or not. Now I solved this issue with removing the initState method and putting the function call inside the widget build:
#override
Widget build(BuildContext context) {
BlocProvider.of<TutSkippedCubit>(context).load();
....
The widget build gets called when the pages loads, so, isn't it the same as the initial state? For what exactly is the methode initState() and I have the feeling that my way of handling this problem is a bad practise, but what would be a better way, how do I solve it?
The initState() method is to control what happens after the app is built. The problem is that you call BlocProvider before the app begins. The correct way is to put all the actions after super.initState() call and add the context to the BlocProvider inside build method. Like this:
TutSkippedCubit? tutSkippedCubitProvider;
#override
void initState() {
super.initState();
SchedulerBinding.instance.addPostFrameCallback((_) {
tutSkippedCubitProvider!.load();
});
}
#override
Widget build(BuildContext context) {
tutSkippedCubitProvider = BlocProvider.of<TutSkippedCubit>(context);
...
}
The initState and build method is called when the widget is inserted into the widget tree, but the build method also is called every time the state is changed.
You do need to have in mind that every time the state is changed your method BlocProvider.of<TutSkippedCubit>(context).load(); also is called.
Maybe, the code below can help you:
WidgetsBinding.instance.endOfFrame.then(
(_) async {
if (mounted) {
BlocProvider.of<TutSkippedCubit>(context).load();
}
},
);
You wouldn't be surprise of getting that error since you are using BlocProvider.<T>(context) out of a BuildContext. This context in bracket is the just the same as the one given in the build function.
The initState() is a method that is called when an object for your
stateful widget is created and inserted inside the widget tree.

Need explanation for setState() function in Flutter

I am confused on why do we have to put a function in setState to update variables. I could instead update the variables and call setState. I modified the code from https://flutter.dev/docs/development/ui/widgets-intro
class _CounterState extends State<Counter> {
int _counter = 0;
void _increment() {
setState(() {
_counter++;
});
}
Instead, I thought of doing this
class _CounterState extends State<Counter> {
int _counter = 0;
void _increment() {
_counter++;
setState(() {
});
}
This still works, now I was thinking why make setState() have a function as a parameter, instead setState could not have any parameters like setState(); and we would just call it after we update our variables.
You'll find the answer in the official docs:
The provided callback is immediately called synchronously. [...] If you just change the state directly without calling setState, the framework might not schedule a build and the user interface for this subtree might not be updated to reflect the new state.
I think the problem is that UI and state object will not be in sync for a short period of time if you call setState((){}); after changing the state.
it can not run properly in the big project. So answer of your problem is like this, if you dont use setState(() => {});, your screen can not render if you change any value of variable. So if you want to effect when you change value of variable in the screen, you have to use setState(() => {});.

Does setState() trigger itself when variables and data change just like stream and streambuilder does?

class _LocationScreenState extends State<LocationScreen> {
WeatherModel weather = WeatherModel();
String weatherMessage;
String weatherIcon;
String cityName;
int temperature;
#override
void initState() {
super.initState();
updateUI(widget.locationWeather);
}
void updateUI(dynamic weatherData) {
setState(() {
temperature=weatherData['main']['temp'].toInt();
var condition = weatherData['weather'][0]['id'];
cityName=weatherData['name'];
weatherIcon=weather.getWeatherIcon(condition);
weatherMessage=weather.getMessage(temperature);
});
}
Hi I am getting confused with what setState() does. If the value of temperature or condition or anything changes inside that of setState does setState() trigger itself to update the UI and build UI with updated temp or condition or do I have to invoke updateUI function myself to invoke setState and update the UI?
SetState Rebuild the build method whenever it called.
SetState never call it self, you have to call it. If you change value of your variable and did not use SetState then it will not reflect in UI, to do so you have to call SetState.
As you put you code in updateUI in SetState, so it will update ui whenever you call that method, where as if you don't use SetState there then it will not reflect changes in UI.
documentation states that setState(fn()), Calling setState notifies the framework that the internal state of this object has changed in a way that might impact the user interface in this subtree, which causes the framework to schedule a build for this State object.
whenever a setState called the ui will be scheduled for a build based on the new state.
Eg:
class _MyWidgetState extends State<MyWidget> {
String text;
#override
void initState() {
super.initState();
text = "Hello";
}
void updateUI(String action) {
setState(() {
text = action;
});
}
#override
Widget build(BuildContext context) {
return Column(children: [
Text(text, style: Theme.of(context).textTheme.headline4),
MaterialButton(
onPressed: () {
updateUI("Pressed");
},
child: Text("Change state"))
]);
}
}
Note : initState() will be called once in the life cycle of Stateful widget.
setState():
Notify the framework that the internal state of this object has changed.
Whenever you change the internal state of a State object, make the change in a function that you pass to setState:
setState(() { _myState = newValue; });
Calling setState notifies the framework that the internal state of this object has changed in a way that might impact the user interface in this subtree, which causes the framework to schedule a build for this State object.
Nope, you need to call it in order to "re-draw" the current stateful widget. I believe the variable that changed doesn't need to be wrapped inside the setState either, as long as it's changed before the setState.

Flutter How to recreate stateful widget in build()

I'm developing android/ios app using flutter with provider(state management)
in my app, i have a Main scaffold with bottom navigation menu. (so, one scaffold with many views and controll it using bottom navigation, NOT Navigator.push())
i want to know that is it possible recall initstate() from build().
for example
... Statefulwidget
void initState() {
super.initState();
MYHttp.callAPI_only_once_for_some_reason();
}
Widget build(...) {
var flag = Provider.of<MyProvider>(context).flagdata; // flag is true when push notification has been arrived
if (flag) {
initstate() // apparently it should not work, but i have to recreate whole stateful widget to call initState()
}
}
No it is not possible. The initstate() is only called each time a new widget is painted.
Instead of recalling the initstate. Create a method, add it to use init state and call wherever you want to call it.
Check the code below for an example. It works perfectly:
// create the method.
void makeRequest() {
MYHttp.callAPI_only_once_for_some_reason();
}
void initState() {
//call the created method here
makeRequest();
super.initState();
}
Widget build(...) {
var flag = Provider.of<MyProvider>(context).flagdata; // flag is true when push notification has been arrived
if (flag) {
// call the method here again. if you need to use it.
makeRequest(); // apparently it should not work, but i have to recreate whole stateful widget to call initState()
}
}
I hope this helps.

Flutter provider in initState

I'm currently trying Provider as a state management solution, and I understand that it can't be used inside the initState function.
All examples that I've seen call a method inside a derived ChangeNotifier class upon user action (user clicks a button, for example), but what if I need to call a method when initialising my state?
Motivation:
Creating a screen which loads assets (async) and shows progress
An example for the ChangeNotifier class (can't call add from initState):
import 'package:flutter/foundation.dart';
class ProgressData extends ChangeNotifier {
double _progress = 0;
double get progress => _progress;
void add(double dProgress) {
_progress += dProgress;
notifyListeners();
}
}
You can call such methods from the constructor of your ChangeNotifier:
class MyNotifier with ChangeNotifier {
MyNotifier() {
someMethod();
}
void someMethod() {
// TODO: do something
}
}
Change your code to this
class ProgressData extends ChangeNotifier {
double _progress = 0;
double get progress => _progress;
void add(double dProgress) async {
// Loading Assets maybe async process with its network call, etc.
_progress += dProgress;
notifyListeners();
}
ProgressData() {
add();
}
}
In initState all the of(context) things don't work correctly, because the widget is not fully wired up with every thing in initState.
You can use this code:
Provider.of<ProgressData>(context, listen: false).add(progress)
Or this code:
Future.delayed(Duration.zero).then(_){
Provider.of<ProgressData>(context).add(progress)
}):
So an AssetLoader class which reports on its progress will look something like this, I guess:
import 'package:flutter/foundation.dart';
class ProgressData extends ChangeNotifier {
double _progress = 0;
ProgressData() {
_loadFake();
}
Future<void> _loadFake() async {
await _delayed(true, Duration(seconds: 1));
_add(1.0);
await _delayed(true, Duration(seconds: 2));
_add(2.0);
await _delayed(true, Duration(seconds: 3));
_add(3.0);
}
// progress
double get progress => _progress;
// add
void _add(double dProgress) {
_progress += dProgress;
notifyListeners();
}
// _delayed
Future<dynamic> _delayed(dynamic returnVal, Duration duration) {
return Future.delayed(duration, () => returnVal);
}
}
As Fateme said:
the widget is not fully wired up with everything in initState
Also, you can use something like this in your initState
WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
Provider.of<ProgressData>(context, listen: false).add(5);
});
I think it's more standard!
Be aware that you should use the correct context! I mean the context of the Builder!
The problem here lies with the fact that context does not exist yet in initState as extensively explained by the other answers. It doesn't exist because it hasn't yet been made a part of the widget tree.
Calling a method
If you're not assigning any state and only calling a method then initState would be the best place to get this done.
// The key here is the listen: false
Provider.of<MyProvider>(context, listen: false).mymethod();
The code above is allowed by Flutter because it doesn't have to listen for anything. In short, it's a one off. Use it where you only want to do something instead of read/listen to something.
Listening to changes
Alternatively, if you need to listen to changes from Provider then the use of didChangeDependencies would be the best place to do so as context would exist here as in the docs.
This method is also called immediately after initState.
int? myState;
#override
void didChangeDependencies() {
// No listen: false
myState = Provider.of<MyProvider>(context).data;
super.didChangeDependencies();
}
If you've never used didChangeDependencies before, what it does is get called whenever updateShouldNotify() returns true. This in turn lets any widgets that requested an inherited widget in build() respond as needed.
I'd usually use this method in a FutureBuilder to prevent reloading data when data already exists in Provider after switching screens. This way I can just check Provider for myState and skip the preloader (if any) entirely.
Hope this helps.