
开篇寄语
这一篇文章主要是学习flutter中实现的表格功能,有过Web开发的朋友肯定知道echarts这个js库,比如柱状图,饼状图,曲线图等等,而flutter自然也不例外,当然是有支持它的库,本篇文章就来学习Line Charts,分享给大家。
前情提要
- 《Flutter有关ColorFiltered一些有趣实例,图片变色大不同》
- 《如何在Windows电脑上安装Flutter并建立自己第一个全系统应用》
- 《Flutter制作自己的第一个全平台APP学习之Navigation篇》
- 《Flutter如何设置移动端禁止随手机自动旋转屏幕》
- 《Flutter制作自己的第一个全平台APP学习之多媒体篇》
内容详情
以下内容大多是更改lib下的main.dart文件内容,删掉里面的内容,复制粘贴代码,开启调试就可以出现了。
这个需要安装一个名为fl_chart的支撑库,安装和例子请前往这个地址:fl_chart。
Basic Single Line Chart
- 基础折线变化图,比较简单,试举一例:
// main.dart import 'package:flutter/material.dart'; import 'package:fl_chart/fl_chart.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return const MaterialApp( // Remove the debug banner debugShowCheckedModeBanner: false, title: 'Flutter Demo', home: HomeScreen(), ); } } class HomeScreen extends StatelessWidget { const HomeScreen({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Flutter Demo'), ), body: SafeArea( child: Container( padding: const EdgeInsets.all(24.0), width: double.infinity, child: LineChart( LineChartData(borderData: FlBorderData(show: false), lineBarsData: [ LineChartBarData(spots: [ FlSpot(0, 1), FlSpot(1, 3), FlSpot(2, 10), FlSpot(3, 7), FlSpot(4, 12), FlSpot(5, 13), FlSpot(6, 17), FlSpot(7, 15), FlSpot(8, 20) ]) ]), ), ), ), ); } }
生成效果如下图所示:

The simplest Bar Chart
- 多线段同在一图,这个也比较简单,试举一例:
// main.dart import 'package:flutter/material.dart'; import 'package:fl_chart/fl_chart.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return const MaterialApp( // Remove the debug banner debugShowCheckedModeBanner: false, title: 'Flutter Demo', home: HomeScreen(), ); } } class HomeScreen extends StatelessWidget { const HomeScreen({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Flutter Demo'), ), body: SafeArea( child: Container( padding: const EdgeInsets.all(24.0), width: double.infinity, child: BarChart(BarChartData( borderData: FlBorderData( border: const Border( top: BorderSide.none, right: BorderSide.none, left: BorderSide(width: 1), bottom: BorderSide(width: 1), )), groupsSpace: 10, barGroups: [ BarChartGroupData(x: 1, barRods: [ BarChartRodData(y: 10, width: 15, colors: [Colors.amber]), ]), BarChartGroupData(x: 2, barRods: [ BarChartRodData(y: 9, width: 15, colors: [Colors.amber]), ]), BarChartGroupData(x: 3, barRods: [ BarChartRodData(y: 4, width: 15, colors: [Colors.amber]), ]), BarChartGroupData(x: 4, barRods: [ BarChartRodData(y: 2, width: 15, colors: [Colors.amber]), ]), BarChartGroupData(x: 5, barRods: [ BarChartRodData(y: 13, width: 15, colors: [Colors.amber]), ]), BarChartGroupData(x: 6, barRods: [ BarChartRodData(y: 17, width: 15, colors: [Colors.amber]), ]), BarChartGroupData(x: 7, barRods: [ BarChartRodData(y: 19, width: 15, colors: [Colors.amber]), ]), BarChartGroupData(x: 8, barRods: [ BarChartRodData(y: 21, width: 15, colors: [Colors.amber]), ]), ]))), ), ); } }
生成效果如下图所示:

