我正在使用提供程序包来管理Flutter应用程序中的状态。我开始嵌套对象时遇到问题。
一个非常简单的示例:父A的子类型为B,子的类型为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()小部件)包含以下内容:
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)。上面的代码经过了极大简化,但确实显示了我遇到的问题。
(A -> B -> C -> D.color)
回到重点 :如何将 子D 的color属性分配给小部件,以便在更新 子D 的属性时,它也自动更新小部件(使用notifyListeners(),而不是setState())。
notifyListeners()
setState()
我使用了 Stateless , Stateful , Provider.of 和 Consumer ,所有这些都给我相同的结果。重申一下,对象不能分离,它必须具有父子关系。
编辑
更复杂的示例:
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示例的意思是试图简化情况的复杂性。
A
D
假设您要在小部件中显示和/或更改机组人员的姓名。在该应用本身您通常选择Aircraft从Fleet(通过传递对窗口小部件List的索引),然后选择CrewMember从Aircraft(由传递Map密钥),然后显示/改变Name的CrewMember。
Aircraft
Fleet
List
CrewMember
Map
Name
最后,您的小部件将能够使用传入的Aircrafts索引和CrewMembers键来查看您所指的乘员组成员的名字。
Aircrafts
CrewMembers
我绝对愿意接受更好的架构和设计。
编辑:对更新的问题的答案,以下为原文
目前还不清楚是什么A,B,C和D在你原来的问题主张。原来那是 模特 。
B
C
我目前的想法是,用MultiProvider/ 包装您的应用程序 ProxyProvider以提供 服务 ,而不是模型。
MultiProvider
ProxyProvider
不知道如何加载数据(如果有的话),但是我假定了一种异步获取船队的服务。如果您的数据是通过零件/模型通过不同的服务加载的(而不是一次全部)加载的,则可以将其添加到中,MultiProvider并在需要加载更多数据时将其插入适当的小部件中。
下面的示例是完整的功能。为了简单起见,并且由于您以更新name为例进行了询问,所以我仅设置了该属性设置器notifyListeners()。
name
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秒钟以加载数据,然后您将在该地图中看到ID为1的机组人员“约翰”。然后双击文本,它应该更新为“ Peter”。
如您所见,我正在使用服务的顶级注册(Provider.value(value: Service()))和模型的本地注册(ChangeNotifierProvider<CrewMember>(create: ...))。
Provider.value(value: Service())
ChangeNotifierProvider<CrewMember>(create: ...)
我认为这种架构(具有合理数量的模型)应该是可行的。
关于本地级别的提供程序,我觉得它有点冗长,但是可能有一些方法可以使它更简短。同样,为模型提供一些代码生成库,这些模型具有用setter通知更改的功能。
(您有C#背景吗?我已将您的类固定为符合Dart语法。)
让我知道这是否适合您。
如果要使用提供程序,则必须使用提供程序构建依赖关系图。
(您可以选择构造函数注入,而不是setter注入)
这有效:
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()。
ChangeColor()
无需在此之上使用有状态的小部件。