android - 使用 Provider 从第二个屏幕更新 Scaffold
问题描述
我想要一个设置屏幕,我可以在其中选择要返回到第一个屏幕的颜色。
当设置屏幕关闭时,我无法更新第一个屏幕。
我正在使用 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])),
],
);
}),
],
),
),
),
);
}
}
解决方案
Navigator.push
使用起来很棘手Provider
。它会导致很多"Could not find the correct Provider above this Navigator Widget"
错误。我已经在相关问题的答案中解释了原因。
以下是您的情况的快速概述:
提供者范围
有问题的架构代码:
MaterialApp
> provider(Screen A)
> provider(Screen B)
以下解决方案中的架构:
provider(MaterialApp)
> Screen A
> Screen B
这是您的代码示例,缩短了,使用Provider
,从第 2 页更新第 1 页的背景颜色。
我在整个代码中添加了注释来解释更改。
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
// - global var removed -
// Color bgColor = Colors.yellow[100];
void main() {
runApp(ProviderApp());
}
class ProviderApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
/// Define your Provider here, above MaterialApp
return ChangeNotifierProvider(
create: (context) => ColorModel(),
child: MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
home: ScreenA()
),
);
}
}
class ScreenA extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Thanks for your help :)')),
body: Container(
constraints: BoxConstraints.expand(),
//
// color: bgColor // - global var removed -
color: Provider.of<ColorModel>(context).bgColor,
// ↑ use your Provider state-stored value here ↑
//
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text('Change background color on this screen'),
OutlinedButton(
style: OutlinedButton.styleFrom(
backgroundColor: Colors.green[600],
),
child: Text('Go Screen B', style: TextStyle(color: Colors.white)),
// Navigator.push returns a Future, must async/await to use return value
onPressed: () async {
var result = await Navigator.of(context).push(
MaterialPageRoute(builder: (context) => ScreenB()));
// note that this context is not Screen A context, but MaterialApp context
// see https://stackoverflow.com/a/66485893/2301224
print('>>> Button1-onPressed completed, result=$result');
},
),
],
),
),
);
}
}
/// This is your state object. Store your state here.
/// Create this once and use anywhere you need. Don't re-create this unless
/// you want to wipe out all state data you were holding/sharing.
class ColorModel with ChangeNotifier {
// color is the state info you want to store & share
Color bgColor = Colors.yellow[100]; // initialized to yellow
/// Update your state value and notify any interested listeners
void updateBgColor(Color newColor) {
bgColor = newColor;
notifyListeners();
}
/// - removed - replaced with updateBgColor ↑
/*void updateDisplay() {
notifyListeners();
}*/
}
class ScreenB extends StatelessWidget {
// all fields in StatelessWidgets should be final
//final int value; // this value isn't needed
final List<String> names = ['Red', 'Green', 'Blue'];
final List<Color> colors = [Colors.red[100], Colors.green[100], Colors.blue[100]];
@override
Widget build(BuildContext context) {
/// Instantiating your model & giving it to Provider to should only happen once per
/// Widget Tree that needs access to that state. e.g. MaterialApp for this solution
/// The state object & Provider below was repeated & has been commented out / removed.
/// This was wiping out any previously stored state and creating a new Provider / Inherited scope
/// to all children.
/*return MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => ColorModel()),
],
child: ,
);*/
// - end of duplicate Provider removal -
return Scaffold(
appBar: AppBar(
title: Text('Screen2'),
),
body: Container(
alignment: Alignment.center,
child: Consumer<ColorModel>(builder: (context, colorModel, child) {
return DropdownButton(
//value: value, // this value isn't needed
hint: Text("Select a color"),
onChanged: (int value) {
colorModel.updateBgColor(colors[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])),
],
);
}),
),
);
}
}
推荐阅读
- docker - 如何进行 Docker 任务调度?Windows 任务计划程序的替代选项
- javascript - 使用测试库对单击事件进行单元测试时出现 Vis-js 时间轴错误
- firebase - Android FirebaseUI Auth 重定向到网站而不是直接发送 OTP
- entity-framework - 用于连接多个表并在单行中获取逗号分隔值的 LINQ 查询
- android - Android Studio 4.1.3 (Mac Big Sur) 中的内存泄漏
- flutter - 错误在我的应用程序中不正确使用 ParentDataWidget
- javascript - 以编程方式从另一个键创建一个类型化的记录/对象
- android - com.google.api.client.googleapis.json.GoogleJsonResponseException: 403 Forbidden - YouTube Live
- magento - 在管理员上从 Magento 2.3.3 升级到 Magento 2.4.2 后页面构建器错误
- arrays - 在 React.js 中更新 useState 数组的特定变量(按其位置)?