In Flutter, is there a method like onStateChanged which is called when the state of the page changes?
setState(() {
widget._loadCompleted = true;
widget._loading = false;
});
I'm trying to set the two bool values in setState() method. I'm setting states for several other reasons. So I want to know if the last state change was for this particular reason.
As Günter mentioned, there is no such thing like onStateChanged(). You have to deal it in build() method.
If I got you right, you can use like this:
class _MyAppState extends State<MyApp> {
bool myFlag = false; // initially set to false
void _doYourWork() {
setState(() => myFlag = true); // set to true here
}
#override
Widget build(BuildContext context) {
if (myFlag) {
// setState() just got called
} else {
// we are building fresh for the first time.
}
myFlag = false;
return yourWidget();
}
}
After this build() will receive myFlag value to true and it can then set to false again. So, you can do this trick.
Related
Minimal reproducible code:
final provider = StateProvider<bool>((ref) {
Timer? _timer;
ref.listenSelf((_, flag) {
if (!flag) {
_timer = Timer(Duration(seconds: 5), () {
ref.read(this).state = true;
});
}
});
// ... disposing timer, etc.
return true;
});
The above provider returns true initially, and I change that value to false in a widget and 5s after that, I want to change this value back to true. I'm using listenSelf but I'm not able to do it.
Note:
I don't want to use StateNotifier with StateNotifierProvider.
The ref of a provider generally exposes a way to modify itself.
In the case of StateProvider, you can do Ref.controller to obtain the StateController
You can therefore do ref.controller.state = true
If I have statefull widget with initial satate variable called value ,Like this :
#override
void initState() {
thisDayActivity = dataBase.getDetails(widget.courseId, widget.actId);
value = 0.0;
super.initState();
}
thisDayActivity is future list comming from sqflite database, I want check if the list is empty the value variable equal to 0,else value equale some data in future list.
I tride this but don't work :
#override
void initState() {
thisDayActivity = dataBase.getDetails(widget.courseId, widget.actId);
if (thisDayActivity == []) {
value = 0.0;
} else {
value = thisDayActivity[0]['digree'].toDouble();
}
super.initState();
}
How can I solve this?
your method is not working since you are reading a value from a future function, what you need to do is to use the then method to achieve your goal like this:
#override
void initState() {
dataBase.getDetails(widget.courseId, widget.actId)
.then((thisDayActivity) {
if (thisDayActivity == []) {
value = 0.0;
} else {
value = thisDayActivity[0]['digree'].toDouble();
}
});
super.initState();
}
I have run into an issue with Flutter: I have a navbar which, depending on the item selected, returns me an integer number called Index. This index is then passed through a List to get the content of the body of the Scaffold, an object of Widget() class.
The default body is an object of HomePage(), that has an integer parameter called rpm. By default, the HomePage() should be the body displayed, so, as it depends on the rpm parameter, I declare the rpm parameter on the initState. I also have a setState that changes dynamically the rpm.
The weird thing is: if I declare the List<Widgets> bodyList in the initState, the setState doesn't seem to work. However, if I declare List<Widgets> bodyList in the build method, I can see the content of the HomePage() change dynamically with the rpm.
An excerpt of the code. Not working:
class _ScreenTreeState extends State<ScreenTree> {
int _index;
int _rpm;
List<Widget> bodyList;
#override
void initState() {
_isPlaying = false;
_rpm = 0;
_index = 0;
bodyList = [
HomePage(rpm: _rpm),
StatisticsScreen(),
WeightScreen(),
SettingsPage()
];
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(...
Working:
class _ScreenTreeState extends State<ScreenTree> {
int _index;
int _rpm;
List<Widget> bodyList;
#override
void initState() {
_isPlaying = false;
_rpm = 0;
_index =
0;
super.initState();
}
#override
Widget build(BuildContext context) {
bodyList = [
HomePage(rpm: _rpm),
StatisticsScreen(),
WeightScreen(),
SettingsPage()
];
return Scaffold(...
My theory is that this may have to do with the fact that if _rpm is declared as an integer, Dart reads it as a primitive and not as the object of an integer class, so if declared on the initState, I'm not actually passing an object, but a primitive. But I don't understand then why is it working when declared in the build method.
I feel this is irrelevant, but if somebody needs it, here it is the code that updates the value of _rpm. Please, don't read too much into this because I feel it is actually not relevant and may distract and confuse more than help:
onPressed: () {
if (_isPlaying) {
setState(() {
_isPlaying = false;
});
Provider.of<MicrophoneEngine>(context, listen: false)
.stopRecording();
} else {
setState(() {
_isPlaying = true;
});
Provider.of<MicrophoneEngine>(context, listen: false)
.startRecording((rpmCall) {
setState(() {
_rpm = rpmCall;
});
});
}
},
It's a bad practice to keep references for the widgets(like in the first example), in fact, you don't need that List at all.
Every time you use setState, you rebuild the widgets and you pass the new value. In the first example you pass the HomePage(rpm: _rpm) from the init every time, that's why it is not updated. Meaning, you don't rebuild HomePage with the updated value, you just passing the HomePage from the initState which was instantiated with the initial _rpm value of 0.
Since flutter calls the build method many times in different condition, to avoid getting the data many times, I initialize the data in initState.
I want to re-build the widget when the data is ready.
Here is my code :
class Test extends StatefulWidget {
#override
_TestState createState() => new _TestState();
}
class _TestState extends State<Test> {
Data data;
bool dataReady = false;
#override
void initState() {
super.initState();
getData(context).then((Data data) async {
setState(() {
dataReady= true;
});
});
}
#override
Widget build(BuildContext context) {
if (dataReady) {
return createMainContent(context);
} else {
return new Container();
}
}
}
However, it results in following exception :
inheritFromWidgetOfExactType(_InheritedProvider) or inheritFromElement() was called before _TestState.initState() completed.
May I know am I doing something wrong here?
When I add the following line to implementation of getData(context)
await Future.delayed(new Duration(milliseconds: 300));
the exception does not happen.
For everyone coming here at a later point
It is best to use the #override void didChangeDependencies () method of the State class.
From the docs
This method is also called immediately after initState. It is safe to call BuildContext.inheritFromWidgetOfExactType from this method.
But make sure to check if you have already performed your initialization
#override
void didChangeDependencies() {
super.didChangeDependencies();
if (bloc == null) { // or else you end up creating multiple instances in this case.
bloc = BlocProvider<MyBloc>.of(context);
}
}
Edit: Better answer below.
Apparently, you cannot access getData(context) during initState (more concrete: before it completed).
The reason, so I believe, is that getData tries to look up an InheritedWidget ancestor up in the tree, but the tree is just now being built (your widget is created during the parent widget's build).
The obvious solution would be to delay getData's lookup to a later point in time. There are several ways to achieve that:
Delay the lookup to a later time. scheduleMicrotask should work fine.
Look it up during the first build call. You could have an isInitialized field set to false and in you build, something like:
if (!isInitialized) {
isInitialized = true;
// TODO: do the getData(...) stuff
}
an alternative is to put it inside PostFrameCallback which is between initState and Build.
#override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((_) => getData());
super.initState();
}
getData() async {
}
I moved my code to my build method from initState and it worked
class _TestState extends State<Test> {
Data data;
bool dataReady = false;
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
getData(context).then((Data data) async {
setState(() {
dataReady= true;
});
});
if (dataReady) {
return createMainContent(context);
} else {
return new Container();
}
}
}
Befor HTTP response show loading and after https response create tab view. I successfully create tab view but my problem is after HTTP request unable to update the view in the android studio with java we use
`mAdapter.notifyDataSetChanged();
mActivity.runOnUiThread(new Runnable() {
#Override
public void run() {
}
});`
code for doing this.
what is the equivalent code in the flutter?
If you want update any widget in flutter you should call setState((){})
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> {
bool isLoading = false;
#override
void initState() {
super.initState();
isLoading = true;
getData();
}
#override
Widget build(BuildContext context) {
return isLoading ? new CircularProgressIndicator() : // show list;
}
Future<ModelClass> getData() async {
// get data from http
...
// after processing response
setState(() {
isLoading = false;
});
}
}
In Flutter, the UI is updated when the State is changed using setState method.
In your case, you will need some variables (E.x: isLoading, items (for your list)).
When the app loads data from backend, isLoading = true, items = [] (empty). When the request is done, isLoading = false and items = [item1, item2,...]. Here is where you have to use setState method to update your UI.
Under your build method, you have to use these 2 variables to render the corresponding UI.