自定义柱状图

2023-08-06  本文已影响0人  卢融霜

自定义一个柱状图
[图片上传失败...(image-9a235c-1691401253525)]

import 'package:flutter/material.dart';

import 'dotted_dashed_line.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  List<ListColumn> list = [
    ListColumn("周一", [
      ColumnItem(0, Colors.green),
      ColumnItem(0, Colors.red),
      ColumnItem(0, Colors.blue)
    ]),
    ListColumn("周二", [
      ColumnItem(0, Colors.green),
      ColumnItem(0, Colors.red),
      ColumnItem(0, Colors.blue)
    ]),
    ListColumn(
      "周三",
      [
        ColumnItem(0, Colors.green),
        ColumnItem(0, Colors.red),
        ColumnItem(0, Colors.blue)
      ],
    ),
    ListColumn(
      "周四",
      [
        ColumnItem(0, Colors.green),
        ColumnItem(0, Colors.red),
        ColumnItem(0, Colors.blue)
      ],
    ),
    ListColumn("周五", [
      ColumnItem(0, Colors.green),
      ColumnItem(0, Colors.red),
      ColumnItem(0, Colors.blue)
    ]),
    ListColumn(
      "周六",
      [
        ColumnItem(0, Colors.green),
        ColumnItem(0, Colors.red),
        ColumnItem(0, Colors.blue)
      ],
    ),
    ListColumn("周日", [
      ColumnItem(0, Colors.green),
      ColumnItem(0, Colors.red),
      ColumnItem(0, Colors.blue)
    ])
  ];

  @override
  void initState() {
    super.initState();
    Future.delayed(const Duration(seconds: 4), () {
      list = [
        ListColumn("周一", [
          ColumnItem(20, Colors.green),
          ColumnItem(10, Colors.red),
          ColumnItem(20, Colors.blue)
        ]),
        ListColumn("周二", [
          ColumnItem(50, Colors.green),
          ColumnItem(60, Colors.red),
          ColumnItem(20, Colors.blue)
        ]),
        ListColumn(
          "周三",
          [
            ColumnItem(40, Colors.green),
            ColumnItem(30, Colors.red),
            ColumnItem(10, Colors.blue)
          ],
        ),
        ListColumn(
          "周四",
          [
            ColumnItem(60, Colors.green),
            ColumnItem(10, Colors.red),
            ColumnItem(20, Colors.blue)
          ],
        ),
        ListColumn("周五", [
          ColumnItem(15, Colors.green),
          ColumnItem(15, Colors.red),
          ColumnItem(5, Colors.blue)
        ]),
        ListColumn(
          "周六",
          [
            ColumnItem(40, Colors.green),
            ColumnItem(12, Colors.red),
            ColumnItem(8, Colors.blue)
          ],
        ),
        ListColumn("周日", [
          ColumnItem(22, Colors.green),
          ColumnItem(13, Colors.red),
          ColumnItem(16, Colors.blue)
        ])
      ];
      setState(() {});
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
            backgroundColor: Theme.of(context).colorScheme.inversePrimary,
            title: Text(widget.title)),
        body: HistogramWidget(height: 300, list: list));
  }
}

class HistogramWidget extends StatefulWidget {
  final double height;
  final List<ListColumn> list;

  const HistogramWidget({required this.height, required this.list, super.key});

  @override
  State<HistogramWidget> createState() => _HistogramWidgetState();
}

class _HistogramWidgetState extends State<HistogramWidget> {
  double px = 1;

  ///误差线
  List<int> lines = [];

  ///获取数组最大数
  double findMax(List<double> numbers) {
    if (numbers.isEmpty) {
      return 0;
    }
    double max = numbers[0];
    for (double number in numbers) {
      if (number > max) {
        max = number;
      }
    }
    return max;
  }

  ///获取误差线
  List<int> calculateNodes(double inputNumber) {
    var item = nearestMultipleOf5(nearestMultipleOf5(inputNumber ~/ 2) ~/ 2);
    return [0, item, item * 2, item * 3, item * 4];
  }

  ///获取就近 大于当前的5的倍数
  int nearestMultipleOf5(int number) {
    int nextMultiple = ((number ~/ 5) + 1) * 5;
    return nextMultiple;
  }

  @override
  Widget build(BuildContext context) {
    ///获取比例
    List<double> ints = [];
    for (var element in widget.list) {
      double total = 0;
      for (var element in element.list) {
        total += element.value;
      }
      ints.add(total);
    }

    ///获取最大数
    double max = findMax(ints);
    if (max == 0) {
      max = 100;
    }

    ///获取误差线
    lines = calculateNodes(max);
    px = widget.height / lines[lines.length - 1];

    return SizedBox(
        height: (widget.height + 40),
        child: Column(
            mainAxisSize: MainAxisSize.max,
            mainAxisAlignment: MainAxisAlignment.end,
            children: [
              Expanded(child: Stack(fit: StackFit.expand, children: getLines()))
            ]));
  }

  getColumnChildren() {
    List<Widget> children = [];
    for (var element in widget.list) {
      children.add(ColumnWidget(listColumn: element, px: px));
    }
    return children;
  }

  List<Widget> getLines() {
    List<Widget> lis = [];
    for (var element in lines) {
      lis.add(Positioned(
          bottom: element * px,
          left: 0,
          right: 0,
          child: XLine(title: "$element")));
    }
    lis.add(Align(
        alignment: Alignment.bottomCenter,
        child: AnimatedContainer(
            padding: const EdgeInsets.only(left: 30, right: 10),
            duration: const Duration(seconds: 1),
            child: Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                crossAxisAlignment: CrossAxisAlignment.end,
                children: getColumnChildren()))));

    return lis;
  }
}

class ColumnWidget extends StatelessWidget {
  final ListColumn listColumn;
  final double px;

  const ColumnWidget({required this.listColumn, required this.px, super.key});

  @override
  Widget build(BuildContext context) {
    return Transform.translate(
      offset: const Offset(0, 20),
      child: Column(
        children: [
          Expanded(
              child: Align(
                  alignment: Alignment.bottomCenter,
                  child: Column(
                    mainAxisSize: MainAxisSize.min,
                    children: getChildren(listColumn.list),
                  ))),
          SizedBox(
              height: 20,
              child: Center(
                  child: Text(listColumn.title,
                      style: const TextStyle(color: Colors.red))))
        ],
      ),
    );
  }

  List<Widget> getChildren(List<ColumnItem> listValue) {
    List<Widget> children = [];
    for (int i = 0; i < listValue.length; i++) {
      children.add(AnimatedContainer(
        width: 21,
        height: listValue[i].value * px,
        decoration: BoxDecoration(
            borderRadius: i == 0
                ? const BorderRadius.only(
                    topLeft: Radius.circular(3), topRight: Radius.circular(3))
                : null,
            color: listValue[i].color),
        duration: const Duration(seconds: 1),
      ));
    }
    return children;
  }
}

class ListColumn {
  final String title;
  final List<ColumnItem> list;

  const ListColumn(this.title, this.list);
}

class ColumnItem {
  final double value;
  final Color color;

  ColumnItem(this.value, this.color);
}

class XLine extends StatelessWidget {
  final String title;

  const XLine({required this.title, super.key});

  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(title),
        const DottedDashedLine(
            height: 1, width: double.infinity, axis: Axis.horizontal)
      ],
    );
  }
}
上一篇下一篇

猜你喜欢

热点阅读