getter был вызван с нулевым значением при использовании провайдера во флаттере

Я делаю приложение Flutter, используя шаблон поставщика. И я получаю нулевую ошибку при запуске своего приложения.

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

Но похоже, что ошибка возвращается, поскольку, возможно, я не установил значение по умолчанию. в нем говорится, что «тип получателя» был вызван с нулевым значением «Если возможно, я хочу, чтобы« тип A »был установлен в качестве значения по умолчанию, чтобы на кнопках отображались 1, 2, 3.

Итак, чтобы выяснить нулевую ошибку, я попробовал функцию 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();
}

Я пытался найти похожие вопросы, но не могу найти точных вопросов, поэтому задаю вам, ребята.


person Brooklyn Lee    schedule 22.02.2020    source источник


Ответы (2)


Вы делаете здесь много ошибок. Вы должны понимать, что провайдер может предоставлять доступ ко всем находящимся ниже виджетам. Кроме того, это также поможет другим понять Flutter и Provider немного больше.

Прежде всего убедитесь, что вы открываете объект 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(),
        ),
      );
//}


Во-вторых, способ получения кнопок в виджете 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 функции, является вашим деревом виджетов). Используйте 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);

Еще один совет ... Вам не нужно использовать ключевое слово 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);

person musausman.com    schedule 22.02.2020
comment
Спасибо, BasedMusa! Причина, по которой я кодирую этот объект несколько раз, заключалась в том, что я использую несколько поставщиков, и я использовал другой объект в качестве потребителя, поэтому я не знал, как использовать несколько способов потребителя ... Но я действительно ценю ... теперь я знаю, что не так с моим код. - person Brooklyn Lee; 22.02.2020
comment
И я просто попытался получить значение своего Provider, как вы предложили. но ошибка возвращается. 1. В классе ButtonData нет сеттера с именем selectedButton. 2. Выражение здесь имеет тип void и поэтому не может использоваться - - person Brooklyn Lee; 22.02.2020
comment
@BrooklynLee Вы можете проверить обновленный блок, я объяснил, в чем проблемы и как их исправить. - person musausman.com; 23.02.2020
comment
Спасибо. Я просто согласился и проголосовал. Сеттер вроде работает. Но я все равно получаю еще одну ошибку, когда использую initstate .. :-( - person Brooklyn Lee; 23.02.2020
comment
зависимыйOnInheritedWidgetOfExactType ‹InheritedProvider ‹ButtonData›› () или independentOnInheritedElement () был вызван до завершения _ChordsScreenState.initState (). - person Brooklyn Lee; 23.02.2020
comment
Не могли бы вы найти, как решить эту проблему? Если эта проблема связана с другой проблемой, я отправлю другой вопрос. Спасибо. - person Brooklyn Lee; 23.02.2020
comment
Я бы посоветовал опубликовать новый вопрос, вставить сюда ссылку, и я проверю его, а также обязательно включите полный стек для проблемы и код, в котором эта проблема была сгенерирована. - person musausman.com; 23.02.2020
comment
пожалуйста, проверьте это. stackoverflow.com/questions/60363665 / - person Brooklyn Lee; 23.02.2020
comment
У меня такая же проблема, я создал здесь вопрос. Вы можете мне с этим помочь? stackoverflow.com/q/65419586/5901713 - person Arvina Kori; 24.02.2021
comment
@ArvinaKori Я подробно ответил на ваш вопрос, но, пожалуйста, загрузите ваши полные журналы, чтобы я мог помочь вам и найти точное исправление. - person musausman.com; 24.02.2021

В большинстве случаев вы можете исправить это, просто используя свойство Statefull.mounted.

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

  super.initState();
}
person e200    schedule 29.10.2020