gpt4 book ai didi

flutter - 如何防止 Flutter 中不必要的渲染?

转载 作者:行者123 更新时间:2023-12-04 12:15:46 24 4
gpt4 key购买 nike

下面是一个最小的应用程序,表明了我的担忧。如果您运行它,您会看到即使只有一个项目的数据发生了变化,每个可见项目的构建方法也会运行。

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

void main() {
runApp(MyApp());
}

class MyList extends StatelessWidget {
final List<int> items;
MyList(this.items);
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
print("item $index built");
return ListTile(
title: Text('${items[index]}'),
);
});
}
}

class MyApp extends StatefulWidget {
MyApp({Key? key}) : super(key: key);

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

class _MyAppState extends State<MyApp> {
List<int> items = List<int>.generate(10000, (i) => i);

void _incrementItem2() {
setState(() {
this.items[1]++;
});
}

@override
Widget build(BuildContext context) {
final title = 'Long List';

return MaterialApp(
title: title,
home: Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Column(
children: <Widget>[
MyButton(_incrementItem2),
Expanded(child: MyList(this.items))
],
),
),
);
}
}

class MyButton extends StatelessWidget {
final Function incrementItem2;
void handlePress() {
incrementItem2();
}

MyButton(this.incrementItem2);
@override
Widget build(BuildContext context) {
return TextButton(
style: ButtonStyle(
foregroundColor: MaterialStateProperty.all<Color>(Colors.blue),
),
onPressed: handlePress,
child: Text('increment item 2'),
);
}
}
在 React/React Native 中,我会使用 shouldComponentUpdate 或 UseMemo 来防止不必要的渲染。我想在 Flutter 中做同样的事情。
这是 Flutter github 上同一主题的已关闭问题的链接:
(我将在那里发表评论并附上此问题的链接)
https://github.com/flutter/flutter/issues/19421
在 Flutter 中避免无用的“重新渲染”或调用构建方法的最佳实践是什么,或者考虑到 Flutter 和 React 之间的结构差异,这些额外的构建不是一个严重的问题?
这或多或少类似于几年前在堆栈溢出时提出的这个问题: How to Prevent rerender of a widget based on custom logic in flutter?
给出的一个赞成的答案是使用 ListView.builder。虽然这可以防止一些(如果不是最无用的)渲染,但它并不能防止所有无用的渲染,如我的示例所示。
在此先感谢您的知识和洞察力。

最佳答案

我会争辩说,当您看到性能问题/jankiness 时进行优化,但这是概率问题。在这样做之前不值得这样做。 Flutter 团队做了一个 short video about this .
请记住,虽然您可能会看到小部件上发生重建,但您可以使用 print 检测到这些重建。语句,您没有看到单独发生的 Flutter 优化渲染。
Flutter 有一个小部件树、一个元素树和一个渲染对象树。
Image taken from Architectural Overview .
Flutter build flow
小部件树是我们手工编码的 Dart 蓝图,用于我们想要构建的内容。 (只有左树上的蓝色项目。白色项目是底层的:我们不直接写。)
元素树是 Flutter 对我们蓝图的解释,更详细地说明了使用哪些实际组件以及布局和渲染所需的内容。
renderObject 树将生成一个项目列表,其中包含需要在屏幕上光栅化的尺寸、层深度等详细信息。
蓝图可能会被多次删除和重写,但这并不意味着所有(甚至许多)底层组件都被销毁/重新创建/重新渲染/重新定位。未更改的组件将被重新使用。
假设我们正在 build 一个包含水槽和浴缸的浴室。业主决定交换水槽和浴缸的位置。承包商不使用大锤粉碎陶瓷,购买/运输并安装全新的水槽和浴缸。他只是将每个人分开并交换他们的位置。
这是一个使用 AnimatedSwitcher 的人为例子:

        child: AnimatedSwitcher(
duration: Duration(milliseconds: 500),
child: Container(
key: key,
color: color,
padding: EdgeInsets.all(30),
child: Text('Billy'))),
AnimatedSwitcher应该在他们改变时交叉淡入淡出它的 child 。 (在这种情况下说从蓝色到绿色。)但是如果 key未提供, AnimatedSwitcher不会被重建,也不会做交叉淡入淡出动画。
当 Flutter 遍历元素树,到达 AnimatedSwitcher ,它会检查它的 child 是否有明显的变化,即不同的树结构、不同的元素类型等,但它不会进行深入检查(我认为成本太高了)。它在相同的树位置(直接子节点)看到相同的对象类型( Container )并保留 AnimatedSwitcher无需重建它。结果是 Container (当它因为颜色改变而重建时)从绿色变为蓝色,没有动画。
添加唯一 keyContainer帮助 Flutter 在分析 AnimatedSwitcher 时将它们识别为不同的用于优化的子树。现在 Flutter 将重建 AnimatedSwitcher在 500 毫秒的持续时间内大约 30 次以直观地显示动画。 (Flutter 的目标是在大多数设备上达到 60fps。)
这是上面的 AnimatedSwitcher在复制/粘贴示例中使用:
  • 第一次加载时,框从绿色变为蓝色而没有动画(键没有改变)
  • 按AppBar中的刷新按钮重置页面,但是key更新为蓝色Container ,允许 Flutter 发现差异并为 AnimatedSwitcher 进行重建
  • import 'package:flutter/material.dart';

    class AnimatedSwitcherPage extends StatefulWidget {
    @override
    _AnimatedSwitcherPageState createState() => _AnimatedSwitcherPageState();
    }

    class _AnimatedSwitcherPageState extends State<AnimatedSwitcherPage> {
    Color color = Colors.lightGreenAccent;
    ValueKey key;

    @override
    void initState() {
    super.initState();
    key = ValueKey(color.value);

    Future.delayed(Duration(milliseconds: 1500), () => _update(useKey: false));
    // this initial color change happens WITHOUT key changing
    }
    @override
    Widget build(BuildContext context) {
    return Scaffold(
    appBar: AppBar(
    title: Text('Animation Switcher'),
    actions: [
    IconButton(icon: Icon(Icons.refresh), onPressed: _restartWithKey)
    ],
    ),
    body: Center(
    child: AnimatedSwitcher(
    duration: Duration(milliseconds: 500),
    child: Container(
    key: key,
    color: color,
    padding: EdgeInsets.all(30),
    child: Text('Billy'))),
    ),
    );
    }

    void _update({bool useKey}) {
    setState(() {
    color = Colors.lightBlueAccent;
    if (useKey) key = ValueKey(color.value);
    });
    }

    void _restartWithKey() {
    setState(() {
    color = Colors.lightGreenAccent; // reset color to green
    });
    Future.delayed(Duration(milliseconds: 1500), () => _update(useKey: true));
    // this refresh button color change happens WITH a key change
    }
    }
    评论附录
    问题 :
    我知道除非键不同,否则它不会做动画,但如果它没有重建,那么为什么/如何颜色从绿色切换到蓝色?
    回复 : AnimatedSwitcherStatefulWidget .它在构建之间保存前一个子小部件。当一个新的构建发生时,它会比较旧的 child 和新的 child 。 (参见 Widget.canUpdate() 方法进行比较。)
    如果旧的和新的 child 是不同的/不同的,它会为 X 帧/滴答执行过渡动画。
    如果旧 child 和新 child 没有区别/不同(根据 .canUpdate() ), AnimatedSwitcher基本上是一个不需要的包装器:它不会做任何特别的事情,但会进行重建以维护其最新的 child (?)。我不得不承认这是一个糟糕的例子选择。

    关于flutter - 如何防止 Flutter 中不必要的渲染?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67358514/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com