Flutter 实现商品交易节点进度效果

本文介绍了如何在Flutter中创建商品交易的水平进度条,支持自定义样式和动态进度。通过HorizontalNodeProgressViewwidget,结合ProgressBars数据,实现了节点进度的动态计算和展示。
摘要由CSDN通过智能技术生成

Flutter 实现商品交易水平进度效果

Flutter 是一种流行的移动应用程序开发框架,它提供了许多有用的工具和库来简化应用程序的开发过程。在 Flutter 中,可以轻松地实现商品交易的水平进度,让应用程序更具吸引力和用户体验。本文将介绍如何使用 Flutter 实现商品交易的水平进度条,支持自定义样式以及动态进度。

效果如下:

​​​​请添加图片描述

废话不多说,直接上代码,如下:

HorizontalNodeProgressView(
     bars: progressBars, /// 动态节点数据
     isComplete: false, /// 是否完成节点 (完成节点 整体置灰色)
);

自定义水平进度节点(HorizontalNodeProgressView),代码如下:

import 'package:flutter/material.dart';
import "package:jm_foundation/jm_foundation.dart";
import 'package:jm_order/app/modules/order_detail/model/order_model.dart';


class HorizontalNodeProgressView extends StatefulWidget {
  const HorizontalNodeProgressView({Key? key, required this.bars, required this.isComplete})
      : super(key: key);

  final List<ProgressBars> bars;
  final bool isComplete;

  
  State<HorizontalNodeProgressView> createState() => _HorizontalNodeProgressViewState();
}

class _HorizontalNodeProgressViewState extends State<HorizontalNodeProgressView> {
  /// 左边 已完成节点颜色
  final Color leftColor = Colors.blue;
  final Color rightColor = Colors.grey;
  /// 当前完成节点
  late int finishIndex;
  bool isComplete = false;
  List<bool> values = [];

  double progressTitleSize = 11;

  
  Widget build(BuildContext context) {
    for (int i = 0; i < widget.bars.length; i++) {
      values.add(widget.bars[i].bools!);
      if (widget.bars[i].bools == false) {
        finishIndex = i;
        break;
      } else {
        finishIndex = widget.bars.length;
      }
    }
    if (finishIndex == 0 && !widget.isComplete) {
      finishIndex += 1;
    }
    if (!values.contains(false)) {
      isComplete = true;
    }

    /// 用于计算完成节点数
    int computeFinishIndex = finishIndex;
    if (computeFinishIndex >= widget.bars.length) {
      /// 完成节点数不能超过实际节点数
      computeFinishIndex = widget.bars.length - 1;
    }

    return SizedBox(
      height: 50,
      child: LayoutBuilder(
        builder: (BuildContext context, BoxConstraints constraints) {
          double availableWidth = constraints.maxWidth;

          List<double> nodeTitleWidths = [];
          double nodeTitleTotalWidth = 0;
          double nodeTitleEventWidth = 0;

          /// 进度线列表
          List<Widget> lineList = [];

          /// 节点列表
          List<Widget> nodeList = [];

          for (int index = 0; index < widget.bars.length; index++) {
            if (index < widget.bars.length) {
              String title = widget.bars[index].name!;

              double titleWidth = title
                  .computeParagraphSize(
                    textStyle: TextStyle(fontSize: progressTitleSize),
                  )
                  .width;
              nodeTitleWidths.add(titleWidth);

              nodeTitleTotalWidth += titleWidth;
              nodeTitleEventWidth = nodeTitleTotalWidth / (index + 1);

              nodeList.add(
                _nodeView(title, index),
              );
            } else {
              nodeTitleWidths.add(nodeTitleEventWidth);
              nodeTitleTotalWidth += nodeTitleEventWidth;
              nodeList.add(SizedBox(
                width: nodeTitleEventWidth,
              ));
            }
          }

          /// 每个节点间隔
          double side = ((availableWidth - nodeTitleTotalWidth) ~/
                  (widget.bars.length - 1))
              .toDouble();

          for (int index = 0; index < widget.bars.length; index++) {
            if (index < (widget.bars.length - 1)) {
              lineList.add(_lineView(
                  index,
                  ((nodeTitleWidths[index + 1] / 2) +
                      (nodeTitleWidths[index] / 2) +
                      side)));
            }
          }

          return Stack(
            children: [
              Positioned(
                left: nodeTitleWidths.first / 2,
                right: nodeTitleWidths.last / 2,
                child: Row(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: lineList,
                ),
              ),
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: nodeList,
              )
            ],
          );
        },
      ),
    );
  }

  Widget _lineView(int index, double width) {
    Color lineColor = Colors.transparent;
    double leftPadding = 0;
    double rightPadding = 0;
    if (index < (widget.bars.length - 1)) {
      if (index < finishIndex) {
        lineColor = leftColor;
        leftPadding = 4;
        if (index < (finishIndex - 1)) {
          rightPadding = 4;
        }
      } else {
        lineColor = rightColor;
      }
    }

    return SizedBox(
      width: width,
      child: Align(
        alignment: Alignment.topCenter,
        child: Padding(
          padding: EdgeInsets.fromLTRB(leftPadding, 11.65, rightPadding, 0),
          child: Container(
            height: 0.5,
            color: lineColor,
          ),
        ),
      ),
    );
  }

  Widget _nodeView(String element, int index) {
    Color titleColor;
    FontWeight weight;
    if (index < finishIndex) {
      titleColor = Colors.grey;
      weight = FontWeight.w400;
    } else if (index == finishIndex) {
      titleColor = leftColor;
      weight = FontWeight.w500;
    } else {
      titleColor = rightColor;
      weight = FontWeight.w400;
    }

    return Column(
      children: [
        SizedBox(
          width: 24,
          height: 24,
          child: Center(
            child: (index < finishIndex)
                ? Icon(
                    Icons.check_circle,
                    size: 16,
                    color: leftColor,
                  )
                : Container(
                    width: 8,
                    height: 8,
                    decoration: BoxDecoration(
                      color: widget.isComplete
                          ? rightColor
                          : (index > finishIndex ? rightColor : leftColor),
                      borderRadius: BorderRadius.circular(4),
                    ),
                  ),
          ),
        ),
        Expanded(
          child: Text(
            element,
            textAlign: TextAlign.center,
            style: TextStyle(
                color: widget.isComplete
                    ? rightColor
                    : ((index == widget.bars.length - 1 && isComplete)
                        ? leftColor
                        : titleColor),
                fontSize: progressTitleSize,
                fontWeight: widget.isComplete
                    ? FontWeight.w400
                    : ((index == widget.bars.length - 1 && isComplete)
                        ? FontWeight.w400
                        : weight)),
          ),
        ),
      ],
    );
  }
}

重点代码如下,是通过动态的节点计算每个节点的宽度

for (int index = 0; index < widget.bars.length; index++) {
            if (index < widget.bars.length) {
              String title = widget.bars[index].name!;

              double titleWidth = title
                  .computeParagraphSize(
                    textStyle: TextStyle(fontSize: progressTitleSize),
                  )
                  .width;
              nodeTitleWidths.add(titleWidth);

              nodeTitleTotalWidth += titleWidth;
              nodeTitleEventWidth = nodeTitleTotalWidth / (index + 1);

              nodeList.add(
                _nodeView(title, index),
              );
            } else {
              nodeTitleWidths.add(nodeTitleEventWidth);
              nodeTitleTotalWidth += nodeTitleEventWidth;
              nodeList.add(SizedBox(
                width: nodeTitleEventWidth,
              ));
            }
          }

完结

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值