Просмотр виджета 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),
),
],
);
}