Flutter移动应用开发 - 数据可视化之数据面板交互界面(旋转饼图,柱状图,折线图,日历图)

0. 项目简介

项目想法脱胎于2023年服务外包大赛A18题 随手买(详情

整个APP思路如下:

在这里插入图片描述

这篇博客主要服务于管理员界面的数据概览功能

1. 效果展示

饼图

在这里插入图片描述

折线图

在这里插入图片描述

柱状图

在这里插入图片描述

日历热图

在这里插入图片描述

2. 代码

依赖如下

dev_dependencies:
  flutter_test:
    sdk: flutter

  # The "flutter_lints" package below contains a set of recommended lints to
  # encourage good coding practices. The lint set provided by the package is
  # activated in the `analysis_options.yaml` file located at the root of your
  # package. See that file for information about deactivating specific lint
  # rules and activating additional ones.
  
  flutter_screenutil: ^3.1.0
  flutter_echart: ^1.0.0
  fl_chart: 0.55.2

相关文件如下

'dataPage.dart'
'CircleView.dart'
'BarView.dart'
'DateView.dart'
'LineViewNew.dart'

// 数据准备
'draw_grid.dart';
'utils_date.dart';

dataPage.dart

import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'CircleView.dart';
import 'BarView.dart';
import 'DateView.dart';
import 'LineViewNew.dart';


class dataPage extends StatefulWidget {
  const dataPage({Key? key}) : super(key: key);
  
  State<dataPage> createState() => _dataPageState();
}

class _dataPageState extends State<dataPage> with SingleTickerProviderStateMixin{

  TabController? tabController;

  void initState() {
    // TODO: implement initState
    tabController = TabController(
      length: 4,
      vsync: this,
    );
    super.initState();
  }
  
  void dispose() {
    // TODO: implement dispose
    tabController!.dispose();
    super.dispose();
  }

  
  Widget build(BuildContext context) {
    ScreenUtil.init(context, allowFontScaling: false);

    return Scaffold(
      // backgroundColor: Colors.grey.shade300,
      backgroundColor: Color(0xff232d37),
      appBar: AppBar(
        title: TabBar(
          indicatorColor: Colors.white,
          indicatorSize: TabBarIndicatorSize.label,
          unselectedLabelStyle: TextStyle(
            fontSize: 15,
            fontWeight: FontWeight.w500,
          ),
          unselectedLabelColor: Colors.white54,
          labelColor: Colors.white,
          labelStyle: TextStyle(
              fontSize: 20,
              fontWeight: FontWeight.w500
          ),
          controller: tabController,
          tabs: <Widget>[
            Tab(text: '用户主体',),
            Tab(text: 'APP访问量',),
            Tab(text: '交易量',),
            Tab(text: '广告播放量',)
          ],
        ),
      ),

      body: TabBarView(
      controller: tabController,
      children: <Widget>[
        CircleView(),
        LineChartSample2(),
        BarView(),
        DateView(),
      ],
    ),
    );
  }
}


CircleView.dart

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/painting.dart';
import 'package:flutter_echart/flutter_echart.dart';

void main() {
  runApp(
    MaterialApp(
      debugShowCheckedModeBanner: false,
      home: CircleView(),
    ),
  );
}

class CircleView extends StatefulWidget {
  const CircleView({Key? key}) : super(key: key);

  
  State<CircleView> createState() => _CircleViewState();
}

// //定义一个全局的内容主颜色
// Color mainColor = Colors.transparent;


class _CircleViewState extends State<CircleView> {
  
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
  //页面的主内容 先来个居中
      body: Container(
        width: MediaQuery.of(context).size.width,
        height: MediaQuery.of(context).size.height,
        child: Stack(
          alignment: Alignment.center,
          children: [
            Container(
              //来个高度
              height: 230,
              //宽度填充
              width: MediaQuery.of(context).size.width,
              // //设置一下背景
              // color: mainColor,
              //封装一个方法构建左右排列的
              child: buildPieChatWidget(),
            ),
          ],
        ),
      ),
    );
  }

  List<EChartPieBean> _dataList = [
    EChartPieBean(title: "18岁以下", number: 2471, color: Colors.lightBlueAccent),
    EChartPieBean(title: "19~30岁", number: 44658, color: Colors.deepOrangeAccent),
    EChartPieBean(title: "31~40岁", number: 28617, color: Colors.green),
    EChartPieBean(title: "41~50岁", number: 15632, color: Colors.amber),
    EChartPieBean(title: "51~60岁", number: 4584, color: Colors.orange),
    EChartPieBean(title: "60岁以上", number: 8451, color: Colors.deepPurpleAccent),
  ];

  PieChatWidget buildPieChatWidget() {
    return PieChatWidget(
      dataList: _dataList,
      //是否输出日志
      isLog: true,
      //是否需要背景
      isBackground: true,
      //是否画直线
      isLineText: true,
      //背景
      bgColor: Colors.white,
      //是否显示最前面的内容
      isFrontgText: true,
      //默认选择放大的块
      initSelect: 1,
      //初次显示以动画方式展开
      openType: OpenType.ANI,
      //旋转类型
      loopType: LoopType.AUTO_LOOP,
      // //点击回调
      // clickCallBack: (int value) {
      //   print("当前点击显示 $value");
      // },
    );
  }
}


