【问题标题】:Avoid rebuilding when trying to preload widgets尝试预加载小部件时避免重建
【发布时间】:2019-10-08 22:29:09
【问题描述】:

我正在尝试构建一个类似于 tinder 的系统,其中有一堆用户可以刷卡,并且总是预加载接下来的几张卡。

为此,我有一个 Stack 小部件,它构建了一系列卡片子小部件并为它们提供要加载的内容的 id:

class PostCardStack extends StatefulWidget {
  const PostCardStack({Key key, this.postIds, this.onCardDismissed})
      : super(key: key);

  final List<String> postIds;
  final Function onCardDismissed;

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

class _PostCardStackState extends State<PostCardStack> {
  ValueNotifier<double> _notifier = ValueNotifier<double>(0.0);

  _buildCardStack() {
    List<Widget> cards = [];
    for (String postId in widget.postIds) {
      int idx = widget.postIds.indexOf(postId);
      if (postId == widget.postIds.first) {
        cards.add(CustomDismissible(
            resizeDuration: null,
            dismissThresholds: {CustomDismissDirection.horizontal: 0.2},
            notifier: _notifier,
            key: Key(postId),
            onDismissed: (direction) {
              _notifier.value = 0.0;
              widget.onCardDismissed(postId);
            },
            child: SlidablePanel(
              panel: AnimatedBuilder(
                  animation: _notifier,
                  child: PostCard(
                    postId: postId,
                  ),
                  builder: (context, _) {
                    return Opacity(
                        opacity: 1 - _notifier.value,
                        child: PostCard(
                          postId: postId,
                        ));
                  }),
            )));
      } else {
        cards.add(AnimatedBuilder(
            animation: _notifier,
            child: PostCard(
              postId: postId,
            ),
            builder: (context, _) {
              return Opacity(
                  opacity: lerpDouble(1 - (0.1 * idx), 1 - ((0.1 * idx) - 0.1),
                      _notifier.value),
                  child: Transform(
                      origin: null,
                      alignment: Alignment.bottomCenter,
                      transform: Matrix4.translationValues(
                          0.0,
                          lerpDouble(
                              -idx * 35, (-idx * 35 + 35), _notifier.value),
                          0.0)
                        ..scale(
                            lerpDouble(1 - (0.1 * idx), 1 - ((0.1 * idx) - 0.1),
                                _notifier.value),
                            lerpDouble(1 - (0.1 * idx), 1 - ((0.1 * idx) - 0.1),
                                _notifier.value),
                            1),
                      child: PostCard(
                        postId: postId,
                      )));
            }));
      }
    }
    return cards.reversed.toList();
  }

  @override
  Widget build(BuildContext context) {
    return Stack(
        alignment: Alignment.bottomCenter, children: _buildCardStack());
  }
}

PostCard 小部件中,我使用postId 参数来获取卡信息并在准备好时构建它。

class PostCard extends StatefulWidget {
  const PostCard({Key key, this.postId}) : super(key: key);

  final String postId;

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

class _PostCardState extends State<PostCard> {
  PostModel post;

  @override
  void initState() {
    super.initState();
    print("${widget.postId} mounted");
    _fetchPost(widget.postId);
  }

  @override
  void dispose() {
    print("${widget.postId} disposed");
    super.dispose();
  }

  _fetchPost(String postId) async {
    PostModel fullPost = await blablaFirestore(postId);

    setState(() {
      post = fullPost;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 300,
      child: Align(
        alignment: Alignment.topCenter,
        child: Text(post == null ? "Loading..." : post.text),
      ),
    );
  }
}

一切正常,除非 Stack 组件中的 id 列表中的任何元素发生更改,所有子卡都会重新构建,因此会丢失其先前加载的状态并必须再次获取数据。

如果id列表中只有一个元素发生变化,为什么每张卡片都在重建?我在这里错过了什么吗? :)

编辑:在标记为重复的问题中,问题是构建方法具有副作用。我不相信这里是这种情况。就我而言,问题在于 PostCards 小部件的状态即使使用完全相同的参数(postId)重建也没有保留

干杯!

【问题讨论】:

  • 您能出示您的明信片代码吗?
  • @rémi-rousselet 在您标记为重复的问题中,问题是构建方法具有副作用。在我的情况下,问题是 PostCards 小部件的状态即使它们使用完全相同的参数 (postId) 重建也没有保留
  • 那是一回事。无论如何,您在“构建”中做错了什么,否则不会发生此问题(或者您没有提供关键信息)。
  • 你能分享你的明信片代码,尤其是请求的方式吗?
  • 啊,这说明了一些事情!那是因为您的 PostCards 根据某些条件有不同的父级。

标签: flutter


【解决方案1】:

setState被调用时,build函数被再次调用,所有PostCard小部件将被重新创建,因为它们是在build函数中创建的。

这就是说,如果 PostCardStateful 小部件,则不应销毁该状态(例如,不会在它们上调用 initState)。但是您可能会失去对它们的引用,这可能就是您的代码没有按预期运行的原因(很难从您的代码中看出)。

也许您应该将这段代码称为Stateful 的小部件创建一个List&lt;PostCard&gt; postCards 变量来存储您的卡片,这样您就可以在您的initState 函数中初始化postCards,它们将调用setState 时不会重新创建,从而保留引用。

【讨论】:

  • Postcard 的代码非常简单,它只是在初始状态下从提供的postId 获取卡信息,当它准备好时将它们呈现在卡中而不是CircularProgressIndicator。奇怪的是它们似乎被破坏了(在每个父级重新渲染时都会调用 dispose 方法)
  • 您是否尝试将小部件设为有状态小部件?这应该可以解决您的问题。
猜你喜欢
  • 2020-10-18
  • 2019-06-28
  • 2020-04-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-18
  • 2022-06-29
  • 1970-01-01
相关资源
最近更新 更多