How to Make a Fancy Widget Selection in Flutter? 🤔
By: Daniil Lipatkin
Tags: Android App Development, Animation, Flutter, iOS App Development, Dart

Besides the Flutter has a very large set of predefined convenient widgets and classes, there are still some very common things and workflows in native development that this framework doesn’t cover out of the box — and one of them is a decent, clear and fancy widget selection.

Gimme the demo!

Looks cool, right? 🙃

If you take a short look at any popular native application — Telegram, Gmail, Instagram, Google Keep, WhatsApp, etc. — you will be able to see that it’s very common to have a part of UI like that, and that’s not a surprise, as almost every app has to deal with some kind of list data nowadays.

However, when I started to Google on how to achieve this effect in Flutter, I found almost nothing, so I have developed my own way to manage it and now I want to share it with you (many code snippets are too large to embed them in this article, so I will often leave the links to gist files and only show the essential parts of code in examples).

The Architecture 🤓

In Flutter there’s a very widespread pattern called controller. The pattern is simple — you basically build the container class for some state of your app, or, in most cases, just create an instance of the one already built for you, such as AnimationController, and use it in multiple places and widgets by passing it down the constructors.

My approach is very similar — I have written a special SelectionController that I instantiate in some parent widget and then just pass it down to children, allowing them to change its state.

What do we need this controller to have inside? 🤔

Some Set<T> of data that will represent the collection of already selected items. We will add and remove items from this set. Wow, unexpectedly… we will, of course, need an AnimationController! Firstly, this is the provider of animations for the selection mode — for example, the AppBar and TabBar animations. And secondly, each status of animation can be easily applied to selection handling: AnimationStatus.forward will be our SelectionStatus.selecting , complete will become inSelection , reverse will match unselecting and dismissed — notInSelection Methods to add and remove items, close the selection, dispose the animation controller and to listen to controller changes. Also, we will have some shortcut and helper getters, such as inSelection which actually is status == SelectionStatus.selecting || status == SelectionStatus.inSelection Switcher —it is a special class that will allow you to generate some special salt signature that you will add to item data, when you will pass it in a ValueKey down the item widget. It can be anything, even basic sequential looper from 0 to 10 (or whatever, the upper bound doesn’t really matter, the main rule here is not to make it too small, because I had troubles with fast state updates in this case). I will explain and show why we need a switcher and keys later. For now, simple switcher example:

Implementing the UI 😃

Now we have all the essential to start implementing our selection logic in the user interface. For that we are going to create a few implicitly animated widgets —the ones to animate the AppBar and also selectable item widgets themselves. For full code of examples check out the gist.

If you’ll try to use the SelectionAppBar from the gist, it will look like this

Making selectable widgets ✅

The common pattern to create a selectable widget is the following

Firstly, each selectable item has to receive from its parent:

SelectionController

A condition when it should appear as selected

ValueKey to update its state properly

Selectable widget example

Next, each item will have its own animation controller. The item’s initial state has to be calculated based on the current SelectionControler status; we need this to trigger the item unselection animation on press of the “close selection” button.

Why does it work? 🤨 After calling the close method the selection set inside SelectionController is cleared only after the controller comes back to the status SelectionStatus.notInSelection , which in other words is the state when the unselection animation has ended. If we have the selected condition like this:

selected: selectionController.selectionSet.contains(songs[index].id)

then during the whole unselection animation, it will be true. Note that this won’t be true if you just click on item that’s the only selected one. In this case the selection set will be emptied immediately.

Let’s now define selection methods and the build method:

Initializing and using a selection controller instance 😎

Pay attention that after the creation of SelectionController we have to subscribe to all of its updates — both status and single add/remove events (keep in mind that status listener has a priority and add/remove listener won’t be called together with status change listener).

I also added a pop handler to this example, so user can close the selection by pressing the system back button.

The purpose of the ValueKey and the Switcher 🔑

This is very essential to have control over the internal state of each selectable widget. If we just omit any keys, calling the close method of selection controller won’t update widgets’ states and the unselection animation on widget won’t be triggered. The same thing will happen if you provide a ValueKey, but won’t sum unique item data (that can be its index in the list) with some salt (switcher value).

If we don’t give a ValueKey, or won’t use a Switcher

But at the same time, we can’t just give a UniqueKey to every widget, because in that case all widgets’ states will be updated on every build call, even when we actually don’t need it (if you subscribed to add/remove events, this will happen each time user makes a tap).

The main idea is to have a unique, but constant key on each item, with the ability to change it when we need, i.e. when the close method gets called (SelectionController will do it for you internally).

Animating selection counter in the AppBar 🙂

We can use Flutter’s AnimatedSwitcher to make the counter animated, check out this link to see how exactly. Here I’ll only show it’s usage:

The result

UPD: The version of Flutter I used is v1.16.3.

Check out related links 🔗

The gist for this article CodePen live demo GitHub repository with the demo application shown on GIFs My music app that inspired me to write this code and this article

By the way, this is my first experience writing an article for Medium.

Have a nice day 😎🤙