尝试预加载小部件时避免重建

时间:2019-10-08 22:29:09

标签: flutter

我正在尝试构建一个类似于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列表中只有一个元素发生变化,为什么每张卡都需要重建?我在这里想念什么吗? :)

编辑:在标记为重复的问题中,问题是构建方法具有副作用。我不认为这是事实。在我的情况下,问题在于即使使用完全相同的参数(postId)进行重建,PostCards小部件的状态也不会保留

干杯!

1 个答案:

答案 0 :(得分:1)

再次调用protected override void OnDrawItem(DrawItemEventArgs e) { // (...) e.DrawBackground(); using (var brush = new SolidBrush(this.ForeColor)) { e.Graphics.DrawString(this.GetItemText(this.Items[e.Index]), this.Font, brush, e.Bounds); } // (...) } 函数时,setState函数将被重新创建,因为所有build小部件都是在PostCard函数中创建的。

这就是说,如果buildPostCard小部件,则状态不应被破坏(例如,不会在其上调用initState)。但是您可能会失去对它们的引用,这可能就是为什么您的代码无法按预期运行的原因(很难从您的代码中看出来)。

也许您应该制作此代码称为Stateful小部件的小部件,并创建一个Stateful变量来存储您的卡,这样您就可以在{ {1}}函数,并且当您调用List<PostCard> postCards时不会重新创建它们,从而保留了引用。