Flutter:无法从showModalBottomSheet访问提供程序

7ajki6be  于 2023-02-20  发布在  Flutter
关注(0)|答案(4)|浏览(209)

我无法从FloatingActionButton中的showModalBottomSheet访问在Scaffold之上定义的提供程序。
我定义了一个HomePage,如下所示:

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

这是我的FAB

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小部件上找到正确的提供程序〈MyProvider。

vjhs03f7

vjhs03f71#

通过将提供程序置于MaterialApp之上修复,如下所述。
底部表单创建在材质应用的根目录下。如果在材质应用下声明了prodiver,则底部表单无法访问它,因为提供程序不是小部件树中底部表单的祖先。
下面的屏幕截图显示了小部件树:整个应用程序都在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()
    );
  }
}
wydwbb8l

wydwbb8l2#

这是由于传递了错误的上下文。将您的FAB Package 到一个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)
              };
            )
          ],
        );
      }
    );
  }
}
z8dt9xmd

z8dt9xmd3#

溶液
主页:

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

我的FAB:

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

whlutmcx4#

在我看来:showModalBottomSheet使用来自Material App 1st image的上下文构建底部表单,因此当我们返回任何Widget以在底部表单中显示时,它将使用该Material应用上下文,如:1st图像中的builder属性所示。
2ng Image: your code因此,在代码中,当您编写:(上下文的提供程序,侦听:返回值();它正在使用生成器中的上下文:(BuildContext上下文),这是Material App的上下文。我们必须更改此上下文,以便使用此提供程序,而不必将提供程序的位置提升到Material App之上。
现在,如果我们希望继续使用该上下文来获得覆盖和自动检测合适主题的好处,并且仍然希望使用可以访问我们的提供程序的小部件的上下文:
我们可以将具有提供商访问权限的微件的上下文传递到FAB,但是我们将不得不通过微件持续传递该上下文,直到我们需要在我们的FAB中使用该提供商或者直到我们转到不同的路线:在这种情况下,我们可以从新的上下文和提供者开始,因为提供者的作用域是成熟的。
因此,在HomePage中,您可以将scaffold Package 在Builder中,也可以创建一个新小部件,如下所示:“3rd image
这样它就有了自己的上下文,可以访问我们在FAB中需要的提供程序,如下图4所示:4th image
然后在showModalBottomSheet的builder属性中更改匿名函数中参数的名称,以便它不会与MAmaterial App上下文和我们将要传入的上下文混淆(在我的案例图片4中为Builder上下文或IdeaScreen上下文)
5th image
我正在创建一个新的小部件,但你不需要这样做,你可以直接写你的Fab代码内的匿名函数:并且可以在调用Provider时使用上下文(而不是与Material App上下文相关的newContext),就像您已经在做的那样。但是,如果有人的用例与我的用例类似,我将在我的用例中显示我在AddTask小部件中正在做什么:
6th image需要一个上下文,它确实具有提供者访问权限,在我的情况下,它是IdeaScreen的上下文。
然后像这样使用它:7th image

相关问题