Flutter: Making clipped area transparent for scrolling ListView

As I have learnt it from @pskink (thanks for that) in use cases like this where you need the widget to actually adjust its boundaries (spoiler: shape) you should make use of the shape property of different widgets and use the Path you used for this example in a custom class which extends ShapeBorder. Easiest approach would be:

Container(
  height: 370,
  decoration: ShapeDecoration(
    color: Colors.blue,
    shape: AppBarBorder(),
    /// You can also specify some neat shadows to cast on widgets scrolling under this one
    shadows: [
      BoxShadow(
        color: Colors.black.withOpacity(0.7),
        blurRadius: 18.0,
        spreadRadius: 2.0,
      ),
    ],
  ),
),

And the custom class:

class AppBarBorder extends ShapeBorder {
  @override
  Path getOuterPath(Rect rect, {TextDirection textDirection}) {
    Offset controllPoint1 = Offset(0, rect.size.height - 100);
    Offset endPoint1 = Offset(100, rect.size.height - 100);
    Offset controllPoint2 = Offset(rect.size.width, rect.size.height - 100);
    Offset endPoint2 = Offset(rect.size.width, rect.size.height - 200);
    
    return Path()
      ..lineTo(0, rect.size.height)
      ..quadraticBezierTo(
          controllPoint1.dx, controllPoint1.dy, endPoint1.dx, endPoint1.dy)
      ..lineTo(rect.size.width - 100, rect.size.height - 100)
      ..quadraticBezierTo(
          controllPoint2.dx, controllPoint2.dy, endPoint2.dx, endPoint2.dy)
      ..lineTo(rect.size.width, 0);
  }

  @override
  EdgeInsetsGeometry get dimensions => EdgeInsets.only(bottom: 0);

  @override
  Path getInnerPath(Rect rect, {TextDirection textDirection}) => null;

  @override
  void paint(Canvas canvas, Rect rect, {TextDirection textDirection}) {}

  @override
  ShapeBorder scale(double t) => this;
}

Pretty much the same approach how you would declare a CustomClipper or CustomPainter since you don’t need to implement most of those methods and essentially only need to care about getOuterPath.

At the end we need to restructure the layout itself, since currently you have a Column with this custom Container shape and the ListView beneath that. Since the Container is not part of the ListView it can’t be scrolled under or something. Easiest approach would be using a Stack:

Stack(
  children: [
    Expanded(
      child: ListView(
        padding: EdgeInsets.only(top: 370.0),
        itemExtent: 100,
        children: <Widget>[
          Card(
            color: Colors.green,
          ),
        ],
      ),
    ),
    Container(
      height: 370,
      decoration: ShapeDecoration(
        color: Colors.blue,
        shape: AppBarBorder(),
        shadows: [
          BoxShadow(
            color: Colors.black.withOpacity(0.7),
            blurRadius: 18.0,
            spreadRadius: 2.0,
          ),
        ],
      ),
    ),
  ],
),

CLICK HERE to find out more related problems solutions.

Leave a Comment

Your email address will not be published.

Scroll to Top