如果想显示每一行项目更多柱状图形的数量,试举例如下:
// main.dart import 'package:flutter/material.dart'; import 'package:fl_chart/fl_chart.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return const MaterialApp( // Remove the debug banner debugShowCheckedModeBanner: false, title: 'Flutter Demo', home: HomeScreen(), ); } } class HomeScreen extends StatelessWidget { const HomeScreen({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Flutter Demo'), ), body: SafeArea( child: Container( padding: const EdgeInsets.all(24.0), width: double.infinity, child: BarChart(BarChartData( borderData: FlBorderData( border: Border( top: BorderSide.none, right: BorderSide.none, left: BorderSide(width: 1), bottom: BorderSide(width: 1), )), groupsSpace: 10, barGroups: [ BarChartGroupData(x: 1, barRods: [ BarChartRodData(y: 3, width: 15, colors: [Colors.amber]), BarChartRodData(y: 4.2, width: 15, colors: [Colors.red]), BarChartRodData(y: 5, width: 15, colors: [Colors.blue]), ]), BarChartGroupData(x: 2, barRods: [ BarChartRodData(y: 8, width: 15, colors: [Colors.amber]), BarChartRodData(y: 6.2, width: 15, colors: [Colors.red]), BarChartRodData(y: 9, width: 15, colors: [Colors.blue]), ]), BarChartGroupData(x: 3, barRods: [ BarChartRodData(y: 13, width: 15, colors: [Colors.amber]), BarChartRodData(y: 9.1, width: 15, colors: [Colors.red]), BarChartRodData(y: 5.3, width: 15, colors: [Colors.blue]), ]), BarChartGroupData(x: 4, barRods: [ BarChartRodData(y: 15, width: 15, colors: [Colors.amber]), BarChartRodData(y: 11, width: 15, colors: [Colors.red]), BarChartRodData(y: 15, width: 15, colors: [Colors.blue]), ]), BarChartGroupData(x: 5, barRods: [ BarChartRodData(y: 8, width: 15, colors: [Colors.amber]), BarChartRodData(y: 9.2, width: 15, colors: [Colors.red]), BarChartRodData(y: 15, width: 15, colors: [Colors.blue]), ]), ]))), ), ); } }
生成效果如下图所示:

Circle Charts
- 饼状图的样式,试举例如下:
// main.dart import 'package:flutter/material.dart'; import 'package:fl_chart/fl_chart.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return const MaterialApp( // Remove the debug banner debugShowCheckedModeBanner: false, title: 'Flutter Demo', home: HomeScreen(), ); } } class HomeScreen extends StatelessWidget { const HomeScreen({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Flutter Demo'), ), body: SafeArea( child: Container( padding: const EdgeInsets.all(24.0), width: double.infinity, child: PieChart( PieChartData( centerSpaceRadius: 10, borderData: FlBorderData(show: false), sections: [ PieChartSectionData( value: 10, color: Colors.purple, radius: 100), PieChartSectionData( value: 20, color: Colors.amber, radius: 110), PieChartSectionData( value: 30, color: Colors.green, radius: 120) ]), ), ), ), ); } }
生成效果如下图所示:

另一种样式,类似于色环,试举一例:
// main.dart import 'package:flutter/material.dart'; import 'package:fl_chart/fl_chart.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return const MaterialApp( // Remove the debug banner debugShowCheckedModeBanner: false, title: 'Flutter Demo', home: HomeScreen(), ); } } class HomeScreen extends StatelessWidget { const HomeScreen({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Flutter Demo'), ), body: SafeArea( child: Container( padding: const EdgeInsets.all(24.0), width: double.infinity, child: PieChart( PieChartData( centerSpaceRadius: 10, borderData: FlBorderData(show: false), sections: [ PieChartSectionData( value: 10, color: Colors.purple, radius: 100), PieChartSectionData( value: 20, color: Colors.amber, radius: 110), PieChartSectionData( value: 30, color: Colors.green, radius: 120) ]), ), ), ), ); } }
生成效果如下图所示:

Radar Chart
- 雷达图,这个分布图很好看,需要安装一个名为flutter_radar_chart,安装和使用请参看这个官方文档地址:radar_chart
- 试举例如下:
// main.dart import 'package:flutter/material.dart'; import 'package:flutter_radar_chart/flutter_radar_chart.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return const MaterialApp( // Remove the debug banner debugShowCheckedModeBanner: false, title: 'Flutter Demo', home: HomeScreen(), ); } } class HomeScreen extends StatelessWidget { const HomeScreen({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Flutter Demo'), ), body: SafeArea( child: Container( padding: const EdgeInsets.all(24.0), width: double.infinity, child: const RadarChart( features: <String>["AA", "BB", "CC", "DD", "EE", "FF", "GG", "HH"], ticks: <int>[7, 14, 21, 28, 35], data: <List<int>>[ [10, 20, 28, 5, 16, 15, 17, 6], [15, 1, 4, 14, 23, 10, 6, 19] ], ), ), ), ); } }
生成效果如下图所示:

