简体   繁体   English

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

[英]Avoid rebuilding when trying to preload widgets

I'm trying to build a system just like tinder where there is a stack of cards a user can swipe and the next few cards are always preloaded.我正在尝试构建一个类似于 tinder 的系统,其中有一堆卡片可供用户刷卡,并且总是预加载接下来的几张卡片。

To do that, I have a Stack widget that build a series of cards child widgets and give them the id of the content to load:为此,我有一个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());
  }
}

In the PostCard widget, I use the postId param to fetch the card information and build it when it is ready.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),
      ),
    );
  }
}

Everything works fine except when any element of the list of id in the Stack component changes, all the child cards gets rebuilt, therefore loosing their previously loaded state and have to fetch the data again.一切正常,除非堆栈组件中的 id 列表的任何元素发生更改,所有子卡都被重建,因此丢失了之前加载的 state 并且必须再次获取数据。

If only one element of the list of id change, why are every cards rebuilding?如果 id 列表中只有一个元素发生变化,为什么每张卡片都在重建? Am I missing something here?我在这里错过了什么吗? :) :)

Edit: In the question marked as duplicate, the problem is the build method having side effects.编辑:在标记为重复的问题中,问题是具有副作用的构建方法。 I don't believe that this is the case here.我不相信这里是这种情况。 In my case, the problem is that state of the PostCards widget is not kept even tho they rebuild with the exact same param (postId)就我而言,问题是 PostCards 小部件的 state 没有保留,即使它们使用完全相同的参数(postId)重建

Cheers!干杯!

When setState is called the build function is called again, and all the PostCard widget will be recreated since they are created in the build function.setState被调用时, build function 被再次调用,并且所有PostCard小部件将被重新创建,因为它们是在build function 中创建的。

This said, if PostCard is a Stateful widget the state should not be destroyed (eg initState will not be called on them).这就是说,如果PostCard是一个Stateful的小部件,那么 state 不应该被破坏(例如 initState 不会被调用)。 But you will likely loose the reference to them, which might be why your code is not behaving as you expect (hard to tell from your code).但是您可能会失去对它们的引用,这可能就是您的代码没有按预期运行的原因(很难从您的代码中分辨出来)。

Maybe you should make the widget where this piece of code is called a Stateful widget and create a List<PostCard> postCards variable to store your cards, this way you can initialize postCards in your initState function and they will not be recreated when you call setState , thus preserving the reference.也许您应该将这段代码称为有Stateful小部件的小部件并创建一个List<PostCard> postCards变量来存储您的卡片,这样您就可以在initState function 中初始化postCards并且在您调用setState时不会重新创建它们,从而保留参考。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM