getter was called on null when using provider in flutter

Brooklyn Lee picture Brooklyn Lee · Feb 22, 2020 · Viewed 7k times · Source

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.

Answer

musausman.com picture musausman.com · Feb 22, 2020

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);