When it first clicked for me that Flutter gives me full control of everything on the screen, right down to the last pixel, I immediately wanted to go beyond the fixed and predictable world of most app UIs. The fully custom widgets that I could draw using CustomPaint begged to be broken out of the typical ScrollView or Container. I wanted to move around, zoom in on things, and navigate the world that Flutter makes it possible to create. The Transform widget was my vehicle for navigating this world, and GestureDetector provided the controls.
GestureDetector gives easy access to most gestures, and Transform provides the ability to modify the perspective in which a child widget is seen. Both are simple, independent widgets that follow Flutter’s pattern of composability. When we combine the two, every gesture on a widget becomes a way to explore.
An introduction to transformation
Despite its power, all that the Transform widget really does is to take a transformation matrix and apply it to its child. This results in the translation, scale, rotation, and even skew of the child with respect to the parent, all specified by one simple Matrix4 object.
Matrices are very convenient to work with when doing transformations because they are compostable. Storing separate translation, scale, rotation, and skew parameters are intuitive at first, but it’s easy to run into ambiguity with things like order.
If we tie some user gestures into updating the transformation matrix using a GestureDetector, then the user has the freedom to navigate the scene that we’re displaying to them. GestureDetector provides easy access to gestures like a drag, which we can use for translation, pinch, for zooming, and even a two-finger rotation.
GestureDetector provides lots of different callbacks for various gestures and their start, update, and end states. However, if you’re implementing something like the pictured demo that combines several, the scale callbacks are all you’ll need. The onScaleUpdate callback provides a focal point, both horizontal and vertical scales, and rotation, all in one. This allows you to respond to multiple gestures at the same time, such as if the user is rotating and scaling in one gesture. See the ScaleUpdateDetails for everything provided in the onScaleUpdate callback.
Combining Transform and GestureDetector gives us an incredibly powerful tool. So much is possible, from moving around a game board, as shown in the screenshots, to navigating a map, to provide an interactive image viewer, and beyond.
The app shown here is currently available as a demo in the Flutter Gallery app. All of the code is a part of the main flutter open-source repository and can be found in the transformation demo source. Most of the transformation logic is in the GestureTransformable class, including the inertia animation.