# Bringing the PageTransformer to Flutter

Source: https://iiro.dev/bringing-the-pagetransformer-from-android-to-flutter/
Published: 2017-09-23

Build PageView parallax effects in Flutter and reuse ideas from existing Android page transition examples.

---

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](https://developer.android.com/reference/android/support/v4/view/ViewPager.html), a component that has swipe gestures to flip through pages of data.

<div style="position:relative;height:0;padding-bottom:75.0%"><iframe src="https://www.youtube.com/embed/DKbWzMU5oCs?rel=0?ecver=2" width="480" height="360" frameborder="0" style="position:absolute;width:100%;height:100%;left:0" allowfullscreen></iframe></div>
<p style="text-align: center;"><small>How the sample app for this article looks like.</small></p>

_ViewPager_ was already great, but when [PageTransformer](https://developer.android.com/reference/android/support/v4/view/ViewPager.PageTransformer.html) 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](https://docs.flutter.io/flutter/widgets/PageView-class.html) 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.


```dart
import 'page_transformer.dart';

class Example extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return 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](https://github.com/FlutterRocks/page-transformer/blob/master/lib/page_transformer.dart). 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](https://docs.flutter.io/flutter/widgets/NotificationListener-class.html).
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](https://github.com/FlutterRocks/page-transformer).

## 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](https://www.google.com/search?q=viewpager+page+transforms&oq=viewpager+page+transforms) for cool transformations and they'll work in the same way.
