Lebecca
Lebecca

Reputation: 2878

How to create widget with transparent hole in flutter

I want to create a hole in any Flutter widget. For example, say we have two widgets in a Stack, I want to dig a hole in the upper one in order to make the below widget visible to users.

There is a question with a similar title but the accepted answer does not match the question exactly. What is achieved by the answer is to dig a hole in a single-color overlay, not on a complete widget.

I have found that with CustomPaint we can draw widgets in three layers:

class BasicExample extends StatelessWidget {
  const BasicExample({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return CustomPaint(
      child: Image.asset(
        "images/flutter.jpg",
        fit: BoxFit.cover,
      ),
      foregroundPainter: DemoPainter(Colors.red.withOpacity(0.5)),
      painter: DemoPainter(Colors.blue.withOpacity(0.5)),
      size: Size.square(200.0),
      willChange: true,
    );
  }
}

But I cannot find a mechanism like ColorFiltered mentioned in the above answer. Is there something called WidgetFiltered, as a counterpart in the widget world to the single-color overlay world, that we can use to handle the render of two overlapped widgets (for example, the painter & child in the example code)?

Upvotes: 2

Views: 1191

Answers (1)

Md. Yeasin Sheikh
Md. Yeasin Sheikh

Reputation: 63699

I am using ClipPath while included answer already used ColorFilter.

Change slider value to change hole size

Run on dartPad

class WidgetHole extends StatefulWidget {
  const WidgetHole({Key? key}) : super(key: key);

  @override
  State<WidgetHole> createState() => _WidgetHoleState();
}

class _WidgetHoleState extends State<WidgetHole> {
  double dimension = 52;

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) => Scaffold(
        backgroundColor: Colors.pink,
        appBar: AppBar(
          backgroundColor: Colors.purple,
          title: Slider(
            value: dimension,
            max: constraints.maxWidth,
            onChanged: (v) {
              setState(() {
                dimension = v;
              });
            },
          ),
        ),
        body: ClipPath(
          clipper: CenterHolePath(dimension: dimension),
          child: Container(
            alignment: Alignment.center,
            color: Colors.yellow,
            child: Column(
              children: [
                Text("Hey this is background"),
                Expanded(
                    child: ListView.builder(
                  clipBehavior: Clip.hardEdge,
                  itemCount: 44,
                  itemBuilder: (context, index) {
                    return Center(
                      child: SizedBox(
                        height: kToolbarHeight,
                        child: Text("lItem  $index"),
                      ),
                    );
                  },
                ))
              ],
            ),
          ),
        ),
      ),
    );
  }
}

class CenterHolePath extends CustomClipper<Path> {
  final double dimension;
  CenterHolePath({
    required this.dimension,
  });

  @override
  Path getClip(Size size) {
    final rect = Rect.fromLTRB(0, 0, size.width, size.height);

    final path = Path()
      ..fillType = PathFillType.evenOdd
      ..addOval(
        Rect.fromCenter(
            center: Offset(size.width / 2, size.height / 2),
            width: dimension,
            height: dimension),
      )
      ..addRect(rect);

    return path;
  }

  @override
  bool shouldReclip(covariant CenterHolePath oldClipper) {
    return dimension != oldClipper.dimension;
  }
}

Where pink is scaffold background

enter image description here

Upvotes: 2

Related Questions