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
There are different types of
Clip widgets like
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)
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),
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.
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:
- Using a
- Using a
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
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)
CircleAvatar, you can achieve the very same effect but have a few more options.
backgroundImageoption. It doesn’t matter which one to use if you don’t plan to use a fallback image. So in comparison to the
Clipwidgets, you should not use the
childproperty for images. If you do so, the image won’t be clipped and look like this:
If you use an image source that has a loading time before being displayed, the content will be displayed in the following ordered cascade:
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
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)
Flutter comes up with a decent utility to display any image with rounded corners: it’s called
Clip widget renders only parts of the widget you provide. With the predefined
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 🤌