Flutter---折线图(使用外部库)

一.折线图入门

效果图

代码逻辑

1.坐标范围

2.网格线

3.坐标文字

4.边框

5.折线数据

6.交互行为(点击折线区域)

代码实例

Dart 复制代码
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

import 'package:fl_chart/fl_chart.dart'; //导入包



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

  @override
  State<StatefulWidget> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {


  //构建UI
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("折线图入门示例")),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child:
        LineChart( //f1_chart提供的主控件,用于显示折线图
          mainData(),
        ),
      ),
    );
  }

  // 折线图配置
  LineChartData mainData() {
    return LineChartData(

      //1.坐标范围
      minX: 0,
      maxX: 6,
      minY: 0,
      maxY: 6,

      //2.网格线
      gridData: FlGridData(
        //默认情况下内部线是虚线,外部线是实线
        show: true,//启动网格
        horizontalInterval: 1, //每隔一个单元绘制一条水平线
        verticalInterval: 1, //每隔一个单元绘制一条垂直线

        // getDrawingHorizontalLine: (value) {  //内部横实线
        //   return FlLine(
        //     color: Colors.grey,
        //     strokeWidth: 1,
        //     dashArray: null, // 设为0或null代表实线
        //   );
        // },
        //
        // getDrawingVerticalLine: (value) { //内部竖实线
        //   return FlLine(
        //     color: Colors.grey.withOpacity(0.5),
        //     strokeWidth: 1,
        //     dashArray: null, // ← 实线
        //   );
        // },
      ),


      //3.坐标文字
      titlesData: FlTitlesData(
        bottomTitles: //横轴
        AxisTitles(
          sideTitles: SideTitles(
            showTitles: true, //打开X轴标签显示
            interval: 1,//每隔1单位画一个刻度并显示数字
            getTitlesWidget: (value, meta) { //显示每个X值对应显示什么文字(会覆盖默认的显示数字)
              const style = TextStyle(fontSize: 12);
              switch (value.toInt()) {
                case 0: return const Text("一", style: style);
                case 1: return const Text("二", style: style);
                case 2: return const Text("三", style: style);
                case 3: return const Text("四", style: style);
                case 4: return const Text("五", style: style);
                case 5: return const Text("六", style: style);
                case 6: return const Text("日", style: style);
              }
              return const Text("");
            },
          ),
        ),

        leftTitles: //纵轴
        AxisTitles(
          sideTitles: SideTitles(
            showTitles: true, //打开Y轴标签显示
            interval: 1,//每隔1单位画一个刻度并显示数字(默认显示Y轴数字)

          ),
        ),
        rightTitles: AxisTitles(sideTitles: SideTitles(showTitles: false)),//右侧轴
        topTitles: AxisTitles(sideTitles: SideTitles(showTitles: false)), //顶部轴
      ),

      //4.边框
      borderData: FlBorderData(
        show: true, //显示边框
        border: Border.all(color: Colors.red),
      ),

      //5.折线数据
      lineBarsData: [
        LineChartBarData(
          isCurved: true, // 是否平滑曲线
          color: Colors.blue, //折线颜色
          barWidth: 3,
          dotData: FlDotData(show: true), // 显示折线点
          belowBarData: BarAreaData( //曲线下方区域
            show: true,
            color: Colors.blue.withOpacity(0.2), // 曲线下方阴影颜色
          ),
          spots: const [ //折线的点的数据
            FlSpot(0, 1),
            FlSpot(1, 3),
            FlSpot(2, 5),
            FlSpot(3, 4.5),
            FlSpot(4, 3.5),
            FlSpot(5, 5),
            FlSpot(6, 4),
          ],
        ),
      ],

      //6.用来控制折线图的触摸和交互行为
      lineTouchData: LineTouchData(
        touchTooltipData: LineTouchTooltipData(
          tooltipBgColor: Colors.blueAccent.withOpacity(0.8),
        ),
        handleBuiltInTouches: true, // 启用点击浮窗
      ),
    );
  }


}