BarView.dart

import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';
import 'dart:math' as math;

class AppUtils {
  factory AppUtils() {
    return _singleton;
  }

  AppUtils._internal();
  static final AppUtils _singleton = AppUtils._internal();

  double degreeToRadian(double degree) {
    return degree * math.pi / 180;
  }

  double radianToDegree(double radian) {
    return radian * 180 / math.pi;
  }
}

class BarView extends StatefulWidget {
  const BarView({super.key});

  
  State<StatefulWidget> createState() => BarViewState();
}

class BarViewState extends State<BarView> {
  static const double barWidth = 50;
  static const shadowOpacity = 0.2;
  static const mainItems = <int, List<double>>{
    0: [2, 3, 2.5, 8, 7, 0.2, 0.3, 1.1],
    1: [1.8, 2.7, 3, 6.5, 0.9, 0.2, 0.3, 5],
    2: [1.5, 2, 3.5, 6, 6.5, 0.2, 9, 3],
    3: [1.5, 1.5, 4, 6.5, 6.5, 2, 0.3, 3],
    4: [2, 2, 5, 9, 6.5, 0.2, 3, 1.1],
    5: [1.2, 1.5, 4.3, 10, 6.5, 0.2, 0.3, 8],
    6: [1.2, 8, 5, 7, 6.5, 1, 0.3, 1.1],
    7: [1, 4.8, 5, 5, 7.5, 0.2, 0.3, 1.1],
    8: [1.2, 3, 5, 1, 6.5, 7, 0.3, 1.1],
    9: [1.2, 4.8, 3, 5, 2.5, 0.2, 7, 1.1],
    10: [1.2, 2, 5, 3, 6.5, 2, 0.3, 1.1],
    11: [1.2, 4.8, 5, 5, 1.5, 0.2, 4, 1.1],
  };
  int touchedIndex = -1;

  
  void initState() {
    super.initState();
  }

  Widget bottomTitles(double value, TitleMeta meta) {
    const style = TextStyle(color: Colors.white, fontSize: 10);
    String text;
    switch (value.toInt()%7) {
      case 0:
        text = 'Mon';
        break;
      case 1:
        text = 'Tue';
        break;
      case 2:
        text = 'Wed';
        break;
      case 3:
        text = 'Thu';
        break;
      case 4:
        text = 'Fri';
        break;
      case 5:
        text = 'Sat';
        break;
      case 6:
        text = 'Sun';
        break;
      default:
        text = '';
        break;
    }
    return SideTitleWidget(
      axisSide: meta.axisSide,
      child: Text(text, style: style),
    );
  }

  Widget topTitles(double value, TitleMeta meta) {
    const style = TextStyle(color: Colors.white, fontSize: 10);
    String text;
    switch (value.toInt()%7) {
      case 0:
        text = 'Mon';
        break;
      case 1:
        text = 'Tue';
        break;
      case 2:
        text = 'Wed';
        break;
      case 3:
        text = 'Thu';
        break;
      case 4:
        text = 'Fri';
        break;
      case 5:
        text = 'Sat';
        break;
      case 6:
        text = 'Sun';
        break;
      default:
        return Container();
    }
    return SideTitleWidget(
      axisSide: meta.axisSide,
      child: Text(text, style: style),
    );
  }

  Widget leftTitles(double value, TitleMeta meta) {
    const style = TextStyle(color: Colors.white, fontSize: 10);
    String text;
    if (value == 0) {
      text = '0';
    } else {
      text = '${value.toInt()}0k';
    }
    return SideTitleWidget(
      angle: AppUtils().degreeToRadian(value < 0 ? -45 : 45),
      axisSide: meta.axisSide,
      space: 4,
      child: Text(
        text,
        style: style,
        textAlign: TextAlign.center,
      ),
    );
  }

