在Flutter中,如何解决`AnimatedCrossFade`的低效设计问题?

13

AnimatedCrossFade 的一个问题是,即使只显示其中一个,您也必须提供两个子元素。

如果这些子元素中的一个(或两个)比较复杂和繁重,那么这并不高效。

我尝试提供一个 Builder 作为子元素,像这样:

AnimatedCrossFade(
    firstChild: widget1,
    secondChild: Builder(builder: widget2builder),
    duration: const Duration(milliseconds: 500),
    crossFadeState: toggle ? CrossFadeState.showFirst : CrossFadeState.showSecond,
),

var widget2builder = (context) {
  print("Building widget 2");
  return Container(),
  );
};

然而,即使第一个小部件是正在显示的小部件,因此根本不需要第二个小部件,但这会立即打印“Building widget 2”。
这是一个完整的可运行示例:
import 'package:flutter/material.dart';

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

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> with TickerProviderStateMixin {
  bool toggle;

  @override
  void initState() {
    super.initState();
    toggle = true;
  }

  @override
  Widget build(BuildContext context) {
    var toggleButton = Padding(
      padding: const EdgeInsets.all(8.0),
      child: MaterialButton(
        child: const Text("Toggle"),
        color: Colors.grey,
        onPressed: () {
          setState(() {
            toggle = !toggle;
          });
        },
      ),
    );

    var widget1 = Container(
      key: UniqueKey(),
      color: Colors.blue,
      width: 200.0,
      child: const Text(
        "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt "
            "ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation "
            "ullamco laboris nisi ut aliquip ex ea commodo consequat.",
      ),
    );

    var widget2builder = (context) {
      print("Building widget 2.");
      return Container(
        key: UniqueKey(),
        color: Colors.red,
        width: 200.0,
        child: const Text(
          "I am ready for my closeup.",
        ),
      );
    };

    return MaterialApp(
      home: Material(
        child: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            toggleButton,
            Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                const Text("Some text above."),
                AnimatedCrossFade(
                  firstChild: widget1,
                  secondChild: Builder(builder: widget2builder),
                  duration: const Duration(milliseconds: 500),
                  crossFadeState: toggle ? CrossFadeState.showFirst : CrossFadeState.showSecond,
                ),
                const Text("Some text below."),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

如果你运行这个程序,你会发现控制台打印出了Building widget 2,但是显示的是widget 1。

我的问题:

  • AnimatedCrossFade为什么要构建它不使用的小部件?

  • 我该如何避免这个问题并有效地使用AnimatedCrossFade


在任何情况下,小部件都不应该是"复杂和沉重"的。你是什么意思? - Rémi Rousselet
@RémiRousselet 内部包含大量小部件的小部件。进行复杂计算的小部件。访问数据库或网络以获取构建自身所需信息的小部件。 - MarcG
2个回答

8
我想在Rémi的回答中补充一点,就是在提出这个问题后,我发布了一个名为AnimatedSizeAndFade的软件包来解决它: https://pub.dev/packages/animated_size_and_fade 它与其他类似小部件相比如何:
- 使用AnimatedCrossFade需要保留firstChild和secondChild,而使用AnimatedSizeAndFade则不必。 - 使用AnimatedSwitcher只需更改其子元素,但它只动画淡入淡出,而不是大小变化。 - AnimatedContainer也无法工作,除非您预先知道子元素的大小。

6
这是预期的行为。正如dart团队所述,您应该随时调用build方法。 build旨在具有廉价和无副作用的特性。
一个widget被构建并不意味着它会在屏幕上呈现。 Opacity使用0的不透明度实际上可以快捷绘制(并且在完全不透明时还有其他优化)。
如果这造成问题,则应改用AnimatedSwitcher,它在替换其子元素时播放动画。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接