Flutter投票组件
怀君 人气:0前景
基于公司项目需求,仿照微博实现投票功能。
开发遇到的问题
1.选项列表的高度,自适应的问题;
2.进度条动画的问题;
3.列表回收机制,导致进度条动画重复;
4.自定义进度条四周圆角;
如何解决问题
- 拿到数组列表最长的数据,然后根据屏幕宽度计算,超出一行则设定两行高度,否则使用一行的高度;
_didExceedOneMoreLines(String text, double width, TextStyle style) { final span = TextSpan(text: text, style: style); final tp = TextPainter(text: span, maxLines: 1, textDirection: TextDirection.ltr); tp.layout(maxWidth: width); if (tp.didExceedMaxLines) { //设置item选项的高度 _itemHeight = 100.w; } }
- Widget控件初始化(initState)方法时,使用AnimationController动画,并实现SingleTickerProviderStateMixin,在build方法当中调用 _controller.animateTo()
AnimationController _controller; _controller = AnimationController( vsync: this, duration: const Duration(seconds: 1), )..addListener(() { setState(() {}); }); //触发动画,执行的位置 _controller.animateTo()
- 在列表数据当中给动画标记字段,让其是否执行动画;当用户投票成功,改变状态执行进度条动画。用户滑动列表之后,将标记改为false。关闭动画效果。
- 针对修改部分源码,设置进度条圆角控件;
import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; class RoundLinearProgressPainter extends ProgressIndicator { const RoundLinearProgressPainter({ Key key, double value, Color backgroundColor, Color color, Animation<Color> valueColor, this.minHeight, String semanticsLabel, String semanticsValue, }) : assert(minHeight == null || minHeight > 0), super( key: key, value: value, backgroundColor: backgroundColor, color: color, valueColor: valueColor, semanticsLabel: semanticsLabel, semanticsValue: semanticsValue, ); final double minHeight; @override _RoundLinearProgressPainterState createState() => _RoundLinearProgressPainterState(); } class _RoundLinearProgressPainterState extends State<RoundLinearProgressPainter> with SingleTickerProviderStateMixin { AnimationController _controller; @override void initState() { super.initState(); _controller = AnimationController( duration: const Duration(milliseconds: 1), vsync: this, )..addListener(() { setState(() {}); }); if (widget.value != null) _controller.forward(); } @override Widget build(BuildContext context) { return widget._buildSemanticsWrapper( context: context, child: Container( constraints: BoxConstraints( minWidth: double.infinity, minHeight: widget.minHeight ?? 4.0, ), child: CustomPaint( painter: _LinearProgressIndicatorPainter( backgroundColor: widget._getBackgroundColor(context), valueColor: widget._getValueColor(context), value: widget.value, animationValue: _controller.value, ), ), ), ); } @override void didUpdateWidget(RoundLinearProgressPainter oldWidget) { super.didUpdateWidget(oldWidget); if (widget.value == null && !_controller.isAnimating) _controller.repeat(); else if (widget.value != null && _controller.isAnimating) _controller.stop(); } @override void dispose() { _controller.dispose(); super.dispose(); } } class _LinearProgressIndicatorPainter extends CustomPainter { const _LinearProgressIndicatorPainter({ this.backgroundColor, this.valueColor, this.value, this.animationValue, }); final Color backgroundColor; final Color valueColor; final double value; final double animationValue; @override void paint(Canvas canvas, Size size) { final Paint paint = Paint() ..color = backgroundColor ..isAntiAlias = true ..style = PaintingStyle.fill; canvas.drawRect(Offset.zero & size, paint); paint.color = valueColor; void drawBar(double x, double width) { if (width <= 0.0) return; RRect rRect; ///圆角的宽度 var radius = Radius.circular(8.w); if (value == 1.0) { ///当进度条为1时,设置四周圆角 rRect = RRect.fromRectAndRadius( Offset(0.0, 0.0) & Size(width, size.height), radius); } else { ///小于1时,设置左侧圆角 rRect = RRect.fromRectAndCorners( Offset(0.0, 0.0) & Size(width, size.height), topLeft: radius, bottomLeft: radius); } canvas.drawRRect(rRect, paint); } if (value != null) { drawBar(0.0, value.clamp(0.0, 1.0) * size.width); } } @override bool shouldRepaint(_LinearProgressIndicatorPainter oldPainter) { return oldPainter.backgroundColor != backgroundColor || oldPainter.valueColor != valueColor || oldPainter.value != value || oldPainter.animationValue != animationValue; } } abstract class ProgressIndicator extends StatefulWidget { const ProgressIndicator({ Key key, this.value, this.backgroundColor, this.color, this.valueColor, this.semanticsLabel, this.semanticsValue, }) : super(key: key); final double value; final Color backgroundColor; final Color color; final Animation<Color> valueColor; final String semanticsLabel; final String semanticsValue; Color _getBackgroundColor(BuildContext context) => backgroundColor ?? Theme.of(context).colorScheme.background; Color _getValueColor(BuildContext context) => valueColor?.value ?? color ?? Theme.of(context).colorScheme.primary; @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties.add(PercentProperty('value', value, showName: false, ifNull: '<indeterminate>')); } Widget _buildSemanticsWrapper({ BuildContext context, Widget child, }) { String expandedSemanticsValue = semanticsValue; if (value != null) { expandedSemanticsValue ??= '${(value * 100).round()}%'; } return Semantics( label: semanticsLabel, value: expandedSemanticsValue, child: child, ); } }
加载全部内容