Image with rounded corners

How to crop images in Flutter

Image with rounded corners
No time to read?
tl;dr

The key to crop images and make them have rounded corners is called Clip. Use ClipRect for rounded corners and ClipOval for circles.

Just wrap the image widget with the respective Clip widget to achieve the desired effect.

Of course, this works for any other widget as well.

Displaying images with rounded corners or even as a circle is a pretty common design requirement. Rectangular images can look rather harsh and sometimes don’t fit into a modern design.

You can’t always control the image source, especially when you are working with APIs, so premature image editing is not always an option.

That’s where runtime clipping comes into play.

Let’s see how we can create a widget that displays all kinds of images with rounded corners.

Image as a rounded rectangle

Let’s look at images with rounded corners first.

Keeping the original shape

If the rectangle is not square, then you might want to keep its shape instead of making it a square. In this case, you can wrap your Image widget with a ClipRRect widget.

There are different types of Clip widgets like ClipRect, ClipOval and the aforementioned ClipRRect. The base principle is always the same: the Clip defines boundaries. The child widget may exceed these boundaries and if it does, then this part of the child widget is not displayed.

An interesting example usage of Clip (in this case ClipPath) is cutting “holes” in widgets like it was explained in this tutorial.

ClipRect also has an additional argument that lets us define the border radius of the corners:

1ClipRRect(
2  borderRadius: BorderRadius.circular(24),
3  child: Image.network(
4    'https://my.image.url',
5  ),
6)
A rectangle image with a border radius of 24
A rectangle image with a border radius of 24
A rectangle image with a border radius of 104
A rectangle image with a border radius of 104

Making the image square

A common use case for normalized images that have a square shape are e. g. lists that all have a thumbnail image. Independent of the actual size of the image and its proportions, you might want to display the image as a square shape with rounded borders.

Here’s how that’s done:

 1ClipRRect(
 2  borderRadius: BorderRadius.circular(24),
 3  child: SizedBox.fromSize(
 4    size: const Size.fromRadius(144),
 5    child: Image.network(
 6      'https://my.image.url',
 7      fit: BoxFit.cover,
 8    ),
 9  ),
10),
A squared image with a border radius of 24
A squared image with a border radius of 24
A squared image with a border radius of 104
A squared image with a border radius of 104

With border

In addition to the rounded corners, you might want to add a border to draw the line between the image and the background.

To achieve this, you have to wrap your ClipRRect widget with a Container. The padding of the Container determines the thickness of the border.

A rectangular image with rounded corners and a red border
A rectangular image with rounded corners and a red border
A rectangular image with rounded corners and a red border
A rectangular image with rounded corners and a red border
 1final borderRadius = BorderRadius.circular(20);
 2
 3Container(
 4  padding: EdgeInsets.all(8),
 5  decoration: BoxDecoration(color: Colors.red, borderRadius: borderRadius),
 6  child: ClipRRect(
 7    borderRadius: borderRadius,
 8    child: SizedBox.fromSize(
 9      size: Size.fromRadius(48),
10      child: Image.network('https://my.image.url', fit: BoxFit.cover),
11    ),
12  ),
13)

Image as a circle

When you want the image to be displayed as a circle like e. g. when displaying profile images, you have basically two easy-to-use options:

The ClipOval is just the equivalent of ClipRRect for circles. CircleAvatar is explicitly built for displaying profile images and has numerous options to support this whereas ClipOval has no opinion about what’s being rendered inside the child widget.

ClipOval

The usage of ClipOval is as simple as ClipRRect: most of the time, the only parameter you need to use is the child parameter, which expects a Widget that is being turned into a circle.

1ClipOval(
2  child: SizedBox.fromSize(
3    size: const Size.fromRadius(144),
4    child: Image.network(
5      'https://my.image.url',
6      fit: BoxFit.cover,
7    ),
8  ),
9)
A rectangular image cropped to a circle
A rectangular image cropped to a circle
A square image cropped to a circle
A square image cropped to a circle

CircleAvatar

With CircleAvatar, you can achieve the very same effect but have a few more options.

Warning
Be careful: if you want to display an image as a circle, you need to use the foregroundImage or the backgroundImage option. It doesn’t matter which one to use if you don’t plan to use a fallback image. So in comparison to the Clip widgets, you should not use the child property for images. If you do so, the image won’t be clipped and look like this:
A `CircleAvatar` where an image is provided as `child` property
Don’t use the child property for images!

If you use an image source that has a loading time before being displayed, the content will be displayed in the following ordered cascade:

  • foregroundImage
  • child
  • backgroundImage

Meaning that it will look for foregroundImage first. If it’s not there (or still loading), it will display child. Below this it will display backgroundImage if provided and loaded.

There’s also the backgroundColor property which defaults to the primary color of the defined theme.

So if you just want to display an image, you should use the foregroundImage property to point to your image and set the backgroundColor to Colors.transparent to prevent your primary color being shown while the image is being loaded:

1SizedBox.fromSize(
2  size: const Size.fromRadius(144),
3  child: CircleAvatar(
4    backgroundColor: Colors.transparent,
5    foregroundImage: Image.network(
6      'https://my.image.url',
7    ).image,
8  ),
9)

If you want to have a textual fallback for your image (such as the user’s initials), you can use the child property to define this fallback like this:

 1SizedBox.fromSize(
 2  size: const Size.fromRadius(144),
 3  child: CircleAvatar(
 4    backgroundColor: Colors.red,
 5    foregroundImage: Image.network(
 6      'https://my.image.url',
 7    ).image,
 8    child: const Text('XX', style: TextStyle(fontSize: 32)),
 9  ),
10)

Conclusion

Flutter comes up with a decent utility to display any image with rounded corners: it’s called Clip. A Clip widget renders only parts of the widget you provide. With the predefined Clip widgets ClipRect and ClipOval, you can easily show an image with rounded corners or as a circle.

For circles, you also have the option to use the CircleAvatar widget. It allows you to defined fallbacks to be display if the loading of the image fails or is still pending.

It depends on the desired effect, which widget is suitable for your needs. Clip is a more generic solution whereas CircleAvatar aims - as the name suggests - for use cases in which an avatar (user’s image or initials) are displayed.

Comment this 🤌

You are replying to 's commentRemove reference