二.交互式折线图

效果图

代码逻辑

1.定义输入框控制器和焦点控制器

2.初始化存储数据点的列表

3.定义取消焦点的方法

4.定义添加数据点的方法

5.定义删除数据点的方法

6.定义清除所有数据点的方法

7.销毁的相关操作

8.具体UI(输入行,添加按钮,清空按钮,折线图区域,当前坐标点列表)

9.折线图的具体配置

代码实例

Dart 复制代码
import 'package:flutter/material.dart';
import 'package:fl_chart/fl_chart.dart';

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

  @override
  State<StatefulWidget> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {

  final TextEditingController _xController = TextEditingController(); //输入框控制器
  final TextEditingController _yController = TextEditingController(); //输入框控制器
  final FocusNode _xFocusNode = FocusNode();
  final FocusNode _yFocusNode = FocusNode();

  List<FlSpot> userSpots = [ //存储用户输入的数据点
    const FlSpot(0, 1), //初始的数据点
    const FlSpot(1, 2),
    const FlSpot(2, 3),
  ];


  //=================================取消焦点方法==================================
  void _unfocusAll() {
    _xFocusNode.unfocus();  // 取消X输入框焦点
    _yFocusNode.unfocus();  // 取消Y输入框焦点
  }

  //============================添加数据点的方法=================================
  void _addPoint() {
    double? x = double.tryParse(_xController.text);
    double? y = double.tryParse(_yController.text);

    if (x != null && y != null && x <= 10 && y <= 10 ) {
      setState(() {
        userSpots.add(FlSpot(x, y));//添加新点
        userSpots.sort((a, b) => a.x.compareTo(b.x));//按X坐标排序
      });

      //恢复数据框
      _xController.clear();
      _yController.clear();
      _unfocusAll();//取消焦点
    } else {
      //输入无效时显示提示
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text("请输入小于或等于10的有效数字")),
      );

      _unfocusAll();  // 错误时也取消焦点
    }
  }

  //============================删除数据点的方法=================================
  void _removePoint(int index) {
    setState(() {
      userSpots.removeAt(index); //删除指定索引的点
    });
  }

  //=============================清除所有数据点的方法=============================
  void _clearAll() {
    setState(() {
      userSpots.clear();//清空所有点
      _unfocusAll();  // 清空时也取消焦点
    });
  }

  @override
  void dispose() {
    _xController.dispose();
    _yController.dispose();
    _xFocusNode.dispose();
    _yFocusNode.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("交互式折线图")),
      body: Padding(
        padding: const EdgeInsets.all(10),
        child: Column(
          children: [
            // 1.输入行
            Row(
              children: [
                //X坐标输入框
                Expanded(
                  child: TextField(
                    controller: _xController,
                    focusNode: _xFocusNode,  // 绑定FocusNode
                    keyboardType: TextInputType.number, //数字键盘
                    decoration: const InputDecoration(
                      labelText: "X 坐标",
                      border: OutlineInputBorder(),
                    ),
                  ),
                ),
                const SizedBox(width: 10),

                //Y坐标输入框
                Expanded(
                  child: TextField(
                    controller: _yController,
                    focusNode: _yFocusNode,  // 绑定FocusNode
                    keyboardType: TextInputType.number,
                    decoration: const InputDecoration(
                      labelText: "Y 坐标",
                      border: OutlineInputBorder(),
                    ),
                  ),
                ),
                const SizedBox(width: 10),

                //添加按钮
                ElevatedButton(
                  onPressed: _addPoint, //添加的点击事件
                  child: const Text("添加"),
                ),
              ],
            ),

            const SizedBox(height: 15),

            //2.清空按钮
            Row(
              mainAxisAlignment: MainAxisAlignment.end,
              children: [
                TextButton.icon(
                  onPressed: userSpots.isEmpty ? null : _clearAll, //点击事件
                  icon: const Icon(Icons.delete_forever, color: Colors.red),
                  label: const Text(
                    "清空所有",
                    style: TextStyle(color: Colors.red),
                  ),
                ),
              ],
            ),

            const SizedBox(height: 10),

            // 3.折线图区域
            Expanded(
              flex: 2,
              child: LineChart(mainData()),
            ),

            const SizedBox(height: 20),

            //4.当前坐标点列表
            Expanded(
              flex: 1,
              child: userSpots.isEmpty
                  ? const Center(child: Text("暂无数据点"))
                  : ListView.builder(
                itemCount: userSpots.length,
                itemBuilder: (context, index) {
                  final spot = userSpots[index];
                  return Card(
                    child: ListTile(
                      leading: const Icon(Icons.circle, color: Colors.blue),
                      title: Text("X: ${spot.x}, Y: ${spot.y}"),
                      trailing: IconButton(
                        icon: const Icon(Icons.delete, color: Colors.red),
                        onPressed: () => _removePoint(index),
                      ),
                    ),
                  );
                },
              ),
            ),
          ],
        ),
      ),
    );
  }

  //===================================折线图配置==================================
  LineChartData mainData() {
    return LineChartData(

      //1,范围
      minX: 0, maxX: 10, //x轴范围
      minY: 0, maxY: 10, //Y轴范围

      //2.网格线
      gridData: FlGridData(show: true),

      //3.坐标轴标签
      titlesData: FlTitlesData(
        bottomTitles: AxisTitles(
          sideTitles: SideTitles(
              showTitles: true,
              interval: 1,
              reservedSize: 30, //为标签保留空间
          ),
        ),
        leftTitles: AxisTitles(
          sideTitles: SideTitles(
              showTitles: true,
              interval: 1, //标签间隔
            reservedSize: 30, //为标签保留空间
          ),
        ),
        rightTitles: AxisTitles(sideTitles: SideTitles(showTitles: false)),
        topTitles: AxisTitles(sideTitles: SideTitles(showTitles: false)),
      ),

      //4.边框
      borderData: FlBorderData(
        show: true,
        border: Border.all(color: Colors.black26),
      ),

      //5.折线数据
      lineBarsData: [
        LineChartBarData(
          isCurved: true, //曲线连接
          color: Colors.blue, //线条颜色
          barWidth: 3, //线条宽度
          dotData: FlDotData(show: true),//显示数据点
          belowBarData: BarAreaData(show: true, color: Colors.blue.withOpacity(0.2)), //区域填充
          spots: userSpots.isNotEmpty
              ? userSpots
              : [const FlSpot(0, 0)], // 防止空图报错
        ),
      ],

      //6.交互行为
      lineTouchData: LineTouchData(
        touchTooltipData: LineTouchTooltipData(
          tooltipBgColor: Colors.blueAccent.withOpacity(0.8),
        ),
        handleBuiltInTouches: true,
      ),
    );
  }
}
相关推荐
坚持学习前端日记1 天前
Android JS桥技术深度解析
android·开发语言·javascript
json{shen:"jing"}1 天前
07_表单输入绑定
java·前端·javascript
yyt3630458411 天前
K 线图高性能窗口化渲染
前端·javascript·css·vue.js·gitee·vue
XiaoYu20021 天前
第5章 Nest.js精进-IOC控制反转
前端
LV技术派1 天前
适合很多公司和团队的 AI Coding 落地范式(二)
前端·aigc·ai编程
IT_陈寒1 天前
Redis性能翻倍的5个冷门技巧:从每秒10万到20万的实战优化之路
前端·人工智能·后端
ss2731 天前
高版本node启动RuoYi-Vue若依前端ruoyi-ui
前端·javascript·vue.js
百锦再1 天前
Elements Plus 跨设备自适应显示问题综合解决方案
python·flutter·小程序·uni-app·k8s·tornado·net
饼干,1 天前
模拟试卷2
前端·javascript·easyui