  Widget rightTitles(double value, TitleMeta meta) {
    const style = TextStyle(color: Colors.white, fontSize: 10);
    String text;
    if (value == 0) {
      text = '0';
    } else {
      text = '${value.toInt()}0k';
    }
    return SideTitleWidget(
      angle: AppUtils().degreeToRadian(value > 0 ? -45 : 45),
      axisSide: meta.axisSide,
      space: 0,
      child: Text(
        text,
        style: style,
        textAlign: TextAlign.center,
      ),
    );
  }

  BarChartGroupData generateGroup(
      int x,
      double value1,
      double value2,
      double value3,
      double value4,
      double value5,
      double value6,
      double value7,
      double value8,
      ) {
    // final isTop = value1 > 0;
    final sum1 = value1 + value2 + value3 + value4;
    final sum2 = value5 + value6 + value7 + value8;
    // final abs_sum = value1.abs() + value2.abs() + value3.abs() + value4.abs();
    final isTop = sum1 > sum2;
    final isTouched = touchedIndex == x;
    return BarChartGroupData(
      x: x,
      groupVertically: true,
      showingTooltipIndicators: isTouched ? [0] : [],
      barRods: [
        BarChartRodData(
          toY: isTop
            ?sum1
            :-sum2,
          width: barWidth,
          borderRadius: isTop
              ? const BorderRadius.only(
            topLeft: Radius.circular(6),
            topRight: Radius.circular(6),
          )
              : const BorderRadius.only(
            bottomLeft: Radius.circular(6),
            bottomRight: Radius.circular(6),
          ),
          rodStackItems: [
            BarChartRodStackItem(
              0,
              isTop? value1:-value5,
              const Color(0xff2bdb90),
              BorderSide(
                color: Colors.white,
                width: isTouched ? 2 : 0,
              ),
            ),
            BarChartRodStackItem(
              isTop? value1:-value5,
              isTop? (value1+value2):-(value5+value6),
              const Color(0xffffdd80),
              BorderSide(
                color: Colors.white,
                width: isTouched ? 2 : 0,
              ),
            ),
            BarChartRodStackItem(
              isTop? (value1+value2):-(value5+value6),
              isTop? (value1+value2+value3):-(value5+value6+value7),
              const Color(0xffff4d94),
              BorderSide(
                color: Colors.white,
                width: isTouched ? 2 : 0,
              ),
            ),
            BarChartRodStackItem(
              isTop? (value1+value2+value3):-(value5+value6+value7),
              isTop? (value1+value2+value3+value4):-(value5+value6+value7+value8),
              const Color(0xff19bfff),
              BorderSide(
                color: Colors.white,
                width: isTouched ? 2 : 0,
              ),
            ),
          ],
        ),
        BarChartRodData(
          toY: isTop
            ?-sum2
            :sum1,
          width: barWidth,
          color: Colors.transparent,
          borderRadius: isTop
              ? const BorderRadius.only(
            bottomLeft: Radius.circular(6),
            bottomRight: Radius.circular(6),
          )
              : const BorderRadius.only(
            topLeft: Radius.circular(6),
            topRight: Radius.circular(6),
          ),
          rodStackItems: [
            BarChartRodStackItem(
              0,
              isTop? -(value5):(value1),
              const Color(0xff2bdb90)
                  .withOpacity(isTouched ? shadowOpacity * 2 : shadowOpacity),
              const BorderSide(color: Colors.transparent),
            ),
            BarChartRodStackItem(
              isTop? -(value5):(value1),
              isTop? -(value5+value6):(value1+value2),
              const Color(0xffffdd80)
                  .withOpacity(isTouched ? shadowOpacity * 2 : shadowOpacity),
              const BorderSide(color: Colors.transparent),
            ),
            BarChartRodStackItem(
              isTop? -(value5+value6):(value1+value2),
              isTop? -(value5+value6+value7):(value1+value2+value3),
              const Color(0xffff4d94)
                  .withOpacity(isTouched ? shadowOpacity * 2 : shadowOpacity),
              const BorderSide(color: Colors.transparent),
            ),
            BarChartRodStackItem(
              isTop? -(value5+value6+value7):(value1+value2+value3),
              isTop? -(value5+value6+value7+value8):(value1+value2+value3+value4),
              const Color(0xff19bfff)
                  .withOpacity(isTouched ? shadowOpacity * 2 : shadowOpacity),
              const BorderSide(color: Colors.transparent),
            ),
          ],
        ),
      ],
    );
  }

