Design Shapes in Flutter — Introduction to the Morphable Shape Package

Wenkai Fan
ITNEXT
Published in
5 min readFeb 22, 2021

--

Shapes and borders are essential aspects of UI frameworks. CSS offers rounded rectangle shapes with four configurable borders out of the box. But you can use a clip-path or some smart tricks to generate all kinds of shapes. It would be great to have those features in Flutter as well.

How does Flutter handle shapes and borders?

All built-in shapes (more precisely, borders of shapes) in Flutter inherits from the abstract ShapeBorder class. Concrete Flutter built-in shape borders can be divided into two categories:

  • The BoxBorder and Border class. They have four border sides that are used to describe the four sides of a rectangle and are used in the BoxDecoration to style a Container widget. The four border sides can have different colors and widths, but they only work for a rectangular box. They can also smoothly animate between each other.
Animation of the Border class
  • The abstract OutlinedBorder class. Inheriting this class are the CircleBorder, RoundedRectangleBorder, BeveledRectangleBorder, etc. Those borders have a uniform width and color. You can morph/animate a rounded rectangle into a circle or a rounded rectangle. But you can not morph a rounded rectangle into a beveled rectangle.
Smooth transition
No smooth animation

Clearly, there are a lot of things we can improve. Here are some features that I would like to see:

  1. A rounded rectangle shape with four configurable borders, just like what CSS offers.
  2. More commonly used shapes, like polygon, star, triangle, trapezoid, diamond, etc.
  3. Be able to use percentage units. By this, I mean to be able to set the border radius of a rectangle by either using some px value or using percentages pf the width/height of the rectangle. CSS supports this natively, we should come up with some similar solutions in Flutter.
  4. Gradient borders. This is also supported by CSS but can not be done in Flutter without going through some hassle.
  5. Be able to smoothly animate all those shapes and borders would be a huge bonus.
  6. Be able to serialize/deserialize those shapes. So it would be easy to store and reuse some complex shapes you designed.

Introducing the Morphable Shape Package

The morphable_shape package is aimed to provide all the features I mentioned above.

  • First, rounded rectangles with different border sides. You can provide any border radius or border width value and the package will handle it in a way that no two borders overlap, similar to CSS.
  • More commonly used shapes. Many shapes can be generated from a rectangle with different corner styles and corner radius.
Shapes made out of a rectangle
Polygons with different corner radius and style
Stars with different corner radius and style
Custom path shapes
  • More commonly used shapes like arrow, trapezoid, bubble, triangle, circle are supported as well.
  • The percentage unit is supported by using the dimension package.
ShapeBorder rectangle=RectangleShapeBorder(
borderRadius: DynamicBorderRadius.only(
topLeft: DynamicRadius.circular(10.toPXLength),
bottomRight: DynamicRadius.elliptical(60.toPXLength, 10.toPercentLength))
);

The code above will give you a rectangle with a 60 px circular radius at the top left corner and a (60 px, 10%) elliptical corner at the bottom right corner.

  • Gradient border is supported for every shape. You can also plot just a portion of the border or change the stroke cap and stroke join of the border.
Left: a single linear gradient. Middle: a single radial gradient. Right: Two linear gradients.
Plot only part of the border to make progress indicators
  • Morphing between any two shapes is also supported. You can read this Medium article for the implementation details. The morphing algorithm is pixel perfect and very efficient after dozens of revisions. The original idea was taken from https://github.com/veltman/flubber and https://pub.dev/packages/path_morph but has since changed greatly. The current implementation pays more attention to the symmetry of the shapes. The morphing of built-in Flutter shapes can all be reproduced using this package.
Morphing with a zero width border
Morphing with different border color and gradients
  • Serialization is supported.
ShapeBorder shape = RectangleShapeBorder();
String jsonStr = json.encode(shape.toJson());
ShapeBorder shapeDecoded = parseShapeBorder(json.decode(jsonStr));

There we go! All six goals I mentioned before are achieved. If you want to see actual code for generating those shapes and animations, check out the package on pub.dev or its Github page.

Or play with an online demo…

The example app of this package lets you play with the package in almost every manner. I have also hosted the app online at fluttershape.com. Below are some screenshots of the app’s interface.

On the main screen, you can drag the dots on the shape to modify it or use the slider on the right side
Pick a gradient for the border
Pick a new shape
Design a custom path shape. Points can be snapped to the grid.
See a morphing animation at different speeds
Get the JSON of the current shape

I put a large amount of effort into building this tool. If you are interested in using this package, I strongly recommend you play with the tool.

This article should be the final one concluding and showcasing the results of my monthly long development/adventure in the Flutter world. Hope you enjoyed it and please check out the package on pub.dev and Github. Thank you!

--

--

Ph.D. in Nuclear Physics, M.S. in Computer Science at Duke University, Flutter lover