我的文档和Flutter视频对StatefulWidget
(+ (Widget)State
)设计的解释是:
从示例:
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {...}
}
但是:
setState
才能使状态无效,所以这真的是声明性设计吗? setState
,因此Flutter的(Widget)State/StatefulWidget
模式的好处是什么,我们说:class MyHomePage extends StatefulWidget // Define dirty method
{
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
int _counter = 0;
_incrementCounter() {
_counter++;
this.dirty(); // Require the view to be rebuilt. Arranges generateView to be called.
}
@override
Widget generateView(BuildContext context) {return ... rendering description containing updated counter ... ;}
}
...这将给程序员带来同样的负担,将UI标记为肮脏,并且同样具有剥夺性,并且避免了混淆程序意图的其他抽象。
我错过了什么?在Flutter中将StatefulWidget
与(Widget)State
分开有什么好处?
[在人们听取MVC评论之前,请注意,Flutter模型只显式地仅管理小部件的状态,并且通过build方法将其紧密耦合到UI的小部件-这里没有分离的关注点,并且没有关于未附加到视图的更大的应用程序状态,还有很多话要说。]
[主持人,这些问题也不尽相同:Why does Flutter State object require a Widget?,What is the relation between stateful and stateless widgets in Flutter?。我的问题是关于当前设计的好处是什么,而不是该设计的工作原理。]
更新: @RémiRousselet-这是一个声明性示例,仅需要声明一个新的状态类。通过一些工作,您甚至可以摆脱它(尽管可能不会更好)。
这种声明与需求交互的方式不需要(用户)声明两个新的循环类型引用类,并且响应状态而变化的小部件与状态分离(它构造了一个纯函数)状态,不需要分配状态。
这种做事方式无法在热重装中幸存下来。 (悲伤的脸)。 我怀疑这与热重装有关,但如果有一种方法可以使它工作,那就太好了,
import 'dart:collection';
import 'package:flutter/material.dart';
////////////////////////////////
// Define some application state
class MyAppState with ChangeSubscribeable<MyAppState> {
/***
* TODO. Automate notifyListeners on setter.
* Binds changes to the widget
*/
int _counter;
get counter => _counter;
set counter(int c) {
_counter = c;
notifyListeners(); // <<<<<< ! Calls ... .setState to invalidate widget
}
increment() {
counter = _counter + 1;
}
MyAppState({int counter: 0}) {
_counter = counter;
}
}
void main() => runApp(MyApp5());
class MyApp5 extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Declare the mutable state.
// Note because the state is not coupled to any particular widget
// its possible to easily share the state between concerned.
// StateListeningWidgets register for, and are notified on changes to
// the state.
var state = new MyAppState(counter: 5);
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
appBar: AppBar(
title: Text('Flutter Demo'),
),
body: Center(
child: Column(
children: [
// When the button is click, increment the state
RaisedButton(
onPressed: () => {
state.increment(),
print("Clicked. New state: ${state.counter}")
},
child: Text('Click me'),
),
// Listens for changes in state.
StateListeningWidget(
state,
// Construct the actual widget based on the current state
// A pure function of the state.
// However, is seems closures are not hot-reload.
(context, s) => new Text("Counter4 : ${s.counter}"),
),
],
))),
);
}
}
// //////////////////////
// Implementation
// This one is the onChange callback should accept the state.
//typedef OnChangeFunc<ARG0> = void Function(ARG0);
typedef OnChangeFunc = void Function();
mixin ChangeSubscribeable<STATE> {
final _listener2Notifier =
new LinkedHashMap<Object, OnChangeFunc>(); // VoidFunc1<STATE>>();
List<OnChangeFunc> get _listeners => List.from(_listener2Notifier.values);
void onChange(listenerKey, OnChangeFunc onChange) {
// onChange(listenerKey, VoidFunc1<STATE> onChange) {
assert(!_listener2Notifier.containsKey(listenerKey));
_listener2Notifier[listenerKey] = onChange;
print("Num listeners: ${_listener2Notifier.length}");
}
void removeOnChange(listenerKey) {
if (_listener2Notifier.containsKey(listenerKey)) {
_listener2Notifier.remove(listenerKey);
}
}
void notifyListeners() {
// _listener2Notifier.forEach((key, value)=>value(state));
// Safer, in-case state-update triggers add/remove onChange:
// Call listener
_listeners.forEach((value) => value());
}
}
typedef StateToWidgetFunction<WIDGET extends Widget,
STATE extends ChangeSubscribeable>
= WIDGET Function(BuildContext, STATE);
void noOp() {}
class _WidgetFromStateImpl<WIDGET extends Widget,
STATE extends ChangeSubscribeable> extends State<StatefulWidget> {
STATE _state;
// TODO. Make Widget return type more specific.
StateToWidgetFunction<WIDGET, STATE> stateToWidgetFunc;
_WidgetFromStateImpl(this.stateToWidgetFunc, this._state) {
updateState(){setState(() {});}
this._state.onChange(this, updateState);
}
@override
Widget build(BuildContext context) => stateToWidgetFunc(context, this._state);
@override
dispose() {
_state.removeOnChange(this);
super.dispose();
}
}
class StateListeningWidget<WIDGET extends Widget,
STATE extends ChangeSubscribeable> extends StatefulWidget {
STATE _watched_state;
StateToWidgetFunction<WIDGET, STATE> stateToWidgetFunc;
StateListeningWidget(this._watched_state, this.stateToWidgetFunc) {}
@override
State<StatefulWidget> createState() {
return new _WidgetFromStateImpl<WIDGET, STATE>(
stateToWidgetFunc, _watched_state);
}
}
我直接针对ChangeProvider模式:https://github.com/flutter/samples/blob/master/provider_counter/lib/main.dart
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Flutter Demo Home Page'),),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('You have pushed the button this many times:'),
Consumer<Counter>( // <<< Pure. Hidden magic mutable parameter
builder: (context, counter, child) => Text(
'${counter.value}',
style: Theme.of(context).textTheme.display1,
),),],),),
floatingActionButton: FloatingActionButton(
onPressed: () =>
// <<< Also a hidden magic parameter
Provider.of<Counter>(context, listen: false).increment(),
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
...但是这也会遇到问题:
尚不清楚状态要求是什么或如何提供它们-接口(至少在此github示例主页中)该示例不需要Counter作为正式参数。这里我们有new HomePage()
,其参数中未提供配置-这种类型的访问与全局变量也有类似的问题。
对状态的访问是按类类型进行的,而不是对象引用-因此,如果要两个相同类型的对象(例如,shippingAddress,billingAddress)作为对等对象,则不清楚如何(至少是直接做到)该模型。要解决此问题,可能需要重构状态模型。
答案 0 :(得分:0)
我认为与此有关。 (顺便说一句)。 不幸的是,Flutter的作者似乎在View类后缀了“ State”一词。这使整个Flutter状态管理讨论变得很混乱。
我认为这两个类的目的实际上是为了使绘画表现得更好,但是这对我们的开发人员来说是非常沉重的管道成本。
关于命名约定: 脏标志方法允许小部件画家在不了解我们状态的情况下优化其绘画,从而减轻了对两个类的需求。 generateView()也是有意义的(当然,除非您开始使用这些小部件来保存模型片段(根据Package:provider)。