  bool isShadowBar(int rodIndex) => rodIndex == 1;

  
  Widget build(BuildContext context) {
    return AspectRatio(
      aspectRatio: 0.8,
      child: Card(
        elevation: 4,
        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(6)),
        color: const Color(0xff020227),
        child: Padding(
          padding: const EdgeInsets.only(top: 16),
          child: BarChart(
            BarChartData(
              alignment: BarChartAlignment.center,
              maxY: 20,
              minY: -20,
              groupsSpace: 12,
              barTouchData: BarTouchData(
                handleBuiltInTouches: false,
                touchCallback: (FlTouchEvent event, barTouchResponse) {
                  if (!event.isInterestedForInteractions ||
                      barTouchResponse == null ||
                      barTouchResponse.spot == null) {
                    setState(() {
                      touchedIndex = -1;
                    });
                    return;
                  }
                  final rodIndex = barTouchResponse.spot!.touchedRodDataIndex;
                  if (isShadowBar(rodIndex)) {
                    setState(() {
                      touchedIndex = -1;
                    });
                    return;
                  }
                  setState(() {
                    touchedIndex = barTouchResponse.spot!.touchedBarGroupIndex;
                  });
                },
              ),
              titlesData: FlTitlesData(
                show: true,
                topTitles: AxisTitles(
                  sideTitles: SideTitles(
                    showTitles: true,
                    reservedSize: 32,
                    getTitlesWidget: topTitles,
                  ),
                ),
                bottomTitles: AxisTitles(
                  sideTitles: SideTitles(
                    showTitles: true,
                    reservedSize: 32,
                    getTitlesWidget: bottomTitles,
                  ),
                ),
                leftTitles: AxisTitles(
                  sideTitles: SideTitles(
                    showTitles: true,
                    getTitlesWidget: leftTitles,
                    interval: 5,
                    reservedSize: 42,
                  ),
                ),
                rightTitles: AxisTitles(
                  sideTitles: SideTitles(
                    showTitles: true,
                    getTitlesWidget: rightTitles,
                    interval: 5,
                    reservedSize: 42,
                  ),
                ),
              ),
              gridData: FlGridData(
                show: true,
                checkToShowHorizontalLine: (value) => value % 5 == 0,
                getDrawingHorizontalLine: (value) {
                  if (value == 0) {
                    return FlLine(
                      color: const Color(0xff363753),
                      strokeWidth: 3,
                    );
                  }
                  return FlLine(
                    color: const Color(0xff2a2747),
                    strokeWidth: 0.8,
                  );
                },
              ),
              borderData: FlBorderData(
                show: false,
              ),
              barGroups: mainItems.entries
                  .map(
                    (e) => generateGroup(
                  e.key,
                  e.value[0],
                  e.value[1],
                  e.value[2],
                  e.value[3],
                  e.value[4],
                  e.value[5],
                  e.value[6],
                  e.value[7],
                ),
              )
                  .toList(),
            ),
          ),
        ),
      ),
    );
  }
}

DateView.dart

import 'dart:math';
import 'package:flutter/material.dart';
import 'draw_grid.dart';
import 'utils_date.dart';

class DateView extends StatefulWidget {
  const DateView({Key? key}) : super(key: key);

  
  State<DateView> createState() => _DateViewState();
}

class _DateViewState extends State<DateView> {
  
  Widget build(BuildContext context) {
    List<double> datas = [
      0, 0, 0,
      61, 81, 96, 56, 56, 68, 52,
      69, 84, 76, 69, 66, 48, 59,
      80, 96, 84, 80, 79, 90, 84,
      91, 108, 111, 91, 92, 90, 81,
      81, 119, 106, 96, 91, 98, 98,
      100, 96, 102, 89, 101, 156, 141,
      110, 111, 148, 147, 98, 109, 120,
      91, 100, 146, 154, 99, 101, 105,
      102, 120, 169, 167, 94, 96, 103,
      129, 135, 189, 241, 137, 134, 140,
      139, 145, 301, 121, 110, 148, 198,
      145, 141, 268, 267, 189, 142, 158,
      159, 149, 234, 294, 294, 167, 167,
      149, 140, 249, 231, 147, 147, 134,
      170, 180, 254, 267, 195, 196, 96,
      171, 190, 209, 201, 185, 187, 180,
      120, 91, 199, 291, 135, 197, 471,
      201, 196, 272, 270, 197, 194, 200,
      240, 232, 299, 290, 270, 215, 260,
      300, 303, 394, 400, 289, 347, 315,
      402, 419, 464, 498, 394, 514, 415,
      401, 380, 480, 394, 340, 399, 140,
      420, 429, 510, 607, 321, 409, 441,
      320, 431, 520, 314, 324, 411, 412,
    ];
    return CalenderHeatMap(datas: datas);
  }
}


class CalenderHeatMap extends StatelessWidget {
  final List<double> datas;

  const CalenderHeatMap({
    required this.datas,
  });

  
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (BuildContext context, BoxConstraints constraints) {
        return CustomPaint(
          painter: CalenderHeatMapPainter(datas: datas),
          size: constraints.biggest,
        );
      },
    );
  }
}

