使用 Provider 从第二个屏幕更新 Scaffold

use*_*629 0 android flutter flutter-provider

我想要一个“设置”屏幕,我可以在其中选择要返回到第一个屏幕的颜色。

当“设置”屏幕关闭时,我无法更新第一个屏幕。

我使用提供程序作为更改通知程序。但我看不到如何触发第一个屏幕的更新。第三个按钮创建一个更新屏幕的事件,但这可以自动完成吗?

我错过了什么...?

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

void main() => runApp(MyApp());
Color bgColor = Colors.yellow[100];

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(home: MyHomeScreen());
  }
}

class MyHomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (context) => ColorModel()),
      ],
      child: Consumer<ColorModel>(builder: (context, colorModel, child) {
        return Scaffold(
          appBar: AppBar(title: Text('Thanks for your help :)')),
          body: Container(
            constraints: BoxConstraints.expand(),
            color: bgColor,
            child: Column(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                Text('Change background color on this screen'),

                OutlinedButton(
                  style: OutlinedButton.styleFrom(
                    backgroundColor: Colors.green[600],
                  ),
                  child:
                  Text('Button1', style: TextStyle(color: Colors.white)),
                  onPressed: () {
                    var result = Navigator.push(
                        context, MaterialPageRoute(builder: (context) => Screen2()));
                    print('>>> Button1-onPressed completed, result=$result');
                  },
                ),


                OutlinedButton(
                  style: OutlinedButton.styleFrom(
                    backgroundColor: Colors.green[600],
                  ),
                  child:
                  Text('Choose a colour', style: TextStyle(color: Colors.white)),
                  onPressed: () {
                    asyncButton(context);
                    print('>>> Screen1 Button-onPressed completed');
                  },
                ),

                OutlinedButton(
                  style: OutlinedButton.styleFrom(
                    backgroundColor: Colors.green[600],
                  ),
                  child:
                  Text('Now try me', style: TextStyle(color: Colors.white)),
                  onPressed: () {
                    colorModel.notifyListeners();
                  },
                ),
              ],
            ),
          ),
        );
      }),
    );
  }

  void asyncButton(BuildContext context) async {
    var result = await Navigator.push(
        context, MaterialPageRoute(builder: (context) => Screen2()));
    print('>>> asyncButton completed: result = $result');
    bgColor = result;
  }
}

class ColorModel with ChangeNotifier {
  void updateDisplay() {
    notifyListeners();
  }
}

class Screen2 extends StatelessWidget {
  int _value;
  List<String> names = ['Red', 'Green', 'Blue'];
  List<Color> colors = [Colors.red[100], Colors.green[100], Colors.blue[100]];

  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (context) => ColorModel()),
      ],
      child: Scaffold(
        appBar: AppBar(
          toolbarHeight: 80,
          backgroundColor: Colors.blue,
          title: Center(child: Text('Screen2')),
        ),
        body: Container(
          constraints: BoxConstraints.expand(),
          color: Colors.white,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: [
              Consumer<ColorModel>(builder: (context, colorModel, child) {
                return DropdownButton(
                  value: _value,
                  hint: Text("Select a color"),
                  focusColor: Colors.lightBlue,
                  onChanged: (int value) {
                    Navigator.pop(context, colors[value]);
                  },
                  items: [
                    DropdownMenuItem(value: 0, child: Text(names[0])),
                    DropdownMenuItem(value: 1, child: Text(names[1])),
                    DropdownMenuItem(value: 2, child: Text(names[2])),
                  ],
                );
              }),
            ],
          ),
        ),
      ),
    );
  }
}

Run Code Online (Sandbox Code Playgroud)

Bak*_*ker 7

Navigator.push与 一起使用很棘手Provider。它会导致很多"Could not find the correct Provider above this Navigator Widget"错误。我已经在相关问题的回答中解释了原因。

\n

以下是您情况的快速概述:

\n

提供商范围

\n

问题代码中的架构:

\n
MaterialApp\n > provider(Screen A)\n > provider(Screen B)\n
Run Code Online (Sandbox Code Playgroud)\n

解决方案中的架构如下:

\n
provider(MaterialApp)\n > Screen A\n > Screen B\n
Run Code Online (Sandbox Code Playgroud)\n

