【问题标题】:How to pass provider with Navigator?如何通过导航器传递提供者?
【发布时间】:2019-12-27 02:39:16
【问题描述】:

我需要将提供者 (Model2) 从 homepage 传递到 Page2,所以当用户返回 homepage (onWillPop) 时,我可以从提供者 (Model2) 进行 API 调用并更新 @987654327 @。

但是当我调用_onPaidBackPress(context) 时出现错误:

未处理的异常:错误:找不到正确的 此 Consumer Widget 上方的提供者

StatefulWidget 在主页:

@override
  Widget build(BuildContext context) {
return ChangeNotifierProxyProvider<Model1, Model2>(
initialBuilder: (_) => Model2(),
  builder: (_, model1, model2) => model2
    ..string = model1.string,
),
  child: Consumer<Model2>(
    builder: (context, model2, _) =>

...
                        await Navigator.push(
                            context,
                            new MaterialPageRoute(
                                builder: (BuildContext context) =>
                                    new Page2(
                                        context: context)));

在第2页:

class Page2 extends StatefulWidget {

  final BuildContext context;

  Page2({Key key, this.context}) : super(key: key);

  @override
  State createState() => new Page2State(context: context);
}

class Page2State extends State<Page2> {

  final context;

  ChatScreenState({Key key, this.context});

@override
  Widget build(BuildContext context) {

    return Consumer<Model1>(
      builder: (context, model, _) {

        return new WillPopScope(
              onWillPop: () => model.isPaid ? _onPaidBackPress(context) : _onBackPressed(context),




Future<void> _onPaidBackPress(context) async {


final model2 = Provider.of<Model2>(context, listen: false);

  return showDialog<void>(
    context: context,
    barrierDismissible: false, 
    builder: (BuildContext context) {
      return 


    Provider.value(value: model2, child:


AlertDialog(
        title: Text('Back'),
        content: SingleChildScrollView(
          child: ListBody(
            children: <Widget>[
              Text('Go back'),
            ],
          ),
        ),
        actions: <Widget>[
          FlatButton(
            child: Text('OK'),
            onPressed: () async {

await model2.getData();

              Navigator.of(context).pop();
            },
          ),
        ],
),
      );
    },
  );
}

感谢您的帮助!

【问题讨论】:

    标签: flutter dart flutter-provider


    【解决方案1】:

    您可以通过在导航到新页面时创建新的提供程序来轻松地在导航器中传递提供程序。

     Navigator.of(context).push(
          MaterialPageRoute(
            builder: (BuildContext context) => Provider(
              create: (context) => InventoryItem(),
              builder: (context, child) => ProductDetails(),
            ),
          ),
        );
    

    这里要么创建一个新对象InventoryItem(),要么传递现有的提供者对象。

    【讨论】:

    • 这似乎是最简单的解决方案。真的很好的答案!谢谢?
    【解决方案2】:

    您需要使 Provider 出现在 Navigator Widget 上方,以成为当前 Widget 的锚。这意味着,它必须位于 MaterialApp()CupertinoApp() 或您可能使用的任何 Directionality() 之上。

    有关 InheritedWidget 的更多信息,请参阅以下链接。

    https://medium.com/@mehmetf_71205/inheriting-widgets-b7ac56dbbeb1

    https://www.youtube.com/watch?v=Zbm3hjPjQMk

    【讨论】:

    • 您的回答自相矛盾:provider InheritedWidget。
    • @RémiRousselet 您对此问题的解决方案是什么?
    • @RémiRousselet 你是对的,我在 Provider.value 是不可变的并且值将在运行时实例化的上下文中使用它,因此将它放在材料应用程序之上是没有意义的。我编辑了答案以消除混淆。
    • @DanielSzy 感谢您的回复!但我认为这不正确。你的解决方案是可能的。但是You don’t want to place ChangeNotifierProvider higher than necessary (because you don’t want to pollute the scope)。所以对我来说放低点比较好。 @RémiRousselet 说在Navigator 中可以通过providercontext。我正在寻找如何做到这一点。
    • 如果您找到解决方案,请在此处发布您的答案。我试着这样做了 3 个小时,但发现它非常混乱,而且它也会是可变的,对吧?我们不想手动处理随上下文传递的实例。这基本上会破坏提供者为我们开发人员增加的核心价值,不是吗?
    【解决方案3】:

    我有点晚了,但我找到了一个解决方案,关于如何在Navigator.push() 之后保持Provider 的值,而不必将Provider 放在MaterialApp 上方。

    为此,我使用了库custom_navigator。它允许您在树中的任何位置创建Navigator

    您将必须创建 2 个不同的 GlobalKey&lt;NavigatorState&gt;,并将它们提供给 MaterialAppCustomNavigator 小部件。这些键将允许您控制要使用的导航器。

    这里有一个小sn-p来说明怎么做

    class App extends StatelessWidget {
    
       GlobalKey<NavigatorState> _mainNavigatorKey = GlobalKey<NavigatorState>(); // You need to create this key for the MaterialApp too
    
       @override
       Widget build(BuildContext context) {
          return MaterialApp(
             navigatorKey: _mainNavigatorKey;  // Give the main key to the MaterialApp
             home: Provider<bool>.value(
                value: myProviderFunction(),
                child: Home(),
             ),
          );
       }
    
    }
    
    class Home extends StatelessWidget {
    
       GlobalKey<NavigatorState> _navigatorKey = GlobalKey<NavigatorState>(); // You need to create this key to control what navigator you want to use
    
       @override
       Widget build(BuildContext context) {
    
          final bool myBool = Provider.of<bool>(context);
    
          return CustomNavigator (
             // CustomNavigator is from the library 'custom_navigator'
             navigatorKey: _navigatorKey,  // Give the second key to your CustomNavigator
             pageRoute: PageRoutes.materialPageRoute,
             home: Scaffold(
                body: FlatButton(
                   child: Text('Push'),
                   onPressed: () {
                      _navigatorKey.currentState.push(  // <- Where the magic happens
                         MaterialPageRoute(
                            builder: (context) => SecondHome(),
                         ),
                      },
                   ),
                ),
             ),
          );   
       }
    }
    
    class SecondHome extends StatelessWidget {
    
       @override
       Widget build(BuildContext context) {
    
          final bool myBool = Provider.of<bool>(context);
    
          return Scaffold(
             body: FlatButton(
                child: Text('Pop'),
                onPressed: () {
                   Novigator.pop(context);
                },
             ),
          );
       }
    
    }
    

    在这里,您可以从Home 小部件中的Provider 读取值myBool,即使在Navigator.push() 之后也可以在SecondHome 小部件中读取值。

    但是,Android 的back 按钮会从MaterialApp 的导航器中触发Navigator.pop()。如果你想使用CustomNavigator的那个,你可以这样做:

    // In the Home Widget insert this
       ...
       @override
       Widget build(BuildContext context) {
          return WillPopScope(
             onWillPop: () async {
                if (_navigatorKey.currentState.canPop()) {
                   _navigatorKey.currentState.pop();  // Use the custom navigator when available
                   return false;  // Don't pop the main navigator
                } else {
                   return true;  // There is nothing to pop in the custom navigator anymore, use the main one
                }
             },
             child: CustomNavigator(...),
          );
       }
       ...
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-11-23
      • 1970-01-01
      • 2020-08-23
      • 1970-01-01
      • 1970-01-01
      • 2022-08-14
      相关资源
      最近更新 更多