web-dev-qa-db-ja.com

フラッターでプロバイダーを使用しているときにnullでゲッターが呼び出されました

プロバイダーパターンを使用してフラッターアプリを作成しています。また、アプリを起動するとnullエラーが発生します。

button_data.Dartはこのようになります。

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...
}

また、タイプを切り替えたときに、それらのボタンを区別して表示したいのです。

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)),
])

しかし、デフォルト値を設定していない可能性があるため、エラーが返されるようです。 「ゲッター「タイプ」がnullで呼び出されました」と表示されます。可能であれば、「タイプA」をデフォルト値として設定して、ボタンに1、2、3と表示されるようにします。

したがって、nullエラーを理解するには、
以下のinitState関数を試しましたが、エラーがさらに発生します。

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

同様の質問を探してみましたが、正確な質問が見つからないので、皆さんにお聞きします。

1
Brooklyn Lee

ここであなたが間違っていることはたくさんあります。プロバイダーはその下のすべてのウィジェットに公開できることを理解する必要があります。さらに、これは他の人がFlutterProviderをもう少し理解するのにも役立ちます。

Firstすべてのうち、値を使用するRowの上のどこかからButtonDataオブジェクトを公開していることを確認してください。

そうです:

_//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(),
        ),
      );
//}

_


2番目Rowウィジェットでボタンを取得するために使用している方法は非常に悪いです。コードのようにオブジェクトを複数回使用する場合、 Consumerウィジェットを使用してButtonDataを変数として公開し、行全体ではなくProvider.of<ButtonData>(context)を使用する必要があります。

これをチェックしてください:

_//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)),
    ],
  ),
);
_

また、Provider関数でinitState値をフェッチする場合は、ウィジェットツリーの外側で値をフェッチすることを意味します。 (build関数で返すものはすべてウィジェットツリーです)。ウィジェットツリーの外で値をフェッチするには、これを使用します。

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

もう1つのヒント...チュートリアルでこれを見た場合、newキーワードを使用する必要はありません。これは、これらのチュートリアルが非常に古く、Dartを使用しているためです1、現在Dart 2にあるnewキーワードは不要になりました。

これがお役に立てば幸いです!!

更新:

また、ButtonDataクラスに宣言がありません。

コメントで言及したエラーの説明:

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

_
0
BasedMusa