flutter - 从 StatefulWidget 外部控制状态

我正在尝试了解在该 Widget 状态之外控制 StatefulWidget 状态的最佳实践。

我定义了以下接口(interface)。

abstract class StartupView {
  Stream<String> get onAppSelected;

  set showActivity(bool activity);
  set message(String message);
}

我想创建一个实现此接口(interface)的 StatefulWidget StartupPage。我希望小部件执行以下操作:

  1. 当一个按钮被按下时,它会通过 onAppSelected 流发送一个事件。 Controller 甚至会监听并执行一些操作(数据库调用、服务请求等)。

  2. Controller 可以调用 showActivityset message 让 View 通过消息显示进度。

由于 Stateful Widget 不会将其 State 作为属性公开,因此我不知道访问和修改 State 属性的最佳方法。

我希望使用它的方式是这样的:

Widget createStartupPage() {
    var page = new StartupPage();
    page.onAppSelected.listen((app) {
      page.showActivity = true;
      //Do some work
      page.showActivity = false;
    });
  }

我曾考虑通过在 createState() 中传入我希望它返回的状态来实例化 Widget,但感觉不对。

我们为什么采用这种方法的一些背景知识:我们目前有一个 Dart Web 应用程序。为了 View Controller 分离、可测试性和对 Flutter 的前瞻性思考,我们决定为应用程序中的每个 View 创建一个接口(interface)。这将允许 WebComponent 或 Flutter Widget 实现此接口(interface)并保持所有 Controller 逻辑相同。

最佳答案

有多种方式可以与其他有状态小部件进行交互。

1. findAncestorStateOfType

第一个也是最直接的方法是通过context.findAncestorStateOfType方法。

通常包装在 Stateful 的静态方法中像这样的子类:

class MyState extends StatefulWidget {
  static of(BuildContext context, {bool root = false}) => root
      ? context.findRootAncestorStateOfType<_MyStateState>()
      : context.findAncestorStateOfType<_MyStateState>();

  @override
  _MyStateState createState() => _MyStateState();
}

class _MyStateState extends State<MyState> {
  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

这就是 Navigator例如工作。

亲:

  • 最简单的解决方案

缺点:

  • 想访问 State属性或手动调用setState
  • 需要公开State子类

当你想访问一个变量时不要使用这个方法。当该变量更改时,您的小部件可能不会重新加载。

2。 Listenable、Stream 和/或 InheritedWidget

有时您可能想要访问某些属性而不是方法。问题是,您很可能希望您的小部件在该值随时间变化时更新。

在这种情况下,飞镖报价StreamSink .并且 flutter 添加在它的顶部InheritedWidgetListenableValueNotifier .它们都做相对相同的事情:订阅值更改事件时与 StreamBuilder 结合使用。/context.dependOnInheritedWidgetOfExactType/AnimatedBuilder .

当您想要 State 时,这是首选解决方案暴露一些属性。我不会涵盖所有的可能性,但这里有一个使用 InheritedWidget 的小例子:

首先,我们有一个 InheritedWidget暴露一个 count :

class Count extends InheritedWidget {
  static of(BuildContext context) =>
      context.dependOnInheritedWidgetOfExactType<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;
  }
}

然后我们有我们的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++;
            });
          },
        ),
      ),
    );
  }
}

最后,我们有了 CountBody获取这个暴露的count

class CountBody extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Text(Count.of(context).count.toString()),
    );
  }
}

优点:

  • findAncestorStateOfType 更高效
  • Stream 替代方案仅适用于 dart(适用于网络)并且与语言高度集成(关键字如 await forasync*)
  • 当值改变时自动重新加载子项

缺点:

  • 更多样板
  • 流可能很复杂

3.通知

而不是直接调用 State 上的方法,您可以发送Notification从您的小部件。并制作State订阅这些通知。

Notification 的示例将是:

class MyNotification extends Notification {
  final String title;

  const MyNotification({this.title});
}

要发送通知,只需调用 dispatch(context)在您的通知实例上,它会冒泡。

MyNotification(title: "Foo")..dispatch(context)

Note: you need put above line of code inside a class, otherwise no context, can NOT call notification.

任何给定的小部件都可以使用 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;
  }
}

例如 Scrollable ,可以调度ScrollNotification包括开始/结束/过度滚动。然后被 Scrollbar 使用无需访问 ScrollController 即可了解滚动信息

优点:

  • 很酷的响应式(Reactive) API。我们不直接在 State 上做事.它是 State订阅由其子级触发的事件
  • 多个小部件可以订阅同一个通知
  • 防止 child 访问不需要的 State属性

缺点:

  • 可能不适合您的用例
  • 需要更多样板文件

https://stackoverflow.com/questions/46057353/

相关文章:

dart - Flutter:小部件构建上的运行方法完成

Flutter 在极端情况下对齐两项 - 一项在左侧,一项在右侧

dart - 如何在 Flutter 中旋转 15 度?

dart - 在 Flutter 中显示 SnackBar

user-interface - 如何在 Flutter 中垂直和水平居中文本?

android - 所有小部件的 flutter 填充?

flutter - 使用不包含 Navigator 的上下文请求的 Navigator 操作

flutter - 如何覆盖 Flutter 中的 “Back” 按钮?

flutter - 遍历列表以在 Flutter 中呈现多个小部件?

flutter - 如何将清除按钮添加到 TextField 小部件