如何迫使颤动重建 /重新绘制所有小部件



是否有一种方法可以迫使挥舞着所有小部件(例如,在语言环境更改之后(?

您的Widget应该具有setState()方法,每次称为此方法时,小部件都被重新绘制。

文档:Widget setState()

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

在您的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);
}

这将拜访所有孩子,并将他们标记为需要重建。如果将此代码放在小部件树中的最高小部件中,它将重建所有内容。

还要注意,您必须订购该特定的小部件进行重建。另外,您可以拥有一些布尔值,以便在您真的需要时重建该小部件只能重建所有孩子(当然,这是一个昂贵的操作(。


重要:这是一个黑客,只有在知道自己在做什么并且有强有力的理由这样做的情况下,才应该这样做。我的国际化软件包中有必要的一个示例:i18_extension。正如柯林·杰克逊(Collin Jackson(在他的回答中解释的那样,您真的不应该这样做。

这种类型的用例,您可以在其中阅读的数据,但您不想明确地将数据传递给所有孩子的构造者参数,通常需要InheritedWidget。Flutter将自动跟踪哪些小部件取决于数据并重建已更改的树的部分。有一个LocaleQuery小部件,旨在处理语言环境更改,您可以看到库存示例应用中的使用方式。

简短,这是股票在做的事情:

  • 在Root Widget(在这种情况下,StocksApp(上进行回调,以进行处理。此回调可以进行一些工作,然后返回LocaleQueryData的自定义实例
  • 将此回调作为onLocaleChanged参数注册到MaterialApp构造函数
  • 需要语言环境信息的儿童小部件使用LocaleQuery.of(context)
  • 当该语言环境变化时,只有对区域数据具有依赖性的小部件。

如果您想跟踪除语言环境更改以外的其他内容,则可以制作自己的类,以扩展InheritedWidget,并将其包含在应用程序根部附近的层次结构中。它的父母应该是StatefulWidget,其键设置为GlobalKey,可供孩子们访问。StatefulWidgetState应拥有要分发的数据,并揭示更改调用setState的方法。如果儿童小部件想要更改State的数据,他们可以使用全局键来获取State(key.currentState(的指针,并在其上调用方法。如果他们想读取数据,他们可以调用您InheritedWidget子类的静态of(context)方法,这会告诉Flutter,这些小部件需要每当您的State调用setState

简单使用:

Navigator.popAndPushNamed(context,'/screenname');

每当您需要刷新时:(

只需在您的一个高级小部件上使用一个键,下面的一切都会失去状态:

Key _refreshKey = UniqueKey();
void _handleLocaleChanged() => setState((){ 
  _refreshKey = UniqueKey() 
});
Widget build(BuildContext context){
  return MaterialApp(
    key: _refreshKey ,
    ...
  )
}

您也可以使用一个值键,例如:

return MaterialApp(
  key: ValueKey(locale.name)
  ...
);

刷新整个小部件可能很昂贵,当您在用户面前做似乎不甜的眼睛时。

因此,为此,扑扑具有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,
  ),
);

,也永远不会忘记 setState(() {});

的力量

我解释了如何在这篇文章中创建自定义'appbuilder'小部件。

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

您可以通过用它包装材料来使用小部件,例如:

Widget build(BuildContext context) {
  return AppBuilder(builder: (context) {
    return MaterialApp(
      ...
    );
  });
}

您可以告诉应用程序使用:

进行重建
AppBuilder.of(context).rebuild();

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

Navigator.of(context).push(
    new MaterialPageRoute(
        builder: (BuildContext context){
          return new SplashPage();
        }
    )
);

您可以将上述代码中的" new SplashPage(("替换为要重新加载的任何主要小部件(或屏幕(。可以从您可以访问buildContext(UI中的大多数地方(的任何地方调用此代码。

为什么不只是flutter.redrawallwidgetsbecaeisaIdso((; - Timsim

有点是:更改为Redraw Statefull儿童小部件。

耶琳娜·莱西奇(Jelena Lecic

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(),
    );
  }
}
class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key}) : super(key: key);
  @override
  _MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;
  var _forceRedraw; // generate the key from this
  void _incrementCounter() {
    setState(() {
      _counter++;
      _forceRedraw = Object();
    });
  }
  @override
  void initState() {
    _forceRedraw = Object();
    super.initState();
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            MyStatefullTextWidget(
              key: ValueKey(_forceRedraw),
              counter: _counter,
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}
class MyStatefullTextWidget extends StatefulWidget {
  final int counter;
  const MyStatefullTextWidget({
    required this.counter,
    Key? key,
  }) : super(key: key);
  @override
  _MyStatefullTextWidgetState createState() => _MyStatefullTextWidgetState();
}
class _MyStatefullTextWidgetState extends State<MyStatefullTextWidget> {
  @override
  Widget build(BuildContext context) {
    return Text(
      'You have pushed the button this many times:${widget.counter}',
    );
  }
}

我的情况足以重建项目。

更改:

          return child;
        }).toList(),

to:

          return SetupItemTypeButton(
            type: child.type,
            icon: child.icon,
            active: _selected[i] == true,
            onTap: ...,
          );
        }).toList(),
class SetupItemTypeButton extends StatelessWidget {
  final dynamic type;
  final String icon;
  estureTapCallback onTap;
  SetupItemTypeButton({Key? key, required this.type, required this.icon, required this.onTap}) : super(key: key);
  @override
  Widget build(BuildContext context) {
   return Container();
  }
}
class SetupItemsGroup extends StatefulWidget {
  final List<SetupItemTypeButton> children;
  final Function(int index)? onSelect;
  SetupItemsGroup({required this.children, this.onSelect});
  @override
  State<SetupItemsGroup> createState() => _SetupItemsGroupState();
}
class _SetupItemsGroupState extends State<SetupItemsGroup> {
  final Map<int, bool> _selected = {};
  @override
  Widget build(BuildContext context) {
    int index = 0;
    return Container(
      child: GridView.count(
        children: widget.children.map((child) {
          return SetupItemTypeButton(
            type: child.type,
            icon: child.icon,
            active: _selected[i] == true,
            onTap: () {
              if (widget.onSelect != null) {
                int i = index++;
                child.active = _selected[i] == true;
                setState(() {
                  _selected[i] = _selected[i] != true;
                  child.onTap();
                  widget.onSelect!(i);
                });
              }
            },
          );
        }).toList(),
      ),
    );
  }
}

简单使用:

Navigator.popAndPushNamed(context,'/xxx');

最新更新