是否有一种方法可以迫使挥舞着所有小部件(例如,在语言环境更改之后(?
您的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
,可供孩子们访问。StatefulWidget
的State
应拥有要分发的数据,并揭示更改调用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');