这是您的代码示例,已缩短,使用Provider,从第 2 页更新第 1 页的背景颜色。

\n

我在整个代码中添加了注释来解释更改。

\n
import \'package:flutter/material.dart\';\nimport \'package:provider/provider.dart\';\n\n// - global var removed -\n// Color bgColor = Colors.yellow[100];\n\nvoid main() {\n  runApp(ProviderApp());\n}\n\nclass ProviderApp extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    /// Define your Provider here, above MaterialApp\n    return ChangeNotifierProvider(\n      create: (context) => ColorModel(),\n      child: MaterialApp(\n          title: \'Flutter Demo\',\n          debugShowCheckedModeBanner: false,\n        home: ScreenA()\n      ),\n    );\n  }\n}\n\nclass ScreenA extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(title: Text(\'Thanks for your help :)\')),\n      body: Container(\n        constraints: BoxConstraints.expand(),\n        //\n        // color: bgColor // - global var removed -\n        color: Provider.of<ColorModel>(context).bgColor,\n        // \xe2\x86\x91 use your Provider state-stored value here \xe2\x86\x91\n        //\n        child: Column(\n          mainAxisAlignment: MainAxisAlignment.spaceEvenly,\n          children: [\n            Text(\'Change background color on this screen\'),\n            OutlinedButton(\n              style: OutlinedButton.styleFrom(\n                backgroundColor: Colors.green[600],\n              ),\n              child: Text(\'Go Screen B\', style: TextStyle(color: Colors.white)),\n              // Navigator.push returns a Future, must async/await to use return value\n              onPressed: () async {\n                var result = await Navigator.of(context).push(\n                    MaterialPageRoute(builder: (context) => ScreenB()));\n                // note that this context is not Screen A context, but MaterialApp context\n                // see /sf/answers/4654012541/\n                print(\'>>> Button1-onPressed completed, result=$result\');\n              },\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n}\n\n/// This is your state object. Store your state here.\n/// Create this once and use anywhere you need.  Don\'t re-create this unless\n/// you want to wipe out all state data you were holding/sharing.\nclass ColorModel with ChangeNotifier {\n  // color is the state info you want to store & share\n  Color bgColor = Colors.yellow[100]; // initialized to yellow\n\n  /// Update your state value and notify any interested listeners\n  void updateBgColor(Color newColor) {\n    bgColor = newColor;\n    notifyListeners();\n  }\n\n  /// - removed - replaced with updateBgColor \xe2\x86\x91\n  /*void updateDisplay() {\n    notifyListeners();\n  }*/\n}\n\nclass ScreenB extends StatelessWidget {\n  // all fields in StatelessWidgets should be final\n  //final int value;  // this value isn\'t needed\n  final List<String> names = [\'Red\', \'Green\', \'Blue\'];\n  final List<Color> colors = [Colors.red[100], Colors.green[100], Colors.blue[100]];\n\n  @override\n  Widget build(BuildContext context) {\n    /// Instantiating your model & giving it to Provider to should only happen once per\n    /// Widget Tree that needs access to that state. e.g. MaterialApp for this solution\n    /// The state object & Provider below was repeated & has been commented out / removed.\n    /// This was wiping out any previously stored state and creating a new Provider / Inherited scope\n    /// to all children.\n    /*return MultiProvider(\n      providers: [\n        ChangeNotifierProvider(create: (context) => ColorModel()),\n      ],\n      child: ,\n    );*/\n    // - end of duplicate Provider removal -\n    return Scaffold(\n      appBar: AppBar(\n        title: Text(\'Screen2\'),\n      ),\n      body: Container(\n        alignment: Alignment.center,\n        child: Consumer<ColorModel>(builder: (context, colorModel, child) {\n          return DropdownButton(\n            //value: value, // this value isn\'t needed\n            hint: Text("Select a color"),\n            onChanged: (int value) {\n              colorModel.updateBgColor(colors[value]);\n              Navigator.pop(context, colors[value]);\n            },\n            items: [\n              DropdownMenuItem(value: 0, child: Text(names[0])),\n              DropdownMenuItem(value: 1, child: Text(names[1])),\n              DropdownMenuItem(value: 2, child: Text(names[2])),\n            ],\n          );\n        }),\n      ),\n    );\n  }\n}\n\n
Run Code Online (Sandbox Code Playgroud)\n