Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Disccussion / Help] Potentional Pitfalls of using AnimatedList with Redux #165

Open
Charlie9830 opened this issue Jan 18, 2020 · 1 comment

Comments

@Charlie9830
Copy link

Charlie9830 commented Jan 18, 2020

I have been developing an App for a few months using Flutter with Redux.
I think I have discovered a bit of a pitfall when integrating an AnimatedList with Redux. And can't really find any info or discussions relating to this issue.

AnimatedList requires that the underlying state is updated before you make any calls to insertItem() or removeItem()

Redux handles Items coming from my DB via a Thunk Action. As these items come in, I first dispatch them to to the store via sync actions, then use a GlobalKey to access the AnimatedListState and call insertItem() or removeItem() accordingly.

This approach however has a pitfall. Dispatching a synchronous action only guarantees that the Store will be updated, it doesn't guarantee that Flutter will rebuild / update. This is due to Redux ultimately calling setState() and setState() doesn't gaurantee that the Widget tree will be updated immediately. This means that now that my app has grown to a point where Flutter is batching together rebuild calls, insertItem() or removeItem() are throwing index exceptions, as they are trying to operate on the collection they have currently, which is yet to be updated by Flutter, even though the Redux store has been updated.

After discovering this I finally noticed that in the AnimatedList Widget of the week video, in the examples they don't use setState to update the underlying collection, which given the above circumstances.. Makes sense.

I can get my app to work if I check for out of range indexes and render nothing in response. But this has caused issues in the past as it tends to hide symptoms of other issues.

The other approach I have tried was instead of calling insertItem() or removeItem() immediately, I instead wrapped them in closures and stored them in state, to be called later on.
Then inside the parent widget of the AnimatedLists, used the didUpdateWidget() lifecycle to call the closures once the collection had changed. This however had more issues as I couldn't reliable determine if the collection had truly changed without having to implement a collection equality check, and I think the performance implications of that are to great.

My next attempt will be to store the collections both inside the Store and outside it, using Middleware to ensure that the collections stored outside the store are kept in sync with the in state collections. I will then still pass the in State collections down to the AnimatedLists as usual so they can rebuild themselves in response to collection changes but instead of using the in State collection, they use the out of state collection which is guaranteed to be up to date. I could simplify this a bit using ChangeNotifiers I think. This however will require building the Item ViewModels outside the StoreConverter.

I would greatly appreciate if anyone a better solution or one that has worked for them in the past.

I don't see this as a particular issue with Redux or AnimatedList. More so as a problem arising from the performance implications of a Reactive approach when getting towards the edge cases.

@Darrekt
Copy link

Darrekt commented Jul 31, 2020

Hey there, I've been struggling with this for a day too and wanted to write my two cents' worth.

The list wasn't animating for the longest time because any update to the list in global state would trigger a re-render of the component (as per redux and StoreConnector-attached components. So the new element would just appear without animating as the AnimatedList would just re-render with a new "initial list".

To solve this problem, I'm maintaining a shallow copy of the list in a stateful widget and using that to render the AnimatedList, which solves the problem of it re-rendering. I can now mutate this internal list and do the appropriate call to insertItem for the animation, but I find this process it terribly "manual".

The only issue right now is figuring out how to get my hands on the animation listener to dispatch store.dispatch(AddNewItem(item)) when the animation for a specific item stops playing.

I'm a bit of a novice, so I'm not sure if what I'm trying to do is even possible, but I thought I'd post it here for others' consideration.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
2 participants