You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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: // 温度计核心内容
        );
      },
    );
  }
}

二、设计样式精准还原

  1. 提取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;
  1. 用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控制填充比例,实现颜色条从右侧刻度线向上移动的平滑动画:

  1. 初始化动画控制器:
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();
}
  1. 将动画值绑定到CustomPainter:
AnimatedBuilder(
  animation: _fillAnimation,
  builder: (context, child) {
    return CustomPaint(
      painter: ThermometerPainter(
        fillRatio: _fillAnimation.value,
        scaleFactor: _scaleFactor,
      ),
    );
  },
)

内容的提问来源于stack exchange,提问作者PNT

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 20:32:35