Просмотр виджета Flutter в стеке

Итак, у меня есть этот стек:

      Stack(
  children:[
    Widget1(),
    Widget2(),
    Widget3(),
  ]);

они все в полноэкранном режиме, так что вы можете только видетьWidget3

Но я хочу «видеть сквозь» эти виджеты до нижнего в определенной области. Например, круг радиусом 100 в центре страницы или что-то в этом роде: сделайте прозрачную область приложения.

И мне бы очень понравилось, если бы я мог добавить виджет в стек, чтобы добиться этого, вместо того, чтобы оборачивать виджет, хотя это тоже сработает:

      Stack(
  children:[
    Widget1(),
    Widget2(),
    TransparentCenterArea(radius:50), // see through Widget2, to see Widget1
    Widget3(),
    TransparentCenterArea(radius:100), // see through Widget3, to see Widget2
  ]);

возможно ли что-то подобное? Я не могу придумать, как это сделать. Тем более, что я, возможно, захочу изменить радиус, чтобы получить «открывающую» анимацию или что-то в этом роде...

Теперь, в качестве надуманного примера, я пытаюсь использовать CustomPainter, но это действительно не работает, я вижу только Widget3.

      import 'package:flutter/material.dart';

class TransparentCenterArea extends StatelessWidget {
  final double radius;

  TransparentCenterArea({required this.radius});

  @override
  Widget build(BuildContext context) {
    return ClipRRect(
      borderRadius: BorderRadius.circular(radius),
      child: CustomPaint(
        size: Size.fromRadius(radius),
        painter: TransparentPainter(),
      ),
    );
  }
}

class TransparentPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    // Draw a transparent circle in the center of the widget
    final paint = Paint()..color = Colors.transparent;
    canvas.drawCircle(Offset(size.width / 2, size.height / 2), size.width / 2, paint);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return false;
  }
}

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      appBar: AppBar(
        title: Text('Transparent Center Area Example'),
      ),
      body: Stack(
        children: [
          Widget1(),
          Widget2(),
          TransparentCenterArea(radius: 50), // see through Widget2, to see Widget1
          Widget3(),
          TransparentCenterArea(radius: 100), // see through Widget3, to see Widget2
        ],
      ),
    ),
  ));
}

class Widget1 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.blue,
      child: Center(child: Text('Widget 1')),
    );
  }
}

class Widget2 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.green,
      child: Center(child: Text('Widget 2')),
    );
  }
}

class Widget3 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.orange,
      child: Center(child: Text('Widget 3')),
    );
  }
}

1 ответ

@RandalSchwartz все правильно сказал в своем комментарии. По его совету я пришел к такому выводу:

      class AnimatedOpening extends StatefulWidget {
  final double initialRadius;
  final double finalRadius;
  final Duration animationDuration;
  final Widget? child;

  AnimatedOpening({
    required this.initialRadius,
    required this.finalRadius,
    required this.animationDuration,
    this.child,
  });

  @override
  _AnimatedOpeningState createState() => _AnimatedOpeningState();
}

class _AnimatedOpeningState extends State<AnimatedOpening>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _animation;

  @override
  void initState() {
    super.initState();

    _controller = AnimationController(
      duration: widget.animationDuration,
      vsync: this,
    );

    _animation = Tween<double>(
      begin: widget.initialRadius,
      end: widget.finalRadius,
    ).animate(_controller);

    _controller.forward();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) => AnimatedBuilder(
        animation: _animation,
        builder: (context, child) => TransparentCenterArea(
          radius: _animation.value,
          child: child,
        ),
        child: widget.child,
      );
}

class TransparentCenterArea extends StatelessWidget {
  final double radius;
  final Widget? child;
  TransparentCenterArea({required this.radius, this.child});

  @override
  Widget build(BuildContext context) => ClipPath(
        clipper: TransparentClipper(radius: radius),
        child: child,
      );
}

class TransparentClipper extends CustomClipper<Path> {
  final double radius;
  TransparentClipper({required this.radius});

  @override
  Path getClip(Size size) => Path()
    ..addRect(Rect.fromPoints(Offset(0, 0), Offset(size.width, size.height)))
    ..addOval(Rect.fromCircle(
        center: Offset(size.width / 2, size.height / 2), radius: radius))
    ..fillType = PathFillType.evenOdd;

  @override
  bool shouldReclip(CustomClipper<Path> oldClipper) => true;
}

class Home extends StatelessWidget {
  const Home({super.key});

  @override
  Widget build(BuildContext context) => Stack(
        children: [
          Container(color: Colors.blue),
          AnimatedOpening(
            initialRadius: 0,
            finalRadius: 100,
            animationDuration: Duration(seconds: 2),
            child: Container(color: Colors.green),
          ),
          TransparentCenterArea(
            radius: 100,
            child: Container(color: Colors.orange),
          ),
        ],
      );
}
Другие вопросы по тегам