Flutter How to recreate stateful widget in build() - flutter

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.

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.

How to force initState every time the page is rendered in 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.

Flutter: Provider and how to update records from the DB in the background

I am new to Flutter and I have this simple use case: in my Cloud Firestore DB I have a list of JSON representing events. I want to show them through my Flutter app in a ListView.
My requirements is that the ListView doesn't refresh in real-time but only when a pull-on refresh (implemented using RefreshIndicator) is done by the user or when the app resumes from background
I tried to implement this in 2 ways (I am using provider package for state management):
Using StreamProvider to create a stream of records from the DB. This continuosly updates the list view (basically the widget changes while the user is looking at it and I don't want this)
Using a ChangeNotifierProvider that refers to a EventManager class which holds a List<Event>. This class has a pull method which updates its internal state. I call this method when the user does the pull-on refresh (in the onRefresh callback of RefreshIndicator).
Option 2 seems to work well however I do not know how to implement the refresh when the app resumes from background. As I said I am using provider (and therefore StatelessWidget) and apparently there is no way to bind to these events when using StatelessWidgets
Do you have any suggestions and best practices for this use case?
You need to access Flutters lifecycle methods and fire a callback when the app resumes.
You can add a stateful widget with WidgetsBindingObserver and put that somewhere in the scope of your Provider, but as a parent of whatever widget you use to display the info.
Or you can make your PullToRefresh widget stateful and do the same thing.
class LifeCycleWidget extends StatefulWidget {
#override
_LifeCycleWidgetState createState() => _LifeCycleWidgetState();
}
class _LifeCycleWidgetState extends State<LifeCycleWidget>
with WidgetsBindingObserver {
AppLifecycleState _appLifecycleState;
#override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
refreshOnResume();
}
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
setState(() {
_appLifecycleState = state;
});
refreshOnResume();
}
void refreshOnResume() {
if (_appLifecycleState == AppLifecycleState.resumed) {
print('resumed');
// your refresh method here
}
}
#override
Widget build(BuildContext context) {
return HomePage();
}
}
Add the following to your main method if it's not there already.
WidgetsFlutterBinding.ensureInitialized();
Another way to do it without adding a stateful widget would be with GetX. You can still keep all your Provider stuff but only use the SuperController which provides lifecycle methods. This I can't test because I don't have your Provider code but you can probably get away with creating the class below and initializing the controller somewhere within the scope of the relevant Provider widget with
Get.put(LifeCycleController());
Then call the function in the onResumed override and you can use Get.context if you need context.
class LifeCycleController extends SuperController {
#override
void onDetached() {
debugPrint('on detached');
}
#override
void onInactive() {
debugPrint('on inactive');
}
#override
void onPaused() {
debugPrint('on pause');
}
#override
void onResumed() {
// your refresh function here. Access context with Get.context
debugPrint('on resume');
}
}

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.

Should codes be written before super.initState(); or after in Flutter?

Should the code that is being written to initState() function be written before super.initState(); or after?
Which one is proper:
#override
// code here
super.initState();
}
or
#override
super.initState();
// code here
}
both will work.
But if you see from any dependencies or official docs flutter, write your code in initSate() after super.initState();
#overrride
initState(){
super.initState()
//your code
}
reference to this initState
the opposite for dispose(), write your code before super.dispose();
#overrride
dispose(){
//your code
super.dispose()
}
reference to dispose
When I see #Kahoo answer, I check it by cmd + click at super.dispose and super.initstate, I found this for dispose
/// If you override this, make sure to end your method with a call to
/// super.dispose().
///
/// See also:
///
/// * [deactivate], which is called prior to [dispose].
#protected
#mustCallSuper
void dispose() {
assert(_debugLifecycleState == _StateLifecycle.ready);
assert(() {
_debugLifecycleState = _StateLifecycle.defunct;
return true;
}());
}
abstract class State :
/// If you override this, make sure your method starts with a call to
/// super.initState().
#protected
#mustCallSuper
void initState() {
assert(_debugLifecycleState == _StateLifecycle.created);
}
Both will work fine, but the better practise is to write before super.initState(), because it will do all the initialisations before creating the state widget which will help you in maintaining the check on the Widget State.
But this does not means that the second method will not keep close eye on maintaining the state, But as of better practise the First way is preferred.