11

我正在尝试保留小部件的状态,因此如果我暂时从小部件树中删除有状态小部件,然后稍后重新添加它,小部件将具有与我删除它之前相同的状态。这是我的一个简化示例:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

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

class _MyHomePageState extends State<MyHomePage> {
  bool showCounterWidget = true;
  @override
  Widget build(BuildContext context) {

    return Material(
      child: Center(
        // Center is a layout widget. It takes a single child and positions it
        // in the middle of the parent.
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            showCounterWidget ? CounterButton(): Text("Other widget"),
            SizedBox(height: 16,),
            FlatButton(
              child: Text("Toggle Widget"),
              onPressed: (){
                setState(() {
                showCounterWidget = !showCounterWidget;
              });
                },
            )
          ],
        ),
      ),
    );
  }
}

class CounterButton extends StatefulWidget {
  @override
  _CounterButtonState createState() => _CounterButtonState();
}

class _CounterButtonState extends State<CounterButton> {
  int counter = 0;

  @override
  Widget build(BuildContext context) {
    return MaterialButton(
      color: Colors.orangeAccent,
      child: Text(counter.toString()),
      onPressed: () {
        setState(() {
          counter++;
        });
      },
    );
  }
}

理想情况下,我不希望状态重置,因此计数器不会重置为 0,我将如何保留计数器小部件的状态?

4

3 回答 3

17

正如 Joshua 所说,小部件在临时从树中移除时失去其状态的原因是因为它失去了其元素/状态。

现在你可能会问:

我不能缓存元素/状态,以便下次插入小部件时,它会重用前一个而不是重新创建它们?

这是一个有效的想法,但不是。你不能。Flutter 将其判断为反模式,并在这种情况下抛出异常。

您应该做的是将小部件保持在小部件树内,处于禁用状态。

要实现这样的事情,您可以使用以下小部件:

  • 索引栈
  • 能见度/台下

这些小部件将允许您在小部件树中保留一个小部件(以便它保持其状态),但禁用其渲染/动画/语义。

因此,而不是:

Widget build(context) {
  if (condition)
    return Foo();
  else
    return Bar();
}

在它们之间切换时会使它们的状态变为Foo/失去它们的状态Bar

做:

IndexedStack(
  index: condition ? 0 : 1, // switch between Foo and Bar based on condition
  children: [
    Foo(),
    Bar(),
  ],
)

使用此代码,然后Foo/它们之间来回执行时不会丢失它们的状态Bar

于 2019-12-29T10:07:40.967 回答
3

小部件旨在在其范围和生命周期内存储它们自己的瞬态数据。

根据您提供的内容,您正在尝试重新创建CounterButton 子小部件,方法是将其删除并添加回小部件树。

在这种情况下,CounterButton 未保存或未保存MyHomePage屏幕、父窗口小部件下的计数器值,没有对视图模型的任何引用或顶层内或顶层的任何状态管理。

Flutter 如何呈现您的小部件的更多技术概述

有没有想过key如果您尝试为小部件创建构造函数会怎样?

class CounterButton extends StatefulWidget {
  const CounterButton({Key key}) : super(key: key);

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

keys ( key) 是 Fl​​utter 框架自动处理和使用的标识符,用于区分小部件树中的小部件实例。在小部件树中删除添加小部件 ( ) 会重置分配给它的内容,因此它所保存的数据及其状态也会被删除。CounterButtonkey

Widget注意:如果 a 只包含key作为其参数,则无需为 a 创建构造函数。

从文档中:

通常,作为另一个小部件的唯一孩子的小部件不需要显式键。

为什么 Flutter 会更改分配给 CounterButton 的键?

CounterButton您正在哪个是 aStatefulWidgetText哪个是 a之间切换,这是StatelessWidget为什么 Flutter 识别出两个完全不同的对象的原因。

您始终可以使用 Dart Devtools 来检查更改并切换 Flutter 应用程序的行为。

关注#3a4d2文末_CounterButtonState图像-1

这是切换小部件后的小部件树结构。从小部件 CounterButtonText图像-2

您现在可以看到以 ,CounterButton结尾#31a53的标识符与之前的标识符不同,因为这两个小部件完全不同图像-2

你能做什么?

我建议您将运行时更改的数据保存在 中_MyHomePageState,并在其中创建一个CounterButton带有回调函数的构造函数来更新调用小部件中的值。

counter_button.dart

class CounterButton extends StatefulWidget {
  final counterValue;
  final VoidCallback onCountButtonPressed;

  const CounterButton({Key key, this.counterValue, this.onCountButtonPressed})
      : super(key: key);

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

class _CounterButtonState extends State<CounterButton> {
  @override
  Widget build(BuildContext context) {
    return MaterialButton(
      color: Colors.orangeAccent,
      child: Text(widget.counterValue.toString()),
      onPressed: () => widget.onCountButtonPressed(),
    );
  }
}

假设你在 中命名你的变量_counterValue_MyHomePageState你可以像这样使用它:

home_page.dart

_showCounterWidget
    ? CounterButton(
        counterValue: _counterValue,
        onCountButtonPressed: () {
          setState(() {
            _counterValue++;
          });
        })
    : Text("Other widget"),

此外,此解决方案将帮助您CounterButton在应用程序的其他部分重复使用或其他类似的小部件。

我在dartpad.dev中添加了完整的示例。

Andrew 和 Matt 就 Flutter 如何在后台渲染小部件进行了精彩的演讲:

进一步阅读

于 2019-12-29T03:04:28.950 回答
2

这个问题的真正解决方案是状态管理。有几个很好的解决方案可以作为概念和颤振包使用。我个人经常使用BLoC模式。

这样做的原因是小部件状态旨在用于 UI 状态,而不是应用程序状态。UI 状态主要是动画、文本输入或其他不持久的状态。

问题中的示例是应用程序状态,因为它旨在比小部件的生存时间更长。

有一个关于创建基于 BLoC 的计数器的小教程,这可能是一个很好的起点。

于 2021-03-08T07:50:14.690 回答