Flutter自定义circle_wave环形波纹组件无法调整宽高问题求助
Flutter环形波纹组件尺寸调整方案
问题原因
- 原代码中CustomPaint外层使用
Positioned.fill布局,会强制占满父容器的全部可用空间,自定义的宽高约束无法生效 - 绘制逻辑中波纹的最大半径硬编码为固定值
165,不会跟随组件的实际尺寸变化
完整修改代码
import 'dart:math' as math; import 'dart:ui'; import 'package:flutter/material.dart'; // 原common.dart中的CounterText实现,可根据自己的需求调整样式 class CounterText extends StatelessWidget { final int counter; const CounterText({super.key, required this.counter}); @override Widget build(BuildContext context) { return Center( child: Text( '$counter', style: const TextStyle(color: Colors.white, fontSize: 80, fontWeight: FontWeight.bold), ), ); } } // 原common.dart中的map映射函数 double map(double value, double minIn, double maxIn, double minOut, double maxOut) { return (value - minIn) * (maxOut - minOut) / (maxIn - minIn) + minOut; } class DemoCircleWave extends StatefulWidget { const DemoCircleWave({ super.key, this.width = 350, this.height = 350, }); // 新增可自定义的宽高参数 final double width; final double height; @override _DemoCircleWaveState createState() => _DemoCircleWaveState(); } class _DemoCircleWaveState extends State<DemoCircleWave> with TickerProviderStateMixin { static const colors = <Color>[ Color(0xFFFF2964), Color(0xFF32FF3A), Color(0xFF4255FF) ]; late AnimationController controller; late AnimationController addPointController; late Animation<double> addPointAnimation; int _counter = 0; @override void initState() { super.initState(); controller = AnimationController( vsync: this, upperBound: 2, duration: const Duration(seconds: 10), )..repeat(); addPointController = AnimationController( vsync: this, duration: const Duration(milliseconds: 500), ); addPointAnimation = addPointController.drive(CurveTween(curve: Curves.ease)); } @override void dispose() { controller.dispose(); addPointController.dispose(); super.dispose(); } void _incrementCounter() { setState(() { _counter++; addPointController.forward(from: 0); }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Circle wave')), backgroundColor: Colors.black, body: Center( // 用SizedBox指定波纹组件的尺寸 child: SizedBox( width: widget.width, height: widget.height, child: Stack( alignment: Alignment.center, children: [ for (int i = 0; i < 3; i++) TweenAnimationBuilder( tween: Tween<double>(begin: 0, end: 1), duration: const Duration(milliseconds: 500), curve: Curves.easeIn, builder: (_, double opacity, __) { return CustomPaint( size: Size(widget.width, widget.height), painter: CircleWavePainter( controller, addPointAnimation, i, colors[i].withOpacity(opacity), _counter, ), ); }, ), CounterText(counter: _counter), ], ), ), ), floatingActionButton: FloatingActionButton( onPressed: _incrementCounter, tooltip: 'Increment', child: const Icon(Icons.add), ), ); } } class CircleWavePainter extends CustomPainter { CircleWavePainter( this.animation, this.addAnimation, this.index, this.color, this.count, ) : super(repaint: animation); final Animation<double> animation; final Animation<double> addAnimation; final int index; final Color color; final int count; static const halfPi = math.pi / 2; static const twoPi = math.pi * 2; final n = 7; @override void paint(Canvas canvas, Size size) { final t = animation.value; final halfWidth = size.width / 2; final halfHeight = size.height / 2; final q = index * halfPi; // 动态计算最大半径,减去描边宽度避免波纹超出边界 final maxRadius = math.min(halfWidth, halfHeight) - 4; List<Offset> computeOffsets(int length) { final offsets = <Offset>[]; for (var i = 0; i < length; i++) { final th = i * twoPi / length; double os = map(math.cos(th - twoPi * t), -1, 1, 0, 1); os = 0.125 * math.pow(os, 2.75); // 替换硬编码的165为动态计算的半径 final r = maxRadius * (1 + os * math.cos(n * th + 1.5 * twoPi * t + q)); offsets.add(Offset( r * math.sin(th) + halfWidth, -r * math.cos(th) + halfHeight)); } return offsets; } final offsets = computeOffsets(count); if (count > 1 && addAnimation.value < 1) { final t = addAnimation.value; final oldOffsets = computeOffsets(count - 1); for (var i = 0; i < count - 1; i++) { offsets[i] = Offset.lerp(oldOffsets[i], offsets[i], t)!; } offsets[count - 1] = Offset.lerp( oldOffsets[count - 2], offsets[count - 1], t, )!; } final path = Path()..addPolygon(offsets, true); canvas.drawPath( path, Paint() ..blendMode = BlendMode.lighten ..color = color ..strokeWidth = 8 ..style = PaintingStyle.stroke, ); } @override bool shouldRepaint(covariant CustomPainter oldDelegate) { return true; } }
使用方法
需要调整组件尺寸时,直接在实例化DemoCircleWave时传入宽高参数即可,示例:
DemoCircleWave(width: 200, height: 200)
内容的提问来源于stack exchange,提问作者DolDurma
相关产品推荐
相关产品推荐