class CalenderHeatMapPainter extends CustomPainter {
  final List<double> datas;

  CalenderHeatMapPainter({
    required this.datas,
  });

  final double gap = 5.0;
  final double blockWidth = 25.0;
  final double blockHeight = 25.0;
  final double blockRadius = 5.0;

  List<Color> _generateBlockColor({double opacity = 1}) {
    if (opacity == 0) {
      return [Colors.black12, Colors.black12, Colors.black12, Colors.black12];
    }

    return [Color.fromRGBO(45, 255, 93, opacity), Color.fromRGBO(255, 45, 93, opacity), Color.fromRGBO(45, 93, 255, opacity), Colors.purpleAccent.withOpacity(opacity), Colors.yellow.withOpacity(opacity), Colors.cyan.withOpacity(opacity)];
  }

  void _drawBlock(Canvas canvas, Offset offset, Color color) {
    final Paint paint = Paint()
      ..color = color
      ..isAntiAlias = true
      ..style = PaintingStyle.fill;
    final Rect rect =
    Rect.fromLTWH(offset.dx, offset.dy, blockWidth, blockHeight);
    final Radius radius = Radius.circular(blockRadius);
    final rrect = RRect.fromRectAndRadius(rect, radius);

    canvas.drawRRect(rrect, paint);
  }

  void _drawWeekDayTexts(Canvas canvas, Size size) {
    canvas.save();
    canvas.translate(0, size.height / 10);
    Offset start = Offset(30.0, 0.0);

    weekDayTextEn.asMap().forEach((index, text) {
      TextPainter(
        textAlign: TextAlign.center,
        textDirection: TextDirection.ltr,
      )
        ..text = TextSpan(
          text: text,
          style: TextStyle(
            fontSize: 24.0,
            fontWeight: FontWeight.bold,
            color: Color(0xffc5d1da)
          ),
        )
        ..layout(
          minWidth: 0.0,
          maxWidth: 100.0,
        )
        ..paint(canvas, start);

      start += Offset(0, gap + blockHeight);
    });
    canvas.restore();
  }

  
  void paint(Canvas canvas, Size size) {
    drawGrid(canvas, size);
    _drawWeekDayTexts(canvas, size);

    canvas.save();
    canvas.translate(0, size.height / 10);
    Offset start = Offset(100.0, 0.0);

    for (int i = 0; i < datas.length; i++) {
      Offset move;

      if (i == 0) {
        move = Offset.zero;
      } else {
        if (i % 7 == 0) {
          move = Offset(gap + blockWidth, -start.dy);
        } else {
          move = Offset(0, gap + blockHeight);
        }
      }
      start += move;

      final double val = datas[i];
      final double maxVal = datas.reduce(max);
      final percent = val / maxVal;
      double opacity;
      opacity = percent;

      // if (percent > .8) {
      //   opacity = 1;
      // } else if (percent > .6) {
      //   opacity = 0.8;
      // } else if (percent > .4) {
      //   opacity = 0.6;
      // } else if (percent < .2) {
      //   opacity = 0.4;
      // } else {
      //   opacity = 0.2;
      // }

      _drawBlock(canvas, start, _generateBlockColor(opacity: opacity)[i~/31]);
    }
    canvas.restore();
  }

  
  bool shouldRepaint(CalenderHeatMapPainter oldDelegate) => false;

  
  bool shouldRebuildSemantics(CalenderHeatMapPainter oldDelegate) => false;
}

LineViewNew.dart

import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';

class LineChartSample2 extends StatefulWidget {
  const LineChartSample2({super.key});

  
  State<LineChartSample2> createState() => _LineChartSample2State();
}

class _LineChartSample2State extends State<LineChartSample2> {
  List<Color> gradientColors = [
    const Color(0xff23b6e6),
    const Color(0xff02d39a),
  ];

  bool showAvg = false;

  
  Widget build(BuildContext context) {
    return Stack(
      children: <Widget>[
        AspectRatio(
          aspectRatio: 3.5,
          child: DecoratedBox(
            decoration: const BoxDecoration(
              borderRadius: BorderRadius.all(
                Radius.circular(18),
              ),
              color: Color(0xff232d37),
            ),
            child: Padding(
              padding: const EdgeInsets.only(
                right: 30,
                left: 30,
                top: 40,
                bottom: 12,
              ),
              child: LineChart(
                showAvg ? avgData() : mainData(),
              ),
            ),
          ),
        ),
        SizedBox(
          width: 200,
          height: 34,
          child: TextButton(
            onPressed: () {
              setState(() {
                showAvg = !showAvg;
              });
            },
            child: Text(
              '平均',
              style: TextStyle(
                fontSize: 12,
                color: showAvg ? Colors.white.withOpacity(0.5) : Colors.white,
              ),
            ),
          ),
        ),
      ],
    );
  }

