Flutter投票组件实现

Posted 怀君

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flutter投票组件实现相关的知识,希望对你有一定的参考价值。

前景

基于公司项目需求,仿照微博实现投票功能。

开发遇到的问题

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,
    );
  }
}

以上是关于Flutter投票组件实现的主要内容,如果未能解决你的问题,请参考以下文章

错误记录Flutter 混合开发获取 BinaryMessenger 报错 ( FlutterActivityAndFragmentDelegate.getFlutterEngine() )(代码片段

使用ivx实现投票的经验总结

VsCode 代码片段-提升研发效率

在 webview_flutter 中启用捏合和缩放,在哪里添加代码片段 [this.webView.getSettings().setBuiltInZoomControls(true);]

Flutter调用平台代码

Flutterflutter doctor 报错Android license status unknown. Run `flutter doctor --android-licenses‘(代码片段