Avatar photo

Jared Bell

Introducing Flutter Custom Carousel

We’re really excited to introduce a new package called Flutter Custom Carousel, a widget for creating fully custom, animated scrollable lists. It manages all of the tricky logic surrounding scroll interactions and physics, and leaves the visual presentation of items up to you.

The idea came from discussions about building a carousel widget; we were looking at the wide diversity of carousel UIs, debating which one to create, and what parameters were needed to customize it. There were too many possibilities, each with a vast range of potential customizations, and it became obvious we either had to pick a single option and try to perfect it, or take a more radical approach that empowered developers to do “anything”.

I started coding a prototype of the latter idea, building a widget that manages all of the tricky logic surrounding scroll interactions and physics, and leaves the visual presentation of children up to an effectsBuilder method. This method simply wraps a child in whatever effects or positioning widgets you want, based on a scrollRatio from -1 to 1.

For example:

// very basic example that scrolls children vertically from -250px to +250px
CustomCarousel(
    effectsBuilder: (index, scrollRatio, child) =>
       Transform.translate(
            offset: Offset(0, scrollRatio * 250),
            child: child,
       ),
    children: [card1, card2, etc],
)

While playing around with this, I realized that almost every “effect” I wanted was already supported by our Flutter Animate package, so I also added an effectsBuilderFromAnimate helper method to make it easy to use them together:

// very basic example that scrolls children vertically from -250px to +250px
CustomCarousel(
    effectsBuilder: CustomCarousel.effectsBuilderFromAnimate(
        effects: EffectList().moveY(begin: -250, end: 250),
    ),
    children: [card1, card2, etc],
)

Once I had the basics working with a rough (and ugly) prototype, I put together a simple test harness app, and handed it over to our amazing design team to play with, explore capabilities, and provide feedback on the overall concept.

A series of technical prototype phases.
Technical prototypes, culminating in the test harness (right)

Jared, our Creative Director, is going to talk about that experience.

From concepts to code

Our prototyping typically takes place within a variety of UI, 3D and motion design tools — uncovering as much as we can to firm up the direction before we start building. While that workflow still helped us plan and design some aspirational concepts, with this project we went a step beyond, and did a lot of the ideation directly in the Flutter testing harness Grant provided.

Although our design team isn’t expected to write code, we all have Flutter installed and are familiar enough to run projects and collaborate with developers, especially when it comes to fine-tuning visuals, animations, and feel. Having the ability and confidence to jump into the code and work directly with the real thing led to more rapid and experimental ideation from our design team.

It’s worth noting, this wouldn’t be possible without a high attention to detail during development. Project structure, view cleanliness, proper documentation and intuitively designed widget properties are massive contributors to the success downstream users (especially us designers) will have with the code.

Getting Started

For all of our experimentation, we leveraged the pre-built effects in Flutter Animate. This isn’t the only way to define carousel behaviors, but as designers working with minimal coding capabilities it streamlined things a lot. It allowed us to approach visuals in a way that was analogous to tools like After Effects; layering on effects in a timeline.

From positional effects like align, move, slide and rotate, to more complex special effects like flip, shimmer and boxShadow the basic parameters all work the same, and specific ones are surfaced with code hinting, or in the online Flutter Animate documentation.

Each time we’d save we were able to observe the changes in our running project in real-time with hot reload. Whether testing in a virtual environment or on a physical device, the instant feedback of those changes and ability to rapidly iterate is an empowering capability when designing and refining the experience.

Admittedly, it took some playing around to fully grasp how everything worked, and there were plenty of aha moments along the way.

Working with scrollRatio

Understanding scrollRatio is foundational to working with CustomCarousel — it’s a value that represents a beginning, middle and end point for how effects are applied as the item scrolls.

If we think about this in terms of a normal vertical scrolling list, the scrollRatio is 1 when the item is at the end of the visible portion of the list (ie. has just scrolled into the viewport from the bottom).

Similarly, its scrollRatio is -1 when it is at the beginning of the viewport (about to scroll out the top).

A scrollRatio of 0 is a bit trickier, it indicates that the item has reached the “selected” position for the list. In a simple vertical scroll, that just means the center of viewport, but with more complex effects or playing with the itemsBefore and itemsAfter properties you can move this around.

Animated diagram that represents scrollRatio behavior across a carousel.
Demonstration of scrollratio & selected / settled states

Delay and duration

In Flutter Animate all effects run in parallel, but you can use delay and duration to modify the timing of when they run in relation to each other.

As discussed, CustomCarousel uses a scrollRatio value from -1 to 1; this doesn’t map directly to the time-oriented parameters in Flutter Animate. To make this work, the total duration of the effects is divided in half, with the first half mapped to the scrollRatio -1 to 0, and the second half to 0 to +1. Note that the actual duration doesn’t matter, just how it maps back to the ratio.

I found the easiest way to think about this was to make all my effects 200ms long, so I had 0-100ms as the “scroll in” time range, and 100-200ms as the “scroll out” time.

Flutter Animate also has some nifty logic where effects inherit their delay and duration from previous effects by default. This makes it really easy to synchronize a list of effects with each other, but can get a bit weird when we want to introduce more asymmetrical, sequenced animations to our carousels.

I found it was often easiest to just list out my effects in the order I needed them applied (as discussed below), and assign each an explicit delay and duration (and curve), bypassing the inheritance model altogether.