  Widget bottomTitleWidgets(double value, TitleMeta meta) {
    const style = TextStyle(
      color: Color(0xff68737d),
      fontWeight: FontWeight.bold,
      fontSize: 16,
    );
    Widget text;
    text = Text(((value.toInt()+1)%12).toString()+'月', style: style);
    return SideTitleWidget(
      axisSide: meta.axisSide,
      child: text,
    );
  }

  Widget leftTitleWidgets(double value, TitleMeta meta) {
    const style = TextStyle(
      color: Color(0xff67727d),
      fontWeight: FontWeight.bold,
      fontSize: 15,
    );
    String text;
    switch (value.toInt()) {
      case 1:
        text = '10K';
        break;
      case 3:
        text = '30k';
        break;
      case 5:
        text = '50k';
        break;
      case 7:
        text = '70K';
        break;
      case 9:
        text = '90k';
        break;
      case 11:
        text = '110k';
        break;
      default:
        return Container();
    }

    return Text(text, style: style, textAlign: TextAlign.left);
  }

  LineChartData mainData() {
    return LineChartData(
      gridData: FlGridData(
        show: true,
        drawVerticalLine: true,
        horizontalInterval: 1,
        verticalInterval: 1,
        getDrawingHorizontalLine: (value) {
          return FlLine(
            color: const Color(0xff37434d),
            strokeWidth: 1,
          );
        },
        getDrawingVerticalLine: (value) {
          return FlLine(
            color: const Color(0xff37434d),
            strokeWidth: 1,
          );
        },
      ),
      titlesData: FlTitlesData(
        show: true,
        rightTitles: AxisTitles(
          sideTitles: SideTitles(showTitles: false),
        ),
        topTitles: AxisTitles(
          sideTitles: SideTitles(showTitles: false),
        ),
        bottomTitles: AxisTitles(
          sideTitles: SideTitles(
            showTitles: true,
            reservedSize: 30,
            interval: 1,
            getTitlesWidget: bottomTitleWidgets,
          ),
        ),
        leftTitles: AxisTitles(
          sideTitles: SideTitles(
            showTitles: true,
            interval: 1,
            getTitlesWidget: leftTitleWidgets,
            reservedSize: 42,
          ),
        ),
      ),
      borderData: FlBorderData(
        show: true,
        border: Border.all(color: const Color(0xff37434d)),
      ),
      minX: 0,
      maxX: 20,
      minY: 0,
      maxY: 11,
      lineBarsData: linesBarData1()
    );
  }

  List<LineChartBarData> linesBarData1() {
    final LineChartBarData lineChartBarData1 = LineChartBarData(
      spots: [
        FlSpot(0, 0.1),
        FlSpot(1, 0.2),
        FlSpot(2, 0.4),
        FlSpot(3, 0.9),
        FlSpot(4, 0.95),
        FlSpot(5, 0.99),
        FlSpot(6, 1.5),
        FlSpot(7, 1.9),
        FlSpot(8, 2),
        FlSpot(9, 2.5),
        FlSpot(10, 2.1),
        FlSpot(11, 2.3),
        FlSpot(12, 2.0),
        FlSpot(13, 1.8),
        FlSpot(14, 2.1),
        FlSpot(15, 2.7),
        FlSpot(16, 2.9),
        FlSpot(17, 2.97),
        FlSpot(18, 3.7),
        FlSpot(19, 4),
        FlSpot(20, 4.3),
      ],
      isCurved: true,
      gradient: LinearGradient(
        colors: gradientColors,
      ),
      barWidth: 2,
      isStrokeCapRound: true,
      dotData: FlDotData(
        show: false,
      ),
      belowBarData: BarAreaData(
        show: true,
        gradient: LinearGradient(
          colors: gradientColors
              .map((color) => color.withOpacity(0.3))
              .toList(),
        ),
      ),
    );
    final LineChartBarData lineChartBarData2 = LineChartBarData(
      spots: [
        FlSpot(0, 0.01),
        FlSpot(1, 0.02),
        FlSpot(2, 0.04),
        FlSpot(3, 0.09),
        FlSpot(4, 0.5),
        FlSpot(5, 1),
        FlSpot(6, 1.1),
        FlSpot(7, 1.2),
        FlSpot(8, 1.2),
        FlSpot(9, 1.5),
        FlSpot(10, 1.6),
        FlSpot(11, 1.9),
        FlSpot(12, 2.04),
        FlSpot(13, 1.33),
        FlSpot(14, 2.2),
        FlSpot(15, 2.6),
        FlSpot(16, 4.4),
        FlSpot(17, 4.5),
        FlSpot(18, 5.2),
        FlSpot(19, 8.7),
        FlSpot(20, 10.2),
      ],
      isCurved: true,
      gradient: LinearGradient(
        colors: [
          ColorTween(begin: gradientColors[0], end: gradientColors[1])
              .lerp(0.2)!,
          ColorTween(begin: gradientColors[0], end: gradientColors[1])
              .lerp(0.2)!,
        ],
      ),
      barWidth: 2,
      isStrokeCapRound: true,
      dotData: FlDotData(
        show: false,
      ),
      belowBarData: BarAreaData(
        show: true,
        gradient: LinearGradient(
          colors: [
            ColorTween(begin: gradientColors[0], end: gradientColors[1])
                .lerp(0.2)!
                .withOpacity(0.1),
            ColorTween(begin: gradientColors[0], end: gradientColors[1])
                .lerp(0.2)!
                .withOpacity(0.1),
          ],
        ),
      ),
    );
    return [lineChartBarData1, lineChartBarData2];
  }



