Bringing the PageTransformer from Android to Flutter

Coming from a native Android developer background, I was ecstatic when the design support library came out. A lot of design guidelines and patterns existed, but there wasn’t officially supported libraries to implement them. One of those patterns was ViewPager, a component that has swipe gestures to flip through pages of data.

How the sample app for this article looks like.

ViewPager was already great, but when PageTransformer was introduced, the whole thing went to another level. Now we could create nice parallax scroll effects easily. PageTransformer spurred up nice looking app onboarding experiences, and they aren’t going away anytime soon.

PageViews and PageTransformers in Flutter

Flutter’s equivalent of ViewPagers is the PageView widget. The API is much simpler: we don’t have to provide any separate adapters to have a few pages. When having a huge (or indefinite) amount of pages, the API is still a lot easier than its Android counterpart.

One thing that seems to be missing is the PageTransformer . I needed a way to make some fancy effects but couldn’t find anything. I ended up implementing a PageTransformer class myself.

Here’s the API I came up with.

PageTransformer(
  pageViewBuilder: (context, pageVisibilityResolver) {
    return PageView.builder(
      itemBuilder: (context, index) {
        final pageVisibility =
            pageVisibilityResolver.resolvePageVisibility(index);

        // Use these two properties to transform your "Hello World" text widget!
        // In this example, the text widget fades in and out of view, since we use
        // the visibleFraction property, which can be between 0.0 - 1.0.
        final position = pageVisibility.pagePosition;
        final visibleFraction = pageVisibility.visibleFraction;

        return Opacity(
          opacity: visibleFraction,
          child: Text('Hello World!'),
        );
      },
    );
  },
);

I iterated a couple times until I had together what I believe is the simplest possible API. It also supports the new PageView(children: [...]) approach, and I believe, with small modifications, could support scroll effects on ListViews as well.

The PageTransformer source code

See the finished PageTransformer class here. I commented some key parts, and a high-level description is below.

Here’s the general way it works:

  1. We wrap the PageView inside the PageTransformer widget’s PageViewBuilder method. The PageTransformer then watches the PageView's scroll events by wrapping it inside a NotificationListener.
  2. Once a new scroll event happens, the _PageTransformerState is then updated with a new PageVisibilityResolver, with the current ScrollMetrics provided in its constructor. Once the state is updated, the pageViewBuilder method gets called with the newly updated PageVisibilityResolver.
  3. Inside our itemBuilder, we call PageVisibilityResolver#resolvePageVisibility(index) and pass the index of the widget we’re currently building. We get a PageVisibility object which contains useful information about the current page’s position and what portion of it is currently visible.
  4. We can transform our pages using the information provided by the PageVisibility object.

The sample app

The sample app, showcasing some cool parallax effects, can be found here.

Conclusion

I don’t really know that deeply about Flutter’s widget rebuilding logic to tell if this is the most efficient way to do it. However, I believe it’s a viable and nice way of doing various effects in a PageView until something official comes up. At least I got buttery smooth animations and framerates even on “slow mode”.

Since the pagePosition here is the same thing as position in PageTransfomer on Android, there are lots of tutorials and examples for cool transformations and they’ll work in the same way.

Comments