如何完善Flutter中基于Syncfusion的动态分段仪表盘UI
使用syncfusion_flutter_gauges实现指定仪表盘UI的问题解决
待解决问题
- 指针后的填充颜色效果未实现
- 仪表盘无法实现均等分段
- 如何保证仪表盘在全设备上的响应式适配
已实现代码
仪表盘主体代码
return SfRadialGauge( title: GaugeTitle( text: 'Speedometer', textStyle: const TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold)), axes: <RadialAxis>[ RadialAxis( minimum: 0, maximum: 320, startAngle: 180, endAngle: 0, // showLabels: false, maximumLabels: 320, onLabelCreated: _handleLabelCreated, canRotateLabels: true, labelsPosition: ElementsPosition.outside, ticksPosition: ElementsPosition.outside, showTicks: false, radiusFactor: 1.1, axisLineStyle: AxisLineStyle( thickness: 5, dashArray: <double>[(320+100) / 5, 3], gradient: SweepGradient(colors: <Color>[ Color(0xFFE7627D), Color(0xFF231557), Color(0xFF44107A), Color(0xFFFF1361), Color(0xFFFFF800), ], stops: <double>[ 0, 100, 190, 240, 320 ]) ), annotations: <GaugeAnnotation>[ GaugeAnnotation( angle: 180, horizontalAlignment: GaugeAlignment.near, positionFactor: 0.78, verticalAlignment: GaugeAlignment.near, widget: Text( "0", style: TextStyle( color: Colors.black, fontWeight: FontWeight.bold, fontSize: 14, ), )), GaugeAnnotation( angle: 0, horizontalAlignment: GaugeAlignment.far, positionFactor: 0.85, verticalAlignment: GaugeAlignment.near, widget: Text( "320", style: TextStyle( color: Colors.black, fontWeight: FontWeight.bold, fontSize: 14, ), )), GaugeAnnotation( axisValue: 100, positionFactor: 0.5, angle: 90, widget: Column(children: [ Center( child: RichText( textAlign: TextAlign.center, text: TextSpan( text: '223\n', style: TextStyle( color: Colors.black, fontWeight: FontWeight.bold, fontSize: 30, ), children: [ TextSpan( text: 'Points earned this month', style: TextStyle( color: Colors.black, fontWeight: FontWeight.normal, fontSize: 12, ), ) ], )), ) ])) ], pointers: <GaugePointer>[ MarkerPointer( value: 223, markerHeight: 15, markerWidth: 15, enableDragging: true, overlayRadius: 12, borderColor: Colors.green, borderWidth: 2, color: Colors.white, markerType: MarkerType.circle) ], ) ]);
标签处理代码
void _handleLabelCreated(AxisLabelCreatedArgs args) { if (args.text == '30') { args.text = '£0 - £4'; } else if (args.text == '100') { args.text = '£4 - £8'; } else if (args.text == '165') { args.text = '£8 - £12'; } else if (args.text == '235') { args.text = '£12 - £16'; } else if (args.text == '290') { args.text = '£16 - £20'; } else { args.text = ''; } }
效果对比
目标UI效果

当前完成效果

解决方案
1. 实现指针后的填充颜色效果
通过添加RangePointer实现指针到起点的填充色,将其放在MarkerPointer之前,确保显示在指针下方:
pointers: <GaugePointer>[ // 指针填充色 RangePointer( value: 223, // 与MarkerPointer的值保持一致 width: 5, // 与axisLine的thickness一致 gradient: SweepGradient( colors: const [ Color(0xFFE7627D), Color(0xFF231557), Color(0xFF44107A), Color(0xFFFF1361), ], stops: const [0, 0.3125, 0.6, 0.75], // 对应分段比例 ), cornerStyle: CornerStyle.bothFlat, ), MarkerPointer( value: 223, markerHeight: 15, markerWidth: 15, enableDragging: true, overlayRadius: 12, borderColor: Colors.green, borderWidth: 2, color: Colors.white, markerType: MarkerType.circle ) ],
动态更新时同步修改两个指针的value即可。
2. 实现均等分段
最大值320分为5段,每段64,修改RadialAxis配置:
RadialAxis( minimum: 0, maximum: 320, interval: 64, // 均等分段间隔 startAngle: 180, endAngle: 0, showLabels: true, maximumLabels: 6, // 0/64/128/192/256/320共6个标签 onLabelCreated: (args) { switch(args.value.toInt()) { case 0: args.text = '£0 - £4'; break; case 64: args.text = '£4 - £8'; break; case 128: args.text = '£8 - £12'; break; case 192: args.text = '£12 - £16'; break; case 256: args.text = '£16 - £20'; break; case 320: args.text = ''; break; default: args.text = ''; } }, axisLineStyle: AxisLineStyle( thickness: 5, dashArray: const [12.8, 3], // 每段虚线长度均等 gradient: SweepGradient( colors: const [ Color(0xFFE7627D), Color(0xFF231557), Color(0xFF44107A), Color(0xFFFF1361), Color(0xFFFFF800), ], stops: const [0, 0.2, 0.4, 0.6, 1], // 均等渐变节点 ), ), // 其他配置保持不变 )
3. 响应式适配方案
结合MediaQuery和LayoutBuilder动态调整尺寸:
LayoutBuilder( builder: (context, constraints) { final screenWidth = MediaQuery.of(context).size.width; final gaugeSize = screenWidth * 0.9; // 占屏幕宽度90% final baseFontSize = screenWidth * 0.03; return SizedBox( width: gaugeSize, height: gaugeSize, child: SfRadialGauge( title: GaugeTitle( text: 'Speedometer', textStyle: TextStyle( fontSize: baseFontSize * 0.5, fontWeight: FontWeight.bold ), ), axes: <RadialAxis>[ RadialAxis( radiusFactor: 0.9, labelStyle: TextStyle(fontSize: baseFontSize * 0.4), annotations: <GaugeAnnotation>[ GaugeAnnotation( widget: Text( "0", style: TextStyle( fontSize: baseFontSize * 0.4, fontWeight: FontWeight.bold ), ), // 其他标注配置 ), ], pointers: <GaugePointer>[ MarkerPointer( markerHeight: screenWidth * 0.04, markerWidth: screenWidth * 0.04, // 其他指针配置 ) ], // 其他轴配置 ) ], ), ); }, )
将所有可缩放元素(尺寸、字体、指针大小)设置为屏幕宽度的比例,确保跨设备自适应。
内容的提问来源于stack exchange,提问作者Sachin Suthar
相关产品推荐
相关产品推荐