  LineChartData avgData() {
    return LineChartData(
      lineTouchData: LineTouchData(enabled: false),
      gridData: FlGridData(
        show: true,
        drawHorizontalLine: true,
        verticalInterval: 1,
        horizontalInterval: 1,
        getDrawingVerticalLine: (value) {
          return FlLine(
            color: const Color(0xff37434d),
            strokeWidth: 1,
          );
        },
        getDrawingHorizontalLine: (value) {
          return FlLine(
            color: const Color(0xff37434d),
            strokeWidth: 1,
          );
        },
      ),
      titlesData: FlTitlesData(
        show: true,
        bottomTitles: AxisTitles(
          sideTitles: SideTitles(
            showTitles: true,
            reservedSize: 30,
            getTitlesWidget: bottomTitleWidgets,
            interval: 1,
          ),
        ),
        leftTitles: AxisTitles(
          sideTitles: SideTitles(
            showTitles: true,
            getTitlesWidget: leftTitleWidgets,
            reservedSize: 42,
            interval: 1,
          ),
        ),
        topTitles: AxisTitles(
          sideTitles: SideTitles(showTitles: false),
        ),
        rightTitles: AxisTitles(
          sideTitles: SideTitles(showTitles: false),
        ),
      ),
      borderData: FlBorderData(
        show: true,
        border: Border.all(color: const Color(0xff37434d)),
      ),
      minX: 0,
      maxX: 20,
      minY: 0,
      maxY: 11,
      lineBarsData: linesBarData2()
    );
  }

  List<LineChartBarData> linesBarData2(){
    final LineChartBarData lineChartBarData1 = LineChartBarData(
      spots:const [
        FlSpot(0, 2.8791),
        FlSpot(1, 2.8791),
        FlSpot(2, 2.8791),
        FlSpot(3, 2.8791),
        FlSpot(4, 2.8791),
        FlSpot(5, 2.8791),
        FlSpot(6, 2.8791),
        FlSpot(7, 2.8791),
        FlSpot(8, 2.8791),
        FlSpot(9, 2.8791),
        FlSpot(10, 2.8791),
        FlSpot(11, 2.8791),
        FlSpot(12, 2.8791),
        FlSpot(13, 2.8791),
        FlSpot(14, 2.8791),
        FlSpot(15, 2.8791),
        FlSpot(16, 2.8791),
        FlSpot(17, 2.8791),
        FlSpot(18, 2.8791),
        FlSpot(19, 2.8791),
        FlSpot(20, 2.8791),
      ],
      isCurved: true,
      gradient: LinearGradient(
        colors: [
          ColorTween(begin: gradientColors[0], end: gradientColors[1])
              .lerp(0.2)!,
          ColorTween(begin: gradientColors[0], end: gradientColors[1])
              .lerp(0.2)!,
        ],
      ),
      barWidth: 5,
      isStrokeCapRound: true,
      dotData: FlDotData(
        show: false,
      ),
      belowBarData: BarAreaData(
        show: true,
        gradient: LinearGradient(
          colors: [
            ColorTween(begin: gradientColors[0], end: gradientColors[1])
                .lerp(0.2)!
                .withOpacity(0.1),
            ColorTween(begin: gradientColors[0], end: gradientColors[1])
                .lerp(0.2)!
                .withOpacity(0.1),
          ],
        ),
      ),
    );
    final LineChartBarData lineChartBarData2 = LineChartBarData(
      spots: const [
        FlSpot(0, 4.307),
        FlSpot(1, 4.307),
        FlSpot(2, 4.307),
        FlSpot(3, 4.307),
        FlSpot(4, 4.307),
        FlSpot(5, 4.307),
        FlSpot(6, 4.307),
        FlSpot(7, 4.307),
        FlSpot(8, 4.307),
        FlSpot(9, 4.307),
        FlSpot(10, 4.307),
        FlSpot(11, 4.307),
        FlSpot(12, 4.307),
        FlSpot(13, 4.307),
        FlSpot(14, 4.307),
        FlSpot(15, 4.307),
        FlSpot(16, 4.307),
        FlSpot(17, 4.307),
        FlSpot(18, 4.307),
        FlSpot(19, 4.307),
        FlSpot(20, 4.307),
      ],
      isCurved: true,
      gradient: LinearGradient(
        colors: [
          ColorTween(begin: gradientColors[0], end: gradientColors[1])
              .lerp(0.2)!,
          ColorTween(begin: gradientColors[0], end: gradientColors[1])
              .lerp(0.2)!,
        ],
      ),
      barWidth: 5,
      isStrokeCapRound: true,
      dotData: FlDotData(
        show: false,
      ),
      belowBarData: BarAreaData(
        show: true,
        gradient: LinearGradient(
          colors: [
            ColorTween(begin: gradientColors[0], end: gradientColors[1])
                .lerp(0.2)!
                .withOpacity(0.1),
            ColorTween(begin: gradientColors[0], end: gradientColors[1])
                .lerp(0.2)!
                .withOpacity(0.1),
          ],
        ),
      ),
    );
    return [lineChartBarData1, lineChartBarData2];
  }
}

