I am making a flutter app using provider pattern. And I am getting null error when I launch my app.
button_data.dart goes like this.
class ButtonData extends ChangeNotifier {
List<Button> _buttons = [
Button(
type: "A",
numberone: "1",
numbertwo: "2",
numberthree: "3",
),
Button(
key: "B",
numberone: "A",
numbertwo: "B",
numberthree: "C",
),
];
Button _selectedButton;
List<Button> get buttons => _buttons;
Button get selectedButton => _selectedButton;
void setSelectedItem(Button s) {
_selectedButton = s;
notifyListeners();
}
Button getType(String value) {
return _buttons
.where((button) => button.type == value).first;
} // it is no use...
}
And I want those button displayed diffently when I switched a type.
new Row(children: [
buildButton(Provider.of<ButtonData>(context).getNumberOne(Provider.of<ButtonData>(context).selectedButton.type)),
buildButton(Provider.of<ButtonData>(context).getNumberTwo(Provider.of<ButtonData>(context).selectedButton.type)),
buildButton(Provider.of<ButtonData>(context).getNumberThree(Provider.of<ButtonData>(context).selectedButton.type)),
])
But Error seems to get returned since maybe I don't set default value. it says 'the getter 'type' was called on null' If possible I want 'type A' to be set as default value so that display shows 1, 2, 3 on the buttons.
So, to figure out null error,
I tried the initState
function below, but that throws more errors.
1. There isn't a setter named 'selectedButton' in class 'ButtonData'
2. The expression here has a type of void and therefore can't be used
@override
void initState() {
Provider.of<ButtonData>(context).selectedButton = Provider.of<ButtonData>(context).setSelectedItem(Provider.of<ButtonData>(context).buttons.first);
super.initState();
}
I've been trying to find similar questions but I can't find the exact questions so I ask you guys.
There are a lot of things you are doing wrong here. You need to understand that Provider can expose to all the Widgets below it. Additionally, this will also help others understand Flutter
and Provider
a bit more.
First of all, make sure that you are exposing the ButtonData object from somewhere above the Row where you want to use the value.
Like so:
//You can use this in the build method, and wrap it on top of your page Scaffold.
//@override
//Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (_) => ButtonData(),
child: Scaffold(
appBar: _appBar(),
body: _body(),
),
);
//}
Second, the way you are using to get buttons in the Row
widget is very bad, when you want to use your object multiple times like in your code, you should use the Consumer
widget to expose your ButtonData as a variable and use that instead of the whole line: Provider.of<ButtonData>(context)
.
Check this out:
//WRONG WAY
new Row(children: [
buildButton(Provider.of<ButtonData>(context).getNumberOne(Provider.of<ButtonData>(context).selectedButton.type)),
buildButton(Provider.of<ButtonData>(context).getNumberTwo(Provider.of<ButtonData>(context).selectedButton.type)),
buildButton(Provider.of<ButtonData>(context).getNumberThree(Provider.of<ButtonData>(context).selectedButton.type)),
])
//CORRECT WAY
Consumer<ButtonData>(
builder: (BuildContext context, ButtonData buttonData, Widget child) => Row(
children: <Widget>[
buildButton(buttonData.getNumberOne(buttonData.selectedButton.type)),
buildButton(buttonData.getNumberTwo(buttonData.selectedButton.type)),
buildButton(buttonData.getNumberThree(buttonData.selectedButton.type)),
],
),
);
Also if you are fetching a Provider
value in your initState
function, that means you are fetching your value OUTSIDE of the widget tree. (Whatever you return in your build
function is your Widget Tree). To fetch a value outside of the widget tree, use this.
///WRONG
Provider.of<ButtonData>(context).selectedButton = Provider.of<ButtonData>(context).setSelectedItem(Provider.of<ButtonData>(context).buttons.first);
///CORRECT
///You need to set `listen` to false.
Provider.of<ButtonData>(context, listen: false).selectedButton = Provider.of<ButtonData>(context, listen: false).setSelectedItem(Provider.of<ButtonData>(context, listen: false).buttons.first);
///BETTER
ButtonData buttonData = Provider.of<ButtonData>(context, listen: false);
buttonData.selectedButton = buttonData.setSelectedItem(buttonData.buttons.first);
One more tip...
You don't need to use new
keyword, if you have seen this in tutorials, then that is because those tutorials are very old, and are using Dart 1, currently in Dart 2, the new
keyword is no longer required.
Hope this helps!!
UPDATE:
You also have missing declarations in your ButtonData
class.
Explanations for the errors you mentioned in the comment:
//If you want to use the following:
Provider.of<ButtonData>(context, listen: false).selectedButton = xyz;
//You need to declare a setter named `selectedButton` in your ButtonData
// class. Like so:
set selectedButton(Button button) {
_selectedButton = button;
notifyListeners();
}
//Currently, in your ButtonData class, you have not declared a setter,
// instead you created a method there for updating the value.
//This one.
void setSelectedItem(Button s) {
_selectedButton = s;
notifyListeners();
}
//If you want to use this method for updating your `selectedButton` value,
// you can use the following line of code.
Provider.of<ButtonData>(context, listen: false).setSelectedItem(xyz);