我正在使用 Provider Package在我的 Flutter App 中管理状态。当我开始嵌套我的对象时,我遇到了问题。
一个非常简单的例子:父 A 有 B 类型的子,B 有 C 类型的子,C 有 D 类型的子。在子 D 中,我想管理一个颜色属性。下面的代码示例:
import 'package:flutter/material.dart';
class A with ChangeNotifier
{
A() {_b = B();}
B _b;
B get b => _b;
set b(B value)
{
_b = value;
notifyListeners();
}
}
class B with ChangeNotifier
{
B() {_c = C();}
C _c;
C get c => _c;
set c(C value)
{
_c = value;
notifyListeners();
}
}
class C with ChangeNotifier
{
C() {_d = D();}
D _d;
D get d => _d;
set d(D value)
{
_d = value;
notifyListeners();
}
}
class D with ChangeNotifier
{
int _ColorIndex = 0;
final List<Color> _ColorList = [
Colors.black,
Colors.blue,
Colors.green,
Colors.purpleAccent
];
D()
{
_color = Colors.red;
}
void ChangeColor()
{
if(_ColorIndex < _ColorList.length - 1)
{
_ColorIndex++;
}
else
{
_ColorIndex = 0;
}
color = _ColorList[_ColorIndex];
}
Color _color;
Color get color => _color;
set color(Color value)
{
_color = value;
notifyListeners();
}
}
现在我的 main.dart (管理我的
Placeholder()
小部件)包含以下内容:import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:provider_example/NestedObjects.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget
{
@override
Widget build(BuildContext context)
{
return MaterialApp(
home: ChangeNotifierProvider<A>(
builder: (context) => A(),
child: MyHomePage()
),
);
}
}
class MyHomePage extends StatefulWidget
{
@override
State createState()
{
return _MyHomePageState();
}
}
class _MyHomePageState extends State<MyHomePage>
{
@override
Widget build(BuildContext context)
{
A a = Provider.of<A>(context);
B b = a.b;
C c = b.c;
D d = c.d;
return Scaffold(
body: Center(
child: Column(
children: <Widget>[
Text(
'Current selected Color',
),
Placeholder(color: d.color,),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => ButtonPressed(context),
tooltip: 'Increment',
child: Icon(Icons.arrow_forward),
),
);
}
void ButtonPressed(BuildContext aContext)
{
A a = Provider.of<A>(context);
B b = a.b;
C c = b.c;
D d = c.d;
d.ChangeColor();
}
}
以上显示占位符小工具 的颜色属性由 定义D类的颜色属性
(A -> B -> C -> D.color)
.上面的代码非常简化,但它确实显示了我遇到的问题。回到正题 :如何将子 D 的颜色属性分配给小部件,以便在更新子 D 的属性时,它也会自动更新小部件(使用
notifyListeners()
,而不是 setState()
)。我用过无国籍 , 有状态的 , 提供者.of 和 消费者 ,所有这些都给了我相同的结果。重申一下,对象不能解耦,它必须具有父子关系。
编辑
更复杂的例子:
import 'dart:ui';
enum Manufacturer
{
Airbus, Boeing, Embraer;
}
class Fleet
{
List<Aircraft> Aircrafts;
}
class Aircraft
{
Manufacturer AircraftManufacturer;
double EmptyWeight;
double Length;
List<Seat> Seats;
Map<int,CrewMember> CrewMembers;
}
class CrewMember
{
String Name;
String Surname;
}
class Seat
{
int Row;
Color SeatColor;
}
上面的代码是真实世界示例的简化版本。正如你可以想象的那样,兔子洞可以越来越深。那么,我所说的
A
是什么意思?通过D
例如试图简化情况的卷积。例如,假设您想在小部件中显示和/或更改机组成员的姓名。在应用程序本身中,您通常会选择
Aircraft
来自 Fleet
(通过 List
索引传递给小部件),然后选择 CrewMember
来自 Aircraft
(通过 Map
键)然后显示/更改 Name
的 CrewMember
.最后,您的小部件将能够通过使用传入的
Aircrafts
来查看您所指的船员姓名。索引和 CrewMembers
key 。我绝对愿意接受更好的架构和设计。
最佳答案
编辑:回答更新后的问题,原文如下
不清楚是什么A
, B
, C
和 D
代表你原来的问题。原来那些是模型。
我目前的想法是,用 MultiProvider
包装你的应用程序/ProxyProvider
提供服务,而不是模型。
不确定您是如何加载数据的(如果有的话),但我假设一个服务可以异步获取您的车队。如果您的数据由零件/模型通过不同的服务加载(而不是一次全部加载),您可以将它们添加到 MultiProvider
并在您需要加载更多数据时将它们注入(inject)适当的小部件中。
下面的示例功能齐全。为简单起见,既然您询问了有关更新 name
的问题举个例子,我只做了那个属性 setter notifyListeners()
.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
main() {
runApp(
MultiProvider(
providers: [Provider.value(value: Service())],
child: MyApp()
)
);
}
class MyApp extends StatelessWidget {
@override
Widget build(context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Consumer<Service>(
builder: (context, service, _) {
return FutureBuilder<Fleet>(
future: service.getFleet(), // might want to memoize this future
builder: (context, snapshot) {
if (snapshot.hasData) {
final member = snapshot.data.aircrafts[0].crewMembers[1];
return ShowCrewWidget(member);
} else {
return CircularProgressIndicator();
}
}
);
}
),
),
),
);
}
}
class ShowCrewWidget extends StatelessWidget {
ShowCrewWidget(this._member);
final CrewMember _member;
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<CrewMember>(
create: (_) => _member,
child: Consumer<CrewMember>(
builder: (_, model, __) {
return GestureDetector(
onDoubleTap: () => model.name = 'Peter',
child: Text(model.name)
);
},
),
);
}
}
enum Manufacturer {
Airbus, Boeing, Embraer
}
class Fleet extends ChangeNotifier {
List<Aircraft> aircrafts = [];
}
class Aircraft extends ChangeNotifier {
Manufacturer aircraftManufacturer;
double emptyWeight;
double length;
List<Seat> seats;
Map<int,CrewMember> crewMembers;
}
class CrewMember extends ChangeNotifier {
CrewMember(this._name);
String _name;
String surname;
String get name => _name;
set name(String value) {
_name = value;
notifyListeners();
}
}
class Seat extends ChangeNotifier {
int row;
Color seatColor;
}
class Service {
Future<Fleet> getFleet() {
final c1 = CrewMember('Mary');
final c2 = CrewMember('John');
final a1 = Aircraft()..crewMembers = { 0: c1, 1: c2 };
final f1 = Fleet()..aircrafts.add(a1);
return Future.delayed(Duration(seconds: 2), () => f1);
}
}
运行应用程序,等待 2 秒以加载数据,您应该会在该 map 中看到 id=1 的机组成员“John”。然后双击文本,它应该更新为“Peter”。
如您所见,我使用的是服务的顶级注册(
Provider.value(value: Service())
)和模型的本地级别注册( ChangeNotifierProvider<CrewMember>(create: ...)
)。我认为这种架构(具有合理数量的模型)应该是可行的。
关于本地级别的提供者,我觉得它有点冗长,但可能有办法让它更短。此外,拥有一些用于带有 setter 的模型的代码生成库来通知更改会很棒。
(你有 C# 背景吗?我修复了你的类以符合 Dart 语法。)
让我知道这是否适合您。
如果要使用 Provider,则必须使用 Provider 构建依赖关系图。
(你可以选择构造函数注入(inject),而不是 setter 注入(inject))
这有效:
main() {
runApp(MultiProvider(
providers: [
ChangeNotifierProvider<D>(create: (_) => D()),
ChangeNotifierProxyProvider<D, C>(
create: (_) => C(),
update: (_, d, c) => c..d=d
),
ChangeNotifierProxyProvider<C, B>(
create: (_) => B(),
update: (_, c, b) => b..c=c
),
ChangeNotifierProxyProvider<B, A>(
create: (_) => A(),
update: (_, b, a) => a..b=b
),
],
child: MyApp(),
));
}
class MyApp extends StatelessWidget {
@override
Widget build(context) {
return MaterialApp(
title: 'My Flutter App',
home: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Current selected Color',
),
Consumer<D>(
builder: (context, d, _) => Placeholder(color: d.color)
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => Provider.of<D>(context, listen: false).color = Colors.black,
tooltip: 'Increment',
child: Icon(Icons.arrow_forward),
),
),
);
}
}
此应用程序基于您的
A
工作, B
, C
和 D
类。您的示例不使用代理,因为它只使用
D
它没有依赖关系。但是您可以看到 Provider 已通过此示例正确连接了依赖项:Consumer<A>(
builder: (context, a, _) => Text(a.b.c.d.runtimeType.toString())
),
它将打印出“D”。
ChangeColor()
没有工作,因为它没有调用 notifyListeners()
.无需在此之上使用有状态小部件。
关于Flutter Provider 嵌套对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59375130/