scatter_chart
- 点状图,甚至可以生成动画状点状图,这个需要安装一个名为fl_chart的支撑库,安装和例子请前往这个地址:fl_chart。
- 试举例如下:
// main.dart import 'dart:math'; import 'package:fl_chart/fl_chart.dart'; import 'package:flutter/material.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return const MaterialApp( // Remove the debug banner debugShowCheckedModeBanner: false, title: 'Flutter Demo', home: HomeScreen(), ); } } class HomeScreen extends StatefulWidget { const HomeScreen({Key? key}) : super(key: key); @override State<HomeScreen> createState() => _HomeScreenState(); } class _HomeScreenState extends State<HomeScreen> { final maxX = 50.0; final maxY = 50.0; final radius = 8.0; Color blue1 = const Color(0xFF0D47A1); Color blue2 = const Color(0xFF42A5F5).withOpacity(0.8); bool showFlutter = true; @override Widget build(BuildContext context) { return GestureDetector( onTap: () { setState(() { showFlutter = !showFlutter; }); }, child: AspectRatio( aspectRatio: 1, child: Card( color: const Color(0xffffffff), elevation: 6, child: ScatterChart( ScatterChartData( scatterSpots: showFlutter ? flutterLogoData() : randomData(), minX: 0, maxX: maxX, minY: 0, maxY: maxY, borderData: FlBorderData( show: false, ), gridData: FlGridData( show: false, ), titlesData: FlTitlesData( show: false, ), scatterTouchData: ScatterTouchData( enabled: false, ), ), swapAnimationDuration: const Duration(milliseconds: 600), ), ), ), ); } List<ScatterSpot> flutterLogoData() { return [ /// section 1 ScatterSpot(20, 14.5, color: blue1, radius: radius), ScatterSpot(22, 16.5, color: blue1, radius: radius), ScatterSpot(24, 18.5, color: blue1, radius: radius), ScatterSpot(22, 12.5, color: blue1, radius: radius), ScatterSpot(24, 14.5, color: blue1, radius: radius), ScatterSpot(26, 16.5, color: blue1, radius: radius), ScatterSpot(24, 10.5, color: blue1, radius: radius), ScatterSpot(26, 12.5, color: blue1, radius: radius), ScatterSpot(28, 14.5, color: blue1, radius: radius), ScatterSpot(26, 8.5, color: blue1, radius: radius), ScatterSpot(28, 10.5, color: blue1, radius: radius), ScatterSpot(30, 12.5, color: blue1, radius: radius), ScatterSpot(28, 6.5, color: blue1, radius: radius), ScatterSpot(30, 8.5, color: blue1, radius: radius), ScatterSpot(32, 10.5, color: blue1, radius: radius), ScatterSpot(30, 4.5, color: blue1, radius: radius), ScatterSpot(32, 6.5, color: blue1, radius: radius), ScatterSpot(34, 8.5, color: blue1, radius: radius), ScatterSpot(34, 4.5, color: blue1, radius: radius), ScatterSpot(36, 6.5, color: blue1, radius: radius), ScatterSpot(38, 4.5, color: blue1, radius: radius), /// section 2 ScatterSpot(20, 14.5, color: blue2, radius: radius), ScatterSpot(22, 12.5, color: blue2, radius: radius), ScatterSpot(24, 10.5, color: blue2, radius: radius), ScatterSpot(22, 16.5, color: blue2, radius: radius), ScatterSpot(24, 14.5, color: blue2, radius: radius), ScatterSpot(26, 12.5, color: blue2, radius: radius), ScatterSpot(24, 18.5, color: blue2, radius: radius), ScatterSpot(26, 16.5, color: blue2, radius: radius), ScatterSpot(28, 14.5, color: blue2, radius: radius), ScatterSpot(26, 20.5, color: blue2, radius: radius), ScatterSpot(28, 18.5, color: blue2, radius: radius), ScatterSpot(30, 16.5, color: blue2, radius: radius), ScatterSpot(28, 22.5, color: blue2, radius: radius), ScatterSpot(30, 20.5, color: blue2, radius: radius), ScatterSpot(32, 18.5, color: blue2, radius: radius), ScatterSpot(30, 24.5, color: blue2, radius: radius), ScatterSpot(32, 22.5, color: blue2, radius: radius), ScatterSpot(34, 20.5, color: blue2, radius: radius), ScatterSpot(34, 24.5, color: blue2, radius: radius), ScatterSpot(36, 22.5, color: blue2, radius: radius), ScatterSpot(38, 24.5, color: blue2, radius: radius), /// section 3 ScatterSpot(10, 25, color: blue2, radius: radius), ScatterSpot(12, 23, color: blue2, radius: radius), ScatterSpot(14, 21, color: blue2, radius: radius), ScatterSpot(12, 27, color: blue2, radius: radius), ScatterSpot(14, 25, color: blue2, radius: radius), ScatterSpot(16, 23, color: blue2, radius: radius), ScatterSpot(14, 29, color: blue2, radius: radius), ScatterSpot(16, 27, color: blue2, radius: radius), ScatterSpot(18, 25, color: blue2, radius: radius), ScatterSpot(16, 31, color: blue2, radius: radius), ScatterSpot(18, 29, color: blue2, radius: radius), ScatterSpot(20, 27, color: blue2, radius: radius), ScatterSpot(18, 33, color: blue2, radius: radius), ScatterSpot(20, 31, color: blue2, radius: radius), ScatterSpot(22, 29, color: blue2, radius: radius), ScatterSpot(20, 35, color: blue2, radius: radius), ScatterSpot(22, 33, color: blue2, radius: radius), ScatterSpot(24, 31, color: blue2, radius: radius), ScatterSpot(22, 37, color: blue2, radius: radius), ScatterSpot(24, 35, color: blue2, radius: radius), ScatterSpot(26, 33, color: blue2, radius: radius), ScatterSpot(24, 39, color: blue2, radius: radius), ScatterSpot(26, 37, color: blue2, radius: radius), ScatterSpot(28, 35, color: blue2, radius: radius), ScatterSpot(26, 41, color: blue2, radius: radius), ScatterSpot(28, 39, color: blue2, radius: radius), ScatterSpot(30, 37, color: blue2, radius: radius), ScatterSpot(28, 43, color: blue2, radius: radius), ScatterSpot(30, 41, color: blue2, radius: radius), ScatterSpot(32, 39, color: blue2, radius: radius), ScatterSpot(30, 45, color: blue2, radius: radius), ScatterSpot(32, 43, color: blue2, radius: radius), ScatterSpot(34, 41, color: blue2, radius: radius), ScatterSpot(34, 45, color: blue2, radius: radius), ScatterSpot(36, 43, color: blue2, radius: radius), ScatterSpot(38, 45, color: blue2, radius: radius), ]; } List<ScatterSpot> randomData() { const blue1Count = 21; const blue2Count = 57; return List.generate(blue1Count + blue2Count, (i) { Color color; if (i < blue1Count) { color = blue1; } else { color = blue2; } return ScatterSpot( (Random().nextDouble() * (maxX - 8)) + 4, (Random().nextDouble() * (maxY - 8)) + 4, color: color, radius: (Random().nextDouble() * 16) + 4, ); }); } }
生成效果如下图所示:
坐标系图
- 点状图,甚至可以生成动画状点状图,这个需要安装一个名为fl_chart的支撑库,安装和例子请前往这个地址:fl_chart。
- 试举例如下:
// main.dart import 'package:fl_chart/fl_chart.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; const gridColor = Color(0xff68739f); const titleColor = Color(0xff8c95db); const fashionColor = Color(0xffe15665); const artColor = Color(0xff63e7e5); const boxingColor = Color(0xff83dea7); const entertainmentColor = Colors.white70; const offRoadColor = Color(0xFFFFF59D); void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return const MaterialApp( // Remove the debug banner debugShowCheckedModeBanner: false, title: 'Flutter Demo', home: HomeScreen(), ); } } class HomeScreen extends StatefulWidget { const HomeScreen({Key? key}) : super(key: key); @override State<HomeScreen> createState() => _HomeScreenState(); } class _HomeScreenState extends State<HomeScreen> { int selectedDataSetIndex = -1; @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center, children: [ GestureDetector( onTap: () { setState(() { selectedDataSetIndex = -1; }); }, child: Text( 'Categories'.toUpperCase(), style: const TextStyle( color: titleColor, fontSize: 32, fontWeight: FontWeight.w300, ), ), ), const SizedBox(height: 4), Column( crossAxisAlignment: CrossAxisAlignment.start, children: rawDataSets() .asMap() .map((index, value) { final isSelected = index == selectedDataSetIndex; return MapEntry( index, GestureDetector( onTap: () { setState(() { selectedDataSetIndex = index; }); }, child: AnimatedContainer( duration: const Duration(milliseconds: 300), margin: const EdgeInsets.symmetric(vertical: 2), height: 26, decoration: BoxDecoration( color: isSelected ? gridColor.withOpacity(0.5) : Colors.transparent, borderRadius: BorderRadius.circular(46), ), padding: const EdgeInsets.symmetric( vertical: 4.0, horizontal: 6), child: Row( mainAxisSize: MainAxisSize.min, children: [ AnimatedContainer( duration: const Duration(milliseconds: 400), curve: Curves.easeInToLinear, padding: EdgeInsets.all(isSelected ? 8 : 6), decoration: BoxDecoration( color: value.color, shape: BoxShape.circle, ), ), const SizedBox(width: 8), AnimatedDefaultTextStyle( duration: const Duration(milliseconds: 300), curve: Curves.easeInToLinear, style: TextStyle( color: isSelected ? value.color : gridColor, ), child: Text(value.title), ), ], ), ), ), ); }) .values .toList(), ), AspectRatio( aspectRatio: 1.3, child: RadarChart( RadarChartData( radarTouchData: RadarTouchData(touchCallback: (response) { if (response.touchedSpot != null && response.touchInput is! PointerUpEvent && response.touchInput is! PointerExitEvent) { setState(() { selectedDataSetIndex = response.touchedSpot?.touchedDataSetIndex ?? -1; }); } else { setState(() { selectedDataSetIndex = -1; }); } }), dataSets: showingDataSets(), radarBackgroundColor: Colors.transparent, borderData: FlBorderData(show: false), radarBorderData: const BorderSide(color: Colors.transparent), titlePositionPercentageOffset: 0.2, titleTextStyle: const TextStyle(color: titleColor, fontSize: 14), getTitle: (index) { switch (index) { case 0: return 'Mobile or Tablet'; case 2: return 'Desktop'; case 1: return 'TV'; default: return ''; } }, tickCount: 1, ticksTextStyle: const TextStyle(color: Colors.transparent, fontSize: 10), tickBorderData: const BorderSide(color: Colors.transparent), gridBorderData: const BorderSide(color: gridColor, width: 2), ), swapAnimationDuration: const Duration(milliseconds: 400), ), ), ], ), ); } List<RadarDataSet> showingDataSets() { return rawDataSets().asMap().entries.map((entry) { var index = entry.key; var rawDataSet = entry.value; final isSelected = index == selectedDataSetIndex ? true : selectedDataSetIndex == -1 ? true : false; return RadarDataSet( fillColor: isSelected ? rawDataSet.color.withOpacity(0.4) : rawDataSet.color.withOpacity(0.25), borderColor: isSelected ? rawDataSet.color : rawDataSet.color.withOpacity(0.25), entryRadius: isSelected ? 3 : 2, dataEntries: rawDataSet.values.map((e) => RadarEntry(value: e)).toList(), borderWidth: isSelected ? 2.3 : 2, ); }).toList(); } List<RawDataSet> rawDataSets() { return [ RawDataSet( title: 'Fashion', color: fashionColor, values: [ 300, 50, 250, ], ), RawDataSet( title: 'Art & Tech', color: artColor, values: [ 250, 100, 200, ], ), RawDataSet( title: 'Entertainment', color: entertainmentColor, values: [ 200, 150, 50, ], ), RawDataSet( title: 'Off-road Vehicle', color: offRoadColor, values: [ 150, 200, 150, ], ), RawDataSet( title: 'Boxing', color: boxingColor, values: [ 100, 250, 100, ], ), ]; } } class RawDataSet { final String title; final Color color; final List<double> values; RawDataSet({ required this.title, required this.color, required this.values, }); }
生成效果如下图所示:

- 我的微信
- 微信扫一扫加好友
-
- 我的微信公众号
- 扫描关注公众号
-