I'm very new to Dart. I was learning basic stuff for Flutter. And I've got some questions while working on BottomNavigationBar:
onTap: (index) {
setState(() {
_currentIndex = index;
});
},
I have no idea why there's index as a parameter in the function. Where is it coming from?
Also, in the setState function, why is there a function and what does it mean?
I'm sorry these questions are very elementary but I couldn't find clear answers!
Thank you in advance!
onTap: (index) {} : Index use for Keep track of the index of the selected BottomNavigationBarItem.
setState() : Whenever you change the state of a State object, make the change in a function that you pass to setState.
For more info :
onTap : https://api.flutter.dev/flutter/material/BottomNavigationBar/onTap.html
setState : https://api.flutter.dev/flutter/widgets/State/setState.html
Firstly onTap is a callback function and depending on widget it can have zero o more parameters. For example in InkWell widget the onTap callback has no parameters and in BottomNavigationBar the onTap callback take index as parameter to recognize the index of items in that widget.
Secondly, as you know setState function is used for updating UI and called inside State class.
When you use StatefulWidget, every StatfulWidget has a state object like code below:
class MyWidget extends StatefulWidget { // immutable Widget
#override
_MyWidgetState createState() => _MyWidgetState();
// creating State Object of MyWidget
}
class _MyWidgetState extends State<MyWidget> { // State Object
#override
Widget build(BuildContext context) {
return Container();
}
}
Since StatefulWidget itself is immutable (cannot be modified), we use State Object to modify the UI.
We tell this State Object to update our screen's UI using a function called setState().
void setState(VoidCallback fn) {
...
}
This setState() takes a function as it's parameter.
The function called inside setState does not need have any parameter as well.
finally, you can hold ctrl key and click on which function you want and see the implementation of the function.
Related
I have this function in a widget (homescreen):
void toggleRecording() async {
// HERE IS THE CONFUSION I GUESS
_isRecording = !_isRecording;
recorder = SoundStream(isRecording: _isRecording);
//recorder.toggleRecording(_isRecording);
setState(() {
_isRecording = recorder.isRecording;
});
if (_isRecording) {
startTimer();
_stopwatch.start();
} else {
stopTimer();
_stopwatch.stop();
}
}
It needs to call (trigger) another function in my recorder class:
void toggleRecording() async {
widget.isRecording ////// currently being passed as an argument from homescreen
? {_recorder.stop, await save(_micChunks, 44100)}
: _recorder.start;
}
Also, the boolean variable _isRecording is present in both the classes. How do I sync the state?
In your situation passing reference of function through widgets will work. However best practice of this will be using provider package.
Managing functions from provider will allow you to control functions from all pages.
If you change a variable you can call notifylistener() function inside your provider function. So that you can change state of widget.
I will try to explain it in a glance however this is an important subject of flutter.
Here is my folder structure
At provider folder we define our provider classes.
Firstly i define class which extends changeNotifier class. This is what make this class provider.
Side note: notifyListener() function here calls setState of every widget if you use any variables inside that class and this is what you are searching for.
Then i import it into my main.dart file or whatever file you want. Only condition is being above the widget that you will use provider at.
At last you can use your function at everywhere if you import provider package and define your provider like i did in this code.
At last here is the visualized stucture of provider package.
I wish i explained it well. There is more about it on youtube.
Pass the function to other widget
using Function keyword
Say ContentWidget is your child and ParentWidget is parent
class ParentWidget extends StatefulWidget {
//Do Something
void onSomeFunction()
{
ContentWidget(onTimerUpdate:onTimerClosed)
}
void onTimerClosed()
{
//Perform Operation on Timer Change
}
}
class ContentWidget extends StatefulWidget {
final Function onTimerUpdate;
ContentWidget({
Key key,
#required this.onTimerUpdate,
}) : super(key: key);
void onAnyActionFromChild()
{
widget.onTimerUpdate() //Note () can have any param or can be null
}
I'm new to using Flutter and I am currently struggling to understand how to use the Provider package for the following task, or if it is even the correct implementation in the first place.
I have a widget that uses another widget within itself to update a time value.
In the parent widget I have the following:
class _AddTimesScreenState extends State<AddTimesScreen> {
List<TimeOfDay> times = [];
#override
Widget build(BuildContext context) {
return Scaffold(
body: Provider<List<TimeOfDay>>.value(
value: times,
child: SetTimes()
In the 2nd widget, which is used to update the times list by using a time picker I have:
class _SetTimesState extends State<SetTimes> {
#override
Widget build(BuildContext context) {
final times = Provider.of<List<TimeOfDay>>(context);
Essentially my goal is to be able to update the times list in the 2nd widget so it can then be used in the first widget. I have methods to add TimeOfDay objects to the list, but when the code is run the list in the first widget does not appear to be updated.
Am I using Provider in a way that it's intended, or have I completely misunderstood its application?
Thanks
In the TimeOfDay class make sure you are extending it with Change Notifier.
How does provider know it has to rebuild?
When the class (TimeOfDay in your case) extends ChangeNotifier, you are provided with a method called notifylisteners() , this triggers a rebuild to all the widgets consuming the provider. So you should call this in the function that is changing the objects data in your class TimeOfDay.
So make sure you are:
extending ChangeNotifier in your class/model.
calling notifylisteners when data is changed.
Example :
class MyClass extends ChangeNotifier{
int a = 0;
addSomething(){
//Here we are changing data
a = a + 1;
notifylisteners();
}
}
let me know if this solves your error.
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.
I have a stateful widget which includes buttons. I'm keeping number of user taps in an int variable called "counter".
I have another widget which is my home screen. I have a Text on that screen. I need to show the number of taps on this Text dynamically. So when user taps the button on the first class, my Text on the second class must be updated.
What is the best way to pass the counter variable from first widget to second widget?
Added some code
Both of my classes are stateful widgets. So, I declared static int counter like these;
class AppListView extends StatefulWidget {
static int counter;
#override
_AppListViewState createState() => _AppListViewState();
}
Now I can reach counter from other class but can't reach from its own class. I mean I need to increase counter in that part but It says undefined.
class _AppListViewState extends State<AppListView> {
#override
Widget build(BuildContext context) {
return FlatButton(
onPressed: (){
counter++ (can't reach counter here)
}
You might want to make your desired variable static.
class Something {
static int counter;
}
Then you can use the variable in the other widget like so:
class StatefulWidget{
FlatButton(
onPressed: (){
Something.counter++; // This variable is your counter you mentioned earlier
}
);
}
Approach 1:
Create a ValueChanged from Parent Widget add pass to FirstWidget, when change couter -> call ValueChanged to send update to Parent Widget, Parent will update state for second Widget.
Approach 2:
Create a Stream from Parent Widget add pass to FirstWidget and Sencond Widget (using StreamBuilder to render widget). when change couter -> change value of Stream to update to Second Widget.
Update Demo:
Link DartPad
Simple, in your code just write "widget.counter" in child. In statefull widget to access parent variable, you have to use "widget.instace". In this case, "widget.counter" will let u access counter variable of the parent.
class AppListView extends StatefulWidget {
int counter;
#override
_AppListViewState createState() => _AppListViewState();
}
class _AppListViewState extends State<AppListView> {
#override
Widget build(BuildContext context) {
return FlatButton(
onPressed: (){
widget.counter++
}
Note: u don't need counter variable to be static as u can call it with widget.counter in child class
You should use the provider package to associate the object with Provider, not any widget. Wrap the counter inside a class and pass it to MultiProvider:
void main() {
runApp(
MultiProvider(
providers: [
Provider(create: (context) => Something()),
],
child: const MyApp(),
),
);
}
And then you can access it from your build methods in each widget by:
var something = Provider.of<Something>(context, listen: false);
something.value++;
I suggest one of the existing easy solutions to use a global library to share your application variables between classes :
Create a dart file named globals.dart
globals.dart :
library YOUR_PACKAGE_NAME.globals;
int x = 1 ;
class1 :
import 'package:YOUR_PACKAGE_NAME/globals.dart' as globals;
print(globals.x) ; //prints 1
globals.x= 2 ;
class2 :
import 'package:YOUR_PACKAGE_NAME/globals.dart' as globals;
print(globals.x) ; //prints 2
https://devbrains.tn/forum/question/how-to-call-variables-from-different-class-in-flutter#6
I am new to flutter and when I want to call my context in InitState it throws an error :
which is about
BuildContext.inheritFromWidgetOfExactType
but then I use didChangeDependencies and it works correctly.
Now I have 2 question:
1- Why calling our context in initState does not work but it works when calling from didChangeDependencies ?
(because as I read in official doc This method is also called immediately after [initState],
and both of them will be called before build method. )
2- Why do we have access to our context outside of build method ( because there we have build(BuildContext context) and we can use our context but in didChangeDependencies we don't have anything like didChangeDependencies(BuildContext context) , so from where can we call context to use it) ?
Context of a state is available to us from the moment the State loads its dependencies.
At the time build is called, context is available to us and is passed as an argument.
Now moving on,
initstate is called before the state loads its dependencies and for that reason no context is available and you get an error for that if you use context in initstate.
However, didChangeDependencies is called just a few moments after the state loads its dependencies and context is available at this moment so here you can use context.
However both of them are called before build is called.
The only difference is that one is called before the state loads its dependencies and the other is called a few moments after the state loads its dependencies.
I've found a significant difference between initState and didChangeDependencies:
initState is called only once for a widget.
didChangeDependencies may be called multiple times per widget lifecycle (in my case it was called when the keyboard appears / disappears)
initState() Called when new Widget is inserted into the tree.
The framework will call this method exactly once for each [State] object
it creates. This will be called once so perform work which required to be performed only once, but remember context can't be used here, as widget state gets loaded only initState() work is done.
Syntax:
#override
void initState() {
debugPrint('initState()');
super.initState();
}
didChangeDependencies() Called when a dependency of this [State] object changes.
So, exactly How it gets called? as by the above definition, it looks like it will be called after state changes but how we come to know the state is changed?
Example:
The below example uses the Provider state management mechanism to update the child widget from the parent widget. The Provider has an attribute called updateShouldNotify which decides whether to state is changed or not. If it's returning true then only didChangeDependencies gets called in ChildWidget class.
updateShouldNotify is returning true by default internally, as it knows the state got changed. Then Why we need updateShouldNotify? it's need because if someone wants to update the state on a specific condition,
Eg: if UI required to show only even values then we can add a condition like
updateShouldNotify: (oldValue, newValue) => newValue % 2 == 0,
Code Snippet:
class ParentWidget extends StatefulWidget {
ParentWidget({Key key, this.title}) : super(key: key);
final String title;
#override
_ParentWidgetState createState() => _ParentWidgetState();
}
class _ParentWidgetState extends State<ParentWidget> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Life Cycle'),
),
body: Provider.value(
value: _counter,
updateShouldNotify: (oldValue, newValue) => true,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Press Fab button to increase counter:',
),
ChildWidget()
],
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
class ChildWidget extends StatefulWidget {
#override
_ChildWidgetState createState() => _ChildWidgetState();
}
class _ChildWidgetState extends State<ChildWidget> {
int _counter = 0;
#override
void initState() {
print('initState(), counter = $_counter');
super.initState();
}
#override
void didChangeDependencies() {
_counter = Provider.of<int>(context);
print('didChangeDependencies(), counter = $_counter');
super.didChangeDependencies();
}
#override
Widget build(BuildContext context) {
print('build(), counter = $_counter');
return Text(
'$_counter',
);
}
}
Output Logs:
I/flutter ( 3779): didChangeDependencies(), counter = 1
I/flutter ( 3779): build(), counter = 1
For detail explanation:
https://medium.com/#jitsm555/differentiate-between-didchangedependencies-and-initstate-f98a8ae43164?sk=47b8dda310f307865d8d3873966a9f4f
According to initState documentation
You cannot use BuildContext.inheritFromWidgetOfExactType from this method. However, didChangeDependencies will be called immediately following this method, and BuildContext.inheritFromWidgetOfExactType can be used there.
So you need to use BuildContext.inheritFromWidgetOfExactType in didChangeDependencies.
Every Widget has its own context. That is why you have access to context outside build method.
Regarding build(BuildContext context), build method accepts context from the parent widget. It means this parameter BuildContext context is not current widget's context but its parent's context.
The notion of context being mysteriously accessible outside build() was the one that bugged me. I think clarifying this subtle point supplements explanations given in other answers about the first question.
How is context accessible from outside the build() method?
The confusion stems from the (wrong) assumption that context needed to be passed to State.build() at all. Note that the State class already has a context property and according to the documentation, it is redundantly provided to build() here, so that its signature matches that of a WidgetBuilder. However, this is not the same build() method as that of a StatelessWidget.
Called when a dependency of this State object changes.
For example, if the previous call to build referenced an InheritedWidget that later changed, the framework would call this method to notify this object about the change.
This method is also called immediately after initState. It is safe to call BuildContext.dependOnInheritedWidgetOfExactType from this method.
In fact Subclasses rarely override this method because the framework always calls build after a dependency changes. Some subclasses do override this method because they need to do some expensive work (e.g., network fetches) when their dependencies change, and that work would be too expensive to do for every build.
This is a supplemental answer showing what the OP described.
The State class of a StatefulWidget has a context property. This build context is first available in didChangeDependencies. Trying to use context in initState will cause an error.
class HomeWidget extends StatefulWidget {
const HomeWidget({Key key}) : super(key: key);
#override
_HomeWidgetState createState() => _HomeWidgetState();
}
class _HomeWidgetState extends State<HomeWidget> {
#override
void initState() {
print('initState');
// print(Theme.of(context)); // ERROR!
super.initState();
}
#override
void didChangeDependencies() {
print('didChangeDependencies');
print(Theme.of(context)); // OK
super.didChangeDependencies();
}
#override
Widget build(BuildContext context) {
print('build');
print(Theme.of(context)); // OK
return Container();
}
}
Running that gives the print statements in the following order:
initState
didChangeDependencies
ThemeData#93b06
build
ThemeData#93b06
See also Working with didChangeDependencies() in Flutter
you can still use context in initState() method, its hack buts works, all you need to do is sought of delay whatever you will need to execute that has context in it like so:
#override
void initState() {
Future.delayed(Duration.zero).then((_) {
// you code with context here
});
super.initState();
}