How I replicated the iOS sliding row animation in Flutter

Deep dive on the SlidableDrawerDelegate

In a previous article, I explained how I created the Slidable widget. Today we’re going to dissect an essential component of it: the SlidableDrawerDelegate.

🤔 What is it?

In the Slidable widget, the developer can choose how the slide actions animate through what I named SlidableDelegates. The SlidableDrawerDelegate is one of the built-in ones:

The SlidableDrawerDelegate in action

The animation I tried to replicate is the one you can found in the iOS Reminder app:

Swipe actions in the iOS Reminder app

🎓 The animation in depth

We can describe this animation like this:

  • The actions are stacked so that the element closest to the tile is the lowest in the stack.
  • Initially all actions are placed outside, right next to the tile.
  • The actions move, for the same duration, from their starting position to their final position.

😐 images speak louder than words 🎨 😁

SlidableDrawerDelegate animation in 3D

We laid out how the animation should work, now let’s see how we can do it in Flutter.

🔧 How it works

Before showing you some code, let me explain how I designed this:

The SlidableDrawerDelegate inheritance graph

As we can see above, the SlidableDrawerDelegate inherits from SlidableStackDelegate which inherits from the SlidableDelegate.

When the Slidable builds, it calls the buildActionsmethod of the SlidableDelegate. It’s an abstract method, so the buildActions method of the SlidableStackDelegate is called:

SlidableStackDelegate implementation

The purpose of this class is to create the animation used to slide the tile to the desired direction and delegate the animation of the actions to the subclasses (through the buildStackActions method).
To do that we create a Stack with two children: the first child represents the animation of the actions and the second child is the animation of the tile. So that the tile is always painted on top of the actions.

The animation of the tile is simply created with the SlideTransition widget for which we set an animation that starts from the zero offset and ends at the desired offset (the total length of the actions in the desired direction).

We can represent this animation like this (actions are purposely removed):

SlideTransition animation of the tile

Now let’s see how we animate the actions in the case of a SlidableDrawerDelegate.

We have seen earlier that the animation of the actions are delegated to the buildStackActions of the SlidableStackDelegate subclasses. So let’s see how this method is implemented for SlidableDrawerDelegate:

buildStackActions implementation for SlidableDrawerDelegate

It’s a little more complicated that the previous method, so let’s break down this one piece by piece.

🌴 Widget Tree

Let’s focus on how the actions are built by taking a look at the widget tree:

Example of actions widget tree

◼️ The first Positioned widget is used to constrain this component to take all the place in the Stack built by the SlidableStackDelegate.

◼️ The LayoutBuilder allows us to know the parent Stack extent. We use it then to compute the actual extent of an action.

◼️ The AnimatedBuilder builds its child whenever the actionsMoveAnimation changes (which is the animation used to slide the tile).

◼️ The Stack is then used to produce as many layers as actions. So, every child in the Stack is the widget of an action.

◼️ Finally each action has a Positioned widget as a parent in order to be placed at the desired position.

💡 Connecting the dots

Now that we saw the widget structure, let’s focus on the code:

◼️ Lines 9–10, we get the extent of an action:

Note: The getMaxExtent is just a utility method used to get either the width or the heigth, depending on the direction the widget can slide.

◼️ Lines 13–18, we compute the position animation for each action:

As we saw earlier, initially all actions are placed outside, right next to the tile. That means here that all actions start to the position -actionExtent (Line 3 above).
The end position for each action is then determined by its index in the action list (Line 4 above). For secondary actions, the lowest the index, the closest the action is to the tile at the end (inverted for actions).

◼️ Lines 25–32, we create the action widget with the desired position:

As mentioned before, actions and secondary actions are laid out in a different order. The last action will be the closest to the tile, whereas it’s the first for secondary actions.
The displayIndex is used for that.

Note: The createPositioned is a utility method which creates a Positioned widget depending on the direction the widget can slide and the type of actions shown (secondary or not).

Finally we use the build method of the actionDelegate to get the actual action.

In the below gif, the Archive action is the first declared in the action list.
We can see that it starts at the same position as the Share action but end up the furthest to the tile.

SlidableDrawerDelegate animations separated

📕 Conclusion

I explained here how I replicated the iOS sliding row animation in Flutter, through the Slidable widget and its SlidableDrawerDelegate. Now feel free to create your own SlidableDelegates 😄.

If you have any questions / suggestions, leave a comment below ✏️.

If this package is helping you, you can support me by ⭐️the repo, or 👏 this article.
You can also follow me on Twitter: