Flutter (34): Clip

Time: Column:Mobile & Frontend views:233

34.1 Clipping Widgets

Flutter provides several clipping widgets to clip components:

Clipping WidgetDefault Behavior
ClipOvalClips a square child into a circle or a rectangular child into an ellipse.
ClipRRectClips a child into a rounded rectangle.
ClipRectClips the overflowed content of a child that exceeds the layout space.
ClipPathClips according to a custom path.

Let’s look at an example:

import 'package:flutter/material.dart';

class ClipTestRoute extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // Avatar
    Widget avatar = Image.asset("imgs/avatar.png", width: 60.0);
    return Center(
      child: Column(
        children: <Widget>[
          avatar, // No clipping
          ClipOval(child: avatar), // Clipped to a circle
          ClipRRect( // Clipped to a rounded rectangle
            borderRadius: BorderRadius.circular(5.0),
            child: avatar,
          ), 
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Align(
                alignment: Alignment.topLeft,
                widthFactor: .5, // Set the width to half of the original, the other half will overflow
                child: avatar,
              ),
              Text("Hello World", style: TextStyle(color: Colors.green),)
            ],
          ),
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              ClipRect( // Clip the overflowed part
                child: Align(
                  alignment: Alignment.topLeft,
                  widthFactor: .5, // Set the width to half of the original
                  child: avatar,
                ),
              ),
              Text("Hello World", style: TextStyle(color: Colors.green))
            ],
          ),
        ],
      ),
    );
  }
}

The result is shown in Figure :

Flutter (34): Clip

The comments in the example code are detailed, so no further explanation is needed here. However, it's worth noting the last two Row widgets. By setting widthFactor to 0.5 through Align, the image’s actual width becomes 60 × 0.5, which is half of the original width. However, the overflowed part of the image will still be displayed, causing the first "Hello World" text to overlap with the other part of the image. To clip the overflow, we use ClipRect in the second Row to remove the overflowed part.


34.2 Custom Clipping (CustomClipper)

If we want to clip a specific area of the child component, for instance, if we only want to extract the middle 40 × 30 pixel area of the image in the above example, how can we do that? In this case, we can use CustomClipper to define the clipping area. Here’s how we can do it:

First, define a custom CustomClipper:

class MyClipper extends CustomClipper<Rect> {
  @override
  Rect getClip(Size size) => Rect.fromLTWH(10.0, 15.0, 40.0, 30.0);

  @override
  bool shouldReclip(CustomClipper<Rect> oldClipper) => false;
}

The getClip() method is used to get the clipping area. Since the image size is 60 × 60, we return the clipping area as Rect.fromLTWH(10.0, 15.0, 40.0, 30.0), which represents a 40 × 30 pixel region in the center of the image.

The shouldReclip() method determines whether the clipping needs to be recalculated. If the clipping area does not change in the application, return false to avoid unnecessary performance overhead. If the clipping area changes (for example, when animating the clipping area), return true to recalculate the clipping.

Next, we use ClipRect to apply the custom clipping. To make the image's actual size visible, we set a red background:

DecoratedBox(
  decoration: BoxDecoration(
    color: Colors.red
  ),
  child: ClipRect(
    clipper: MyClipper(), // Use the custom clipper
    child: avatar
  ),
)

The result is shown in Figure :

Flutter (34): Clip

We can see that the clipping is successful, but the size of the image remains 60 × 60 (the red area). This is because the component's size is determined during the layout phase, and the clipping occurs during the painting phase, so it does not affect the size of the component. This is similar to the principle of Transform.

ClipPath can clip components along a custom path. It requires a custom CustomClipper<Path> clipper, which is defined similarly to the MyClipper class. The getClip method should return a Path instead of a Rect, and we won't go into further detail here.