Flutter: невозможно получить доступ к поставщику из showModalBottomSheet

Я не могу получить доступ к провайдеру, определенному выше Scaffold, из showModalBottomSheet в FloatingActionButton.

Я определил домашнюю страницу так:

class HomePage extends StatelessWidget {
 @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (_) => MyProvider(),
      builder: (context, _) {
        return Scaffold(
          body: Consumer<MyProvider>(
             builder: (context, provider, _) {
               return Text(provider.mytext); // this works fine
             }
          ),
          floatingActionButton: MyFAB(), // here is the problem
        );
      }
    )
  }
}

А это MyFAB:

class MyFAB extends StatefulWidget {
  @override
  _MyFABState createState() => _MyFABState();
}

class _MyFABState extends State<MyFAB> {
  @override
  Widget build(BuildContext context) {
    return FloatingActionButton(
      ...
      onPressed: () => show(),
    );
  }
  
  void show() {
    showModalBottomSheet(
      ...
      context: context,
      builder: (BuildContext context) {
        return Wrap(
          children: [
            ...
            FlatButton(
              onPressed: Provider.of<MyProvider>(context, listen: false).doSomething(); //Can't do this
              Navigator.pop(context);
            )
          ],
        );
      }
    );
  }
}

Ошибка: не удалось найти правильный поставщик ‹MyProvider над этим виджетом BottomSheet.


comment
Вы пытались передать контекст (полученный из метода build) вместо использования context вашего _MyFABState экземпляра?   -  person Guilherme Matuella    schedule 09.09.2020
comment
@GuilhermeMatuella, вы имеете в виду контекст из метода сборки в виджете HomePage?   -  person Fabio    schedule 09.09.2020
comment
Нет, BuildContext из метода build внутри _MyFabState. Тот, который вы создаете для своего FloatingActionButton   -  person Guilherme Matuella    schedule 09.09.2020


Ответы (3)


Это вызвано передачей ему неправильного контекста. Оберните свой FAB в виджет Builder и передайте его как свойство Builder. Это возьмет новый контекст и передаст его showModalBottomSheet. Также можно сделать onPressed: show, это более лаконично.

 class HomePage extends StatelessWidget {
 @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (_) => MyProvider(),
      builder: (context, _) {
        return Scaffold(
          body: Consumer<MyProvider>(
             builder: (context, provider, _) {
               return Text(provider.mytext); // this works fine
             }
          ),
          floatingActionButton: MyFAB(context), // here is the problem
        );
      }
    )
  }
}


class MyFAB extends StatefulWidget {
  @override
  _MyFABState createState() => _MyFABState();
}

class _MyFABState extends State<MyFAB> {
  @override
  Widget build(BuildContext context) {
    return FloatingActionButton(
      ...
      onPressed: (context) => show(context),
    );
  }
  
  void show(ctx) {
    showModalBottomSheet(
      ...
      context: ctx,
      builder: (BuildContext context) {
        return Wrap(
          children: [
            ...
            FlatButton(
              onPressed: () {
              Provider.of<MyProvider>(ctx, listen: false).doSomething(); //Can't do this
              Navigator.pop(ctx)
              };
            )
          ],
        );
      }
    );
  }
}
person Eren    schedule 09.09.2020
comment
Я попробовал floatingActionButton: Builder(builder: (context) => MyFAB()), но это не сработало. Я получаю ту же ошибку - person Fabio; 09.09.2020
comment
@Fabio Я отредактировал образец кода. Вам нужно каким-то образом передать контекст, который вы хотите, в вашу функцию шоу. Это должно сработать. Если нет, поделитесь своим кодом целиком, и я отредактирую его. Я также исправил ваше свойство FlatButton onPressed. Две вещи, которые вы делаете, должны быть в одной функции и переданы кнопке. - person Eren; 09.09.2020
comment
Как вы передали параметр контекста из HomePage без конструктора в MyFAB? - person Fabio; 09.09.2020
comment
Я не тестировал это, так как большая часть кода неполная. Если вы получаете сообщение об ошибке, попробуйте определить show () на своей домашней странице и передать его своей фабрике вместо того, чтобы создавать совершенно новый файл просто для фабрики. - person Eren; 09.09.2020
comment
Я создал новый файл для FAB, потому что он сложнее, однако я просто передал контекст из HomePage в MyFAB, и он работает - person Fabio; 09.09.2020

РЕШЕНИЕ

Домашняя страница:

class HomePage extends StatelessWidget {
 @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (_) => MyProvider(),
      builder: (context, _) {
        return Scaffold(
          body: Consumer<MyProvider>(
             builder: (context, provider, _) {
               return Text(provider.mytext); // this works fine
             }
          ),
          floatingActionButton: MyFAB(context), // here is the problem
        );
      }
    )
  }
}

MyFAB:

class MyFAB extends StatefulWidget {
final BuildContext ctx;

MyFAB(this.ctx)

  @override
  _MyFABState createState() => _MyFABState();
}

class _MyFABState extends State<MyFAB> {
  @override
  Widget build(BuildContext context) {
    return FloatingActionButton(
      ...
      onPressed: () => show(),
    );
  }
  
  void show() {
    showModalBottomSheet(
      ...
      context: context,
      builder: (BuildContext context) {
        return Wrap(
          children: [
            ...
            FlatButton(
              onPressed: Provider.of<MyProvider>(widget.ctx, listen: false).doSomething(); //Can't do this
              Navigator.pop(context);
            )
          ],
        );
      }
    );
  }
}
person Fabio    schedule 09.09.2020

Исправлено размещением поставщика выше MaterialApp, как описано здесь.

Нижние листы создаются в корне приложения материала. Если продайвер объявлен под приложением материала, нижний лист не может получить к нему доступ, потому что поставщик не является предком нижнего листа в дереве виджетов.

На снимке экрана ниже показано дерево виджетов: все приложение находится внутри Wrapper, а нижний лист не создается внутри Wrapper. Он создается как другой дочерний элемент MaterialApp (в данном случае с корневым элементом Container).

дерево виджетов

Для вашего случая:

// main.dart
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (_) => MyProvider(),
      builder: (context, _) {
        return MaterialApp(
          home: HomePage(),
        );
      },
    );
  }
}
// home_page.dart
class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: MyFAB()
    );
  }
}
person Sebastien Raynaud    schedule 06.03.2021