Force Flutter重绘所有小部件

Dog*_*ion 19 dart flutter

有没有办法强制Flutter重绘所有小部件(例如在区域设置更改后)?

sha*_*ais 21

只需在您的一个高级小部件上使用一个键,下面的所有内容都会丢失状态:

Key _refreshKey = UniqueKey();

void _handleLocaleChanged() => setState((){ 
  _refreshKey = UniqueKey() 
});

Widget build(BuildContext context){
  return MaterialApp(
    key: _refreshKey ,
    ...

  )
}

Run Code Online (Sandbox Code Playgroud)

您还可以使用值键,例如:

return MaterialApp(
  key: ValueKey(locale.name)
  ...
);
Run Code Online (Sandbox Code Playgroud)


Col*_*son 18

这种类型的用例,你有孩子可以阅读的数据,但你不想显式地将数据传递给你所有孩子的构造函数参数,通常需要一个InheritedWidget.Flutter将自动跟踪哪些小部件依赖于数据并重建已更改的树的部分.有一个LocaleQuery用于处理区域设置更改的小部件,您可以在Stocks示例应用程序中查看它的使用方式.

简而言之,这就是Stocks正在做的事情:

  • 在根小部件(在本例中为StocksApp)上进行回调以处理区域设置更改.这个回调做了一些工作,然后返回一个自定义的实例LocaleQueryData
  • 将此回调注册为构造函数的onLocaleChanged参数MaterialApp
  • 需要使用区域设置信息的子窗口小部件LocaleQuery.of(context).
  • 当语言环境发生更改时,Flutter仅重绘具有依赖于语言环境数据的小部件.

如果要跟踪区域设置更改以外的其他内容,可以创建自己的扩展类InheritedWidget,并将其包含在应用程序根目录附近的层次结构中.它的父母应该是一个StatefulWidget关键设置为GlobalKey儿童可以访问的.该StateStatefulWidget应该自己要分发和揭露改变它该呼叫的数据保护方法setState.如果子窗口小部件想要更改State数据,则可以使用全局键获取指向State(key.currentState)的指针并在其上调用方法.如果他们想要读取数据,他们可以调用of(context)子类的静态方法,InheritedWidget这将告诉Flutter这些小部件需要在您的State调用时重建setState.

  • 为什么不仅仅拥有`Flutter.redrawAllWidgetsBecauseISaidSo();`? (5认同)
  • Woo,我看了三遍,然后又看了三遍 Flutter Gallery 中的 Shrine 示例代码,仍然只有模糊的理解。试试这个。 (2认同)
  • 您好Collin,因为localeQuery小部件已被删除,您可能想要更新您的答案.详情见https://github.com/flutter/flutter/issues/1269 (2认同)

Mar*_*rcG 17

老问题,但这是解决方案:

在您的build方法中,调用该rebuildAllChildren函数并将其传递给context

@override
Widget build(BuildContext context) { 
  rebuildAllChildren(context);
  return ...
}

void rebuildAllChildren(BuildContext context) {
  void rebuild(Element el) {
    el.markNeedsBuild();
    el.visitChildren(rebuild);
  }
  (context as Element).visitChildren(rebuild);
}
Run Code Online (Sandbox Code Playgroud)

这将访问所有儿童并将他们标记为需要重建。如果您将此代码放在小部件树中最顶层的小部件中,它将重建所有内容。

另请注意,您必须订购要重建的特定小部件。此外,您可以使用一些布尔值,以便该小部件的重建仅在您真正需要它时才重建其所有子项(当然,这是一项昂贵的操作)。


重要提示:这是一种黑客行为,只有在您知道自己在做什么并且有充分的理由时才应该这样做。我的国际化包中有一个必要的例子:i18_extension。正如科林杰克逊在他的回答中解释的那样,你真的不应该这样做。

  • @Dika您可能正在打开底部表单的小部件中调用setState。底页位于覆盖层中,而不是在打开它的小部件内。所以无法重建。如果您在 table_calendar 状态或 Bottomsheet 状态下调用 setState ,它可能会起作用。 (2认同)

Tab*_*aba 11

刷新整个小部件树可能很昂贵,而且当您在用户眼前这样做时,这看起来并不好。

所以为此目的扑了ValueListenableBuilder<T> class。它允许您只重建一些您的目的所必需的小部件,而跳过昂贵的小部件。

您可以在此处查看文档ValueListenableBuilder flutter docs
或仅查看以下示例代码:

  return Scaffold(
  appBar: AppBar(
    title: Text(widget.title)
  ),
  body: Center(
    child: Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        Text('You have pushed the button this many times:'),
        ValueListenableBuilder(
          builder: (BuildContext context, int value, Widget child) {
            // This builder will only get called when the _counter
            // is updated.
            return Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: <Widget>[
                Text('$value'),
                child,
              ],
            );
          },
          valueListenable: _counter,
          // The child parameter is most helpful if the child is
          // expensive to build and does not depend on the value from
          // the notifier.
          child: goodJob,
        )
      ],
    ),
  ),
  floatingActionButton: FloatingActionButton(
    child: Icon(Icons.plus_one),
    onPressed: () => _counter.value += 1,
  ),
);
Run Code Online (Sandbox Code Playgroud)

也永远不会忘记的力量 setState(() {});

  • _counter 在哪里声明的? (3认同)

Tos*_*ade 10

简单使用:

Navigator.popAndPushNamed(context,'/screenname');
Run Code Online (Sandbox Code Playgroud)

每当您需要刷新时 :)


Ale*_*ard 6

您的窗口小部件应具有setState()方法,每次调用此方法时,将重绘窗口小部件。

文档:小部件setState()

  • 我想强制重绘应用程序(包括视图堆栈中更深的视图)(如标题所述)。从我将要触发的地方开始,我无法访问根小部件。 (3认同)

Ada*_*ane 6

可能对您的用例有用的是使用导航器重新加载页面。我在我的应用程序中在“真实”和“演示”模式之间切换时会这样做。这是一个例子:

Navigator.of(context).push(
    new MaterialPageRoute(
        builder: (BuildContext context){
          return new SplashPage();
        }
    )
);
Run Code Online (Sandbox Code Playgroud)

您可以将上面代码中的“new SplashPage()”替换为您想要重新加载的任何主要小部件(或屏幕)。可以从您有权访问 BuildContext 的任何位置(这是 UI 中的大多数位置)调用此代码。

  • 它只是添加了另一个页面 (2认同)

Hil*_*ren 5

我在这篇文章中解释了如何创建自定义的“ AppBuilder”小部件。

https://hillelcoren.com/2018/08/15/flutter-how-to-rebuild-the-entire-app-to-change-the-theme-or-locale/

您可以通过将其与MaterialApp一起包装来使用该小部件,例如:

Widget build(BuildContext context) {
  return AppBuilder(builder: (context) {
    return MaterialApp(
      ...
    );
  });
}
Run Code Online (Sandbox Code Playgroud)

您可以使用以下命令告诉应用重新生成:

AppBuilder.of(context).rebuild();
Run Code Online (Sandbox Code Playgroud)