Flutter温度计组件适配多屏幕及还原设计优化求助
Flutter温度计组件适配与样式优化方案
一、多屏幕适配改造
替换固定尺寸为基于屏幕比例的动态计算值,用MediaQuery或LayoutBuilder获取容器/屏幕尺寸生成适配因子,避免硬写固定数值:
- 以Figma设计稿的基准宽度(比如375px)为参照,计算当前屏幕与基准的缩放比
- 所有组件尺寸、字体、间距都乘以该缩放比,确保在不同设备上比例一致
示例代码片段:
class Thermometer extends StatefulWidget { @override _ThermometerState createState() => _ThermometerState(); } class _ThermometerState extends State<Thermometer> { double _scaleFactor = 1.0; final double _designBaseWidth = 375; // Figma设计稿基准宽度 @override Widget build(BuildContext context) { // 实时计算缩放因子 _scaleFactor = MediaQuery.of(context).size.width / _designBaseWidth; return LayoutBuilder( builder: (context, constraints) { return Container( width: constraints.maxWidth * 0.3 * _scaleFactor, height: constraints.maxHeight * 0.8 * _scaleFactor, child: // 温度计核心内容 ); }, ); } }
二、设计样式精准还原
- 提取Figma精准参数:从设计稿导出颜色、圆角、刻度线粗细/间距、字体样式,定义为常量:
const Color _borderColor = Color(0xFFE0E0E0); const Color _fillGradientStart = Color(0xFFFF6B6B); const Color _fillGradientEnd = Color(0xFFFFB344); const double _borderWidth = 2.0; const double _topCornerRadius = 8.0; const double _bottomBulbRadius = 12.0;
- 用CustomPaint绘制核心部件:替代嵌套Container实现复杂形状,确保与设计稿路径完全匹配:
class ThermometerPainter extends CustomPainter { final double fillRatio; final double scaleFactor; ThermometerPainter({required this.fillRatio, required this.scaleFactor}); @override void paint(Canvas canvas, Size size) { // 绘制温度计外框路径 final borderPath = Path() ..moveTo(size.width / 2, size.height) ..arcToPoint(Offset(size.width / 2, size.height - _bottomBulbRadius * scaleFactor), radius: Radius.circular(_bottomBulbRadius * scaleFactor), clockwise: false) ..lineTo(size.width / 2, _topCornerRadius * scaleFactor) ..arcToPoint(Offset(size.width, _topCornerRadius * scaleFactor), radius: Radius.circular(_topCornerRadius * scaleFactor)) ..lineTo(size.width, size.height - _bottomBulbRadius * scaleFactor) ..arcToPoint(Offset(size.width / 2, size.height), radius: Radius.circular(_bottomBulbRadius * scaleFactor), clockwise: false); // 绘制外框 final borderPaint = Paint() ..color = _borderColor ..strokeWidth = _borderWidth * scaleFactor ..style = PaintingStyle.stroke; canvas.drawPath(borderPath, borderPaint); // 绘制填充颜色条 final fillHeight = size.height * fillRatio; final fillPath = Path() ..moveTo(size.width, size.height - fillHeight) ..lineTo(size.width, size.height - _bottomBulbRadius * scaleFactor) ..arcToPoint(Offset(size.width / 2, size.height), radius: Radius.circular(_bottomBulbRadius * scaleFactor), clockwise: false) ..arcToPoint(Offset(0, size.height - _bottomBulbRadius * scaleFactor), radius: Radius.circular(_bottomBulbRadius * scaleFactor), clockwise: false) ..lineTo(0, size.height - fillHeight) ..close(); final fillPaint = Paint() ..shader = LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [_fillGradientStart, _fillGradientEnd], ).createShader(Rect.fromLTWH(0, size.height - fillHeight, size.width, fillHeight)); canvas.drawPath(fillPath, fillPaint); } @override bool shouldRepaint(covariant ThermometerPainter oldDelegate) { return oldDelegate.fillRatio != fillRatio; } }
三、交互效果实现
用AnimationController控制填充比例,实现颜色条从右侧刻度线向上移动的平滑动画:
- 初始化动画控制器:
late AnimationController _controller; late Animation<double> _fillAnimation; @override void initState() { super.initState(); _controller = AnimationController( duration: const Duration(milliseconds: 1500), vsync: this, ); // 动画从0到目标填充比例,可根据业务逻辑调整终点值 _fillAnimation = Tween<double>(begin: 0, end: 0.75).animate(CurvedAnimation(parent: _controller, curve: Curves.easeInOut)); // 触发动画,可绑定到按钮点击或数据更新事件 _controller.forward(); } @override void dispose() { _controller.dispose(); super.dispose(); }
- 将动画值绑定到CustomPainter:
AnimatedBuilder( animation: _fillAnimation, builder: (context, child) { return CustomPaint( painter: ThermometerPainter( fillRatio: _fillAnimation.value, scaleFactor: _scaleFactor, ), ); }, )
内容的提问来源于stack exchange,提问作者PNT
相关产品推荐
相关产品推荐