draw_grid.dart

import 'package:flutter/material.dart';

Paint paint = Paint()
  ..color = Colors.grey
  ..strokeWidth = .5;
final double step = 30; // 小格边长

void drawGrid(Canvas canvas, Size size) {
  canvas.save();
  Offset p1 = Offset(0, 0);
  Offset p2 = Offset(size.width, 0);

  for (double i = 0; i < size.height; i += step) {
    if (i % 90 == 0) {
      paint.color = Colors.black26;
      paint.strokeWidth = 1.0;
    } else {
      paint.color = Colors.black12;
    }
    canvas.drawLine(p1, p2, paint);
    canvas.translate(0, step);
  }

  canvas.restore();

  canvas.save();
  p1 = Offset(0, 0);
  p2 = Offset(0, size.height);

  for (double i = 0; i < size.width; i += step) {
    if (i % 90 == 0) {
      paint.color = Colors.black26;
      paint.strokeWidth = 1.0;
    } else {
      paint.color = Colors.black12;
    }
    canvas.drawLine(p1, p2, paint);
    canvas.translate(step, 0);
  }

  canvas.restore();
}

utils_date.dart

leapYear(int year) {
  bool leapYear = false;
  bool leap = ((year % 100 == 0) && (year % 400 != 0));

  if (leap == true) {
    leapYear = false;
  } else if (year % 4 == 0) {
    leapYear = true;
  }

  return leapYear;
}

daysInMonth(int year, int month) {
  List<int> monthLength = [31, 31, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];

  if (leapYear(year) == true) {
    monthLength[1] = 29;
  } else {
    monthLength[1] = 28;
  }

  return monthLength[month - 1];
}

yearLength(int year) {
  int yearLength = 0;

  for (int counter = 1; counter < year; counter++) {
    if (counter >= 4) {
      if (leapYear(counter) == true)
        yearLength += 366;
      else
        yearLength += 365;
    } else
      yearLength += 365;
  }
  return yearLength;
}

lastDayOfMonth(int year, int month) {
  return DateTime(year, month + 1, 0);
}

firstDayOfMonth(int year, int month) {
  return DateTime(year, month, 1).day;
}

List<String> weekDayTextEn = [
  'Mon',
  'Tue',
  'Wed',
  'Thur',
  'Fri',
  'Sat',
  'Sun',
];

List<String> weekDayTextCh = [
  '星期一',
  '星期二',
  '星期三',
  '星期四',
  '星期五',
  '星期六',
  '星期日',
];

List<String> monthTextEn = [
  'Jan',
  'Feb',
  'Mar',
  'Apr',
  'May',
  'Jun',
  'Jul',
  'Aug',
  'Sep',
  'Oct',
  'Nov',
  'Dec',
];

List<String> monthTextCn = [
  '一月',
  '二月',
  '三月',
  '四月',
  '五月',
  '六月',
  '七月',
  '八月',
  '九月',
  '十月',
  '十一月',
  '十二月',
];
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

铖铖的花嫁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值