0
votes

I have a web-socket that sends images with some data and than I display them inside AnimatedList. The problem is that every new data comes in, it reload every single item the list which reloads every image. Also because image is from base64 converted, it can't be cashed.

Is there any way to preserve the state of each object in AnimatedList so that when new item is added, it doesn't reload all other objects/images in the list.

AnimatedList sample:

AnimatedList(
  key: node.validListKey,
  controller: widget.scrollController,
  padding: EdgeInsets.fromLTRB(10, 10, 0, 10),
  initialItemCount: node.data.length,
  itemBuilder: (context, index, animation) {
    return FadeTransition(
      opacity: CurvedAnimation(
          parent: animation,
          curve: Interval(0, 0.2, curve: Curves.ease)),
      child: SizeTransition(
        sizeFactor:
            CurvedAnimation(parent: animation, curve: Curves.ease),
        child: DataContainer(data[index])
      ),
    );
  },
),

Base decode sample:

child: AspectRatio(
  aspectRatio: 1 / 1,
  child: ExtendedImage.memory(
    base64.decode(data.base),
    fit: BoxFit.cover,
    loadStateChanged: (state) {
      if (state.extendedImageLoadState ==
          LoadState.completed) {
        return ExtendedRawImage(
          image: state.extendedImageInfo?.image,
        );
      }
      return Center(child: CircularProgressIndicator());
    },
  ),
),

UPDATE#1 It seems that the problem is about how I insert items in the list. Because I insert new items at start of the list, for some reason it calls init state only for the last element, but not the recently added one. When I insert at the end of the list, problem is solved. Is there any way how I could force to call init state for the recently added item instead of the last?

Insert item example:

array.add(data);
array.insert(0, data);
if (key.currentState != null)
  key.currentState.insertItem(0, duration: _insertDuation);

UPDATE#2 So I was able to fix the first problem that my images was reloading by wrapping AnimatedList inside SingleChildScrollView, adding reverse: true, shrinkWrap: true and by adding decode inside initState method.

SingleChildScrollView(
  child : ListView.builer(
    shrinkWrap: true,
    reverse: true,
    physics: NeverScrollableScrollPhysics(),
    itemBuilder: (BuildContext context, int index) {
      ...
    },
  )
);

But now the problem is with remove method. When I try to remove item from the list, base widget content stays the same, but image changes by one index. I have tried adding unique key for each widget, but that forces to reload each image when data has been added.

_data.removeAt(0);
if (_validListKey.currentState != null)
  _validListKey.currentState.removeItem(
      0, (context, animation) => Container(),
      duration: Duration.zero);
1
It's likely your node.validListKey that changes. AnimatedList does not destroy the state of your list itemsRémi Rousselet
@RémiRousselet I use Provider package to manage my states and key for AnimatedList (final NodeHelper node = Provider.of<NodeHelper>(context, listen: false);). Can that be a problem?Mārtiņš Ciekurs

1 Answers

0
votes

You should do the base64.decode(data.base) in your initState function (or an async function that was triggered in initState) and cache the results.

Your build function should not trigger this decoding, or should not run any code with a side effect. Right now at every build you are doing a decode and giving a different Uint8List instance to ExtendedImage. If ExtendedImage is implemented correctly, it should not do any UI changes when it's given the same reference in a new build() call.