我试图理解在Widgets State之外控制StatefulWidget状态的最佳实践.
我定义了以下接口.
abstract class StartupView {
Stream<String> get onAppSelected;
set showActivity(bool activity);
set message(String message);
}
Run Code Online (Sandbox Code Playgroud)
我想创建一个StartupPage实现此接口的StatefulWidget .我希望Widget能够执行以下操作:
按下按钮时,它将通过onAppSelected流发送事件.控制器甚至会监听它并执行一些操作(db调用,服务请求等).
控制器可以调用showActivity或set message使视图显示消息进度.
因为有状态窗口小部件不会将其状态作为属性公开,所以我不知道访问和修改状态属性的最佳方法.
我希望使用它的方式如下:
Widget createStartupPage() {
var page = new StartupPage();
page.onAppSelected.listen((app) {
page.showActivity = true;
//Do some work
page.showActivity = false;
});
}
Run Code Online (Sandbox Code Playgroud)
我已经考虑通过传递我希望它返回的状态来实例化Widget createState()但是感觉不对.
关于我们为什么采用这种方法的一些背景:我们目前有一个Dart Web应用程序.对于视图 - 控制器分离,可测试性和对Flutter的前瞻性思考,我们决定为应用程序中的每个视图创建一个接口.这将允许WebComponent或Flutter Widget实现此接口并使所有控制器逻辑保持不变.
Rém*_*let 51
有多种方法可以与其他有状态小部件进行交互.
1. ancestorStateOfType
第一个也是最直接的是通过context.ancestorStateOfType方法.
通常包含在Stateful子类的静态方法中,如下所示:
class MyState extends StatefulWidget {
static of(BuildContext context, {bool root = false}) => root
? context.rootAncestorStateOfType(const TypeMatcher<_MyStateState>())
: context.ancestorStateOfType(const TypeMatcher<_MyStateState>());
@override
_MyStateState createState() => _MyStateState();
}
class _MyStateState extends State<MyState> {
@override
Widget build(BuildContext context) {
return Container();
}
}
Run Code Online (Sandbox Code Playgroud)
这是如何Navigator工作的例子.
优点:
缺点:
State属性或手动调用setStateState子类要访问变量时,请勿使用此方法.当变量发生变化时,您的窗口小部件可能无法重新加载
2.可监听,流和/或InheritedWidget
有时候您可能想要访问某些属性而不是方法.问题是,当该值随时间变化时,您很可能希望您的小部件更新.
在这种情况下,飞镖报价Stream和Sink.而扑在它的顶部增加InheritedWidget 和Listenable如ValueNotifier.它们都做了相对相同的事情:与StreamBuilder/ context.inheritFromWidgetOfExactType/ 一起订阅价值变化事件AnimatedBuilder.
当您希望State公开某些属性时,这是首选解决方案.我不会涵盖所有可能性,但这是一个小例子使用InheritedWidget:
首先,我们有一个InheritedWidget揭示count:
class Count extends InheritedWidget {
static of(BuildContext context) =>
context.inheritFromWidgetOfExactType(Count);
final int count;
Count({Key key, @required Widget child, @required this.count})
: assert(count != null),
super(key: key, child: child);
@override
bool updateShouldNotify(Count oldWidget) {
return this.count != oldWidget.count;
}
}
Run Code Online (Sandbox Code Playgroud)
然后我们State将实例化它InheritedWidget
class _MyStateState extends State<MyState> {
int count = 0;
@override
Widget build(BuildContext context) {
return Count(
count: count,
child: Scaffold(
body: CountBody(),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
count++;
});
},
),
),
);
}
}
Run Code Online (Sandbox Code Playgroud)
最后,我们得到了CountBody这个暴露的count
class CountBody extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Text(Count.of(context).count.toString()),
);
}
}
Run Code Online (Sandbox Code Playgroud)
优点:
ancestorStateOfTypeawait foror async*)缺点:
3.通知
State您可以Notification从窗口小部件发送a,而不是直接调用方法.并State订阅这些通知.
一个例子Notification是:
class MyNotification extends Notification {
final String title;
const MyNotification({this.title});
}
Run Code Online (Sandbox Code Playgroud)
要发送通知,只需调用dispatch(context)您的通知实例,它就会冒出来.
MyNotification(title: "Foo")..dispatch(context)
Run Code Online (Sandbox Code Playgroud)
任何给定的窗口小部件都可以使用以下命令监听其子项发送的通知NotificationListener<T>:
class _MyStateState extends State<MyState> {
@override
Widget build(BuildContext context) {
return NotificationListener<MyNotification>(
onNotification: onTitlePush,
child: Container(),
);
}
bool onTitlePush(MyNotification notification) {
print("New item ${notification.title}");
// true meaning processed, no following notification bubbling.
return true;
}
}
Run Code Online (Sandbox Code Playgroud)
一个例子是Scrollable,可以发送ScrollNotification包括开始/结束/过度滚动.然后用来Scrollbar知道滚动信息而无法访问ScrollController
优点:
State.这State是订阅由其子女触发的事件State属性缺点:
xqw*_*zts 21
您可以使用静态方法公开状态的窗口小部件,一些颤动的示例以这种方式执行,我也开始使用它:
class StartupPage extends StatefulWidget {
static _StartupPageState of(BuildContext context) => context.ancestorStateOfType(const TypeMatcher<_StartupPageState>());
@override
_StartupPageState createState() => new _StartupPageState();
}
class _StartupPageState extends State<StartupPage> {
...
}
Run Code Online (Sandbox Code Playgroud)
然后,您可以通过呼叫访问该状态StartupPage.of(context).doSomething();.
这里需要注意的是,您需要在其树中的某个位置具有BuildContext.
Bam*_*oUA 21
还有另一种常用方法可以访问 State 的属性/方法:
class StartupPage extends StatefulWidget {
StartupPage({Key key}) : super(key: key);
@override
StartupPageState createState() => StartupPageState();
}
// Make class public!
class StartupPageState extends State<StartupPage> {
int someStateProperty;
void someStateMethod() {}
}
// Somewhere where inside class where `StartupPage` will be used
final startupPageKey = GlobalKey<StartupPageState>();
// Somewhere where the `StartupPage` will be opened
final startupPage = StartupPage(key: startupPageKey);
Navigator.push(context, MaterialPageRoute(builder: (_) => startupPage);
// Somewhere where you need have access to state
startupPageKey.currentState.someStateProperty = 1;
startupPageKey.currentState.someStateMethod();
Run Code Online (Sandbox Code Playgroud)
我愿意:
class StartupPage extends StatefulWidget {
StartupPageState state;
@override
StartupPageState createState() {
this.state = new StartupPageState();
return this.state;
}
}
class DetectedAnimationState extends State<DetectedAnimation> {
Run Code Online (Sandbox Code Playgroud)
而外面只是 startupPage.state
| 归档时间: |
|
| 查看次数: |
12696 次 |
| 最近记录: |