Flutter:无法从showModalBottomSheet访问提供者

问题描述

我无法通过FloatingActionButton中的showModalBottomSheet访问在脚手架上方定义的提供程序。

我已经这样定义了主页:

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

错误:在此BottomSheet小部件上方找不到正确的提供者

解决方法

这是由于传递了错误的上下文引起的。将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)
              };
            )
          ],);
      }
    );
  }
}
,

解决方案

首页:

class HomePage extends StatelessWidget {
 @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (_) => MyProvider(),// 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);
            )
          ],);
      }
    );
  }
}
,

通过将提供者置于 MaterialApp 之上来修复,如 here 所述。

在材质应用的根目录下创建底部工作表。如果在材料应用程序下声明了一个提供者,则底部工作表无法访问它,因为提供者不是小部件树中底部工作表的祖先。

下面的屏幕截图显示了一个小部件树:整个应用程序在 Wrapper 内,底部工作表不是在 Wrapper 内创建的。它被创建为 MaterialApp 的另一个子元素(在本例中具有根元素 Container)。

widget tree

对于您的情况:

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