6

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.

General Grievance
  • 4,555
  • 31
  • 31
  • 45
Brooklyn Lee
  • 369
  • 2
  • 6
  • 15

2 Answers2

11

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

hysabone.com
  • 2,959
  • 2
  • 15
  • 26
  • Thanks, BasedMusa! the reason I code that object multiple times was I am using multi-provider and I used other object as consumer so I did not know how to use multiple consumer way... But I really appreciate.. now I know what is wrong with my code. – Brooklyn Lee Feb 22 '20 at 15:22
  • And I just tried to fetch my Provider value the way you suggested. but the error returns. 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 – – Brooklyn Lee Feb 22 '20 at 15:31
  • @BrooklynLee You can check the updated block, I explained what the issues are and how to fix them. – hysabone.com Feb 23 '20 at 08:51
  • Thanks. I just acceped and gave a vote. Setter seems to work. But I still get another error when I use initstate.. :-( – Brooklyn Lee Feb 23 '20 at 10:39
  • dependOnInheritedWidgetOfExactType>() or dependOnInheritedElement() was called before _ChordsScreenState.initState() completed. – Brooklyn Lee Feb 23 '20 at 10:40
  • Would you mind if you can find how to fix this issue? if it is this issue related to other problem, I will post another question. thx. – Brooklyn Lee Feb 23 '20 at 10:41
  • I'd suggest posting a new question, paste the link here, and I'll check it out, also make sure to include the full stack for the issue and the code where this issue was generated. – hysabone.com Feb 23 '20 at 10:46
  • plz check this out. https://stackoverflow.com/questions/60363665/dependoninheritedelement-was-called-before-initstate-in-flutter – Brooklyn Lee Feb 23 '20 at 15:17
  • 1
    I have same issue, i have created a question here. Can you help me with it. https://stackoverflow.com/q/65419586/5901713 – Arvina Kori Feb 24 '21 at 04:25
  • @ArvinaKori I answered your question in detail, but please upload your complete logs, so that I can further help you and find the exact fix. – hysabone.com Feb 24 '21 at 08:47
2

For most cases you can fix it just using the Statefull.mounted property.

initState() {
  if (mounted) {
    context.read<YouType>();
  }

  super.initState();
}
Eleandro Duzentos
  • 1,370
  • 18
  • 36