After I was done and handed off to Grant, he often tightened up the final implementation by leveraging inheritance, but I think he was just showing off because he wrote Animate.

Carousel effect similar to flipping through a deck of cards.
Create complex animations with well-timed effect sequences

Easing curves

One more interesting property available to every effect is curve. A familiar concept in animation, easing curves affect the characteristics of movement across a given duration. Does it start fast or end slowly, exhibit traits of elasticity or appear to bounce?

Curves can be applied to any of the Flutter Animate effects to achieve really great (and sometimes unusual) results around non-linear alignment, scale, or rotation transitions throughout a carousel’s duration.

This was particularly helpful in our Records Box demo when trying to simulate a rolodex style animation but with a realistic sense of vanishing perspective around an arc.

Two carousel examples with different easing curves applied.
Left: default linear easing, right: creative use of in/out easing to achieve a circular effect

Order up!

Because effects are applied in the order they are defined, knowing how to manipulate that to your benefit is key to achieving your desired effect and in some cases can have notable impacts on performance.

An illustrative example of this is swapping the order of align and rotate effects. If rotate is defined first, carousel items each receive rotation values spanning the alignment change in a straight line. If align is first, the items will be distributed positionally and then have rotation values applied to each step, causing the carousel items to follow an arc.

Carousel examples illustrating how order effects rotation.
Left: no rotation, middle: rotation first, right: rotation second

Less obviously, it is a good idea to add effects like blur and tint early in the list, so that they apply to the smallest screen area. For example, EffectList().blur().align() is more performant than EffectList.align().blur() because the former only applies the blur to the target item, where the latter applies it to the Align widget, which fills all available space.

Conclusion

We’re really pleased with how much functionality is packed into this seemingly simple widget. The more we played with it, the more it felt like a significant shortcut for creating highly expressive carousels in Flutter.

Of course, we’ve just scratched the surface when it comes to discussing capabilities, and complete instructions for use can be found in the official Flutter Custom Carousel documentation.

We can’t wait to see what you build!

XD to Flutter v4: Better Layout Code

v4.0 of the “XD to Flutter” plugin is available now, with a focus on simplifying and improving the Dart code it generates.

Building on v3’s focus on improving the developer experience, v4 includes a fairly significant refactor of how layout code is generated by the plugin to enable smarter, cleaner results.

Continue reading →

Alpha video in HTML5

Alpha video in HTML5 should be easy right? Not quite, certainly not as easy as Flash was. In an article a long long time ago, from an internet far far away … I wrote about alpha video in Flash 8. (remember Flash?). Back then alpha video was a huge new feature that allowed developers to create a .flv with a transparent background that worked in all browsers. Allowing us to do all sorts of fancy effects. With Flash being a thing of the past, modern browsers are not in agreement on what video format we should use on the web. It makes things a little more muddled today.

Continue reading →

XD to Flutter v3.0

I’m very excited to announce the release of v3.0 of the “XD to Flutter” plugin, with a number of powerful new developer features.

Prior to v1.0, the primary goal was just to output as much of the content in Adobe XD to Flutter as possible: Vector graphics, text, images, fills, blurs, blend modes, etc. Version 1 tackled responsive layout, and v2.0 built on that with support for stacks, scroll groups, and padding. Version 2 also included the ability to export null-safe code, a critical developer feature for working with Flutter 2.

In v3.0 we’ve doubled down on improving the workflow for developers, including providing new ways to clean up the exported code and integrate dynamic content.

Continue reading →

5 Times When You Absolutely Must Write Tests For Your Code

It’s my opinion that you should always write tests for your source code. Tests force you to code better. Tests allow you to write dependable code, create better architecture, and help you live longer*. They also help you spot fussy APIs, opportunities for reuse, and redundancies.

That said. You don’t always have the time (or budget) to test everything to death! Not everyone sees the value in all those little green checkmarks. Life isn’t pedantic and heavy-handed. Life is a pack wild horses and sometimes you need to be the cowboy.

So, when do you push back? When do you say NO! We must write tests!

1. When you have the time.

There is no reason to skip out on writing tests if you have the time to write them. Why would you opt out of better code? Taking the extra time to make your code testable will turn average code into dependable code. Code that you know actually works is almost always better than new code.

2. When you’re making a data structure.

You cannot make a data structure without writing tests for it. Why would anyone trust a data structure that cannot prove it works?

Data structures must be tested. I don’t even know how you’d code a data structure without setting up a test harness first. You don’t know how your code will be used, so knowing that every little piece works as expected is necessary.

With tests, you’ll see the logic in breaking code into small pieces. Tests will make it easier to spot problems in your architecture.

Writing data structures against a test suite is the only way to do it right.

3. When you want community contributions.

Tests are the backbone of any open source project. They make sure that community contributions do not break the codebase. This allows fixes, changes, and optimizations to be made with certainty.

Your test suite becomes the hurdle that any contributor must clear. It’s not too much to ask for contributions that prove they work.

4. When you’re designing an API.

Starting with a test suite is a great way to design an API.

This allows you to work backward from your code interface instead of coding to it. This will let you design an API from the user’s perspective first.

5. When it is a dependency

Point blank. If other code needs to use this code, you must write a test for it.

The testable code will become part of an ever-expanding toolbox. Dependable toy soldiers who can be summoned to fight for you. Go! Test the world!

Here are a few resources to help you start writing tests for your code.
Mocha
Node.JS Assert
Writing good tests

*There is no scientific data that shows writing tests will help you live longer.