英文:
flutter go_router unnecessary rebuild on push and go back
问题
以下是要翻译的内容:
这是一个测试应用的示例:dartpad
在应用程序内,我们有两个主要路由:ListPage
和 ListItemViewScreen
。
ListItemViewScreen
是从 ListPage
派生的子路由。
在这种情况下,ShellRoute
仅用于示例。
问题: 当我们点击列表中的任何项目(第81行 - 推),ListPage
会重建并调用 _loadData()
函数。当我们从 ListItemViewScreen
返回时,ListPage
也会重新构建。
如何防止这种不必要的重建?
英文:
Here is example of test app: dartpad
Inside app we have 2 main routes: ListPage
and ListItemViewScreen
.
ListItemViewScreen
is child route from ListPage
.
In this case, ShellRoute
used only for example
Problem: when we click on any item in list(row 81 - push), ListPage
rebuild and _loadData()
function is called. And when we go back from ListItemViewScreen
, ListPage
rebuild too.
How to prevent this unnecessary rebuild?
答案1
得分: 2
这是因为您实际上在build
方法内部调用了函数_loadData()
。这通常是一个不好的做法。
相反,将小部件转换为StatefulWidget
,设置一个成员来保存未来,然后在initState
中为其分配值,并在FutureBuilder
中使用该成员作为future
(请参见_loadDataFuture
):
class ListPage extends StatefulWidget {
final String type;
const ListPage({Key? key, required this.type}) : super(key: key);
@override
State<ListPage> createState() => _ListPageState();
}
class _ListPageState extends State<ListPage> {
late final Future<int> _loadDataFuture;
Future<int> _loadData() {
print("Emulate load data from DB: ${widget.type}");
return Future.value(1);
}
@override
void initState() {
super.initState();
_loadDataFuture = _loadData();
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: _loadDataFuture,
builder: (context, snapshot) => ListView.builder(
itemCount: 100,
itemBuilder: (context, index) {
return InkWell(
onTap: () {
context.push("/list/${widget.type}/view/$index");
},
child: Padding(
padding: const EdgeInsets.all(20),
child: Text("item_$index"),
),
);
},
),
);
}
}
在某些情况下,您可能希望在完成后重新执行未来。在这种情况下,从_loadDataFutre
的声明中删除final
,如果要触发重新加载,可以使用:
setState(() {
_loadDataFuture = _loadData();
});
英文:
This happens because you actually call the function _loadData()
within the build
method. This is generally a bad practice.
Instead, convert the widget into a StatefulWidget
, set up a member that will hold the future, assign value to it in initState
, and use this member in the FutureBuilder
as future
(see _loadDataFuture
):
class ListPage extends StatefulWidget {
final String type;
const ListPage({Key? key, required this.type}) : super(key: key);
@override
State<ListPage> createState() => _ListPageState();
}
class _ListPageState extends State<ListPage> {
late final Future<int> _loadDataFuture;
Future<int> _loadData() {
print("Emulate load data from DB: ${widget.type}");
return Future.value(1);
}
@override
void initState() {
super.initState();
_loadDataFuture = _loadData();
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: _loadDataFuture,
builder: (context, snapshot) => ListView.builder(
itemCount: 100,
itemBuilder: (context, index) {
return InkWell(
onTap: () {
context.push("/list/${widget.type}/view/$index");
},
child: Padding(
padding: const EdgeInsets.all(20),
child: Text("item_$index"),
),
);
},
),
);
}
}
It is possible that at some point you do want to re-execute the future after it is completed. In the case, remove the final
from the declaration of _loadDataFutre
, and if you'd like to trigger a reload, use:
setState(() {
_loadDataFuture = _loadData();
});
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论