
开篇寄语
这一篇文章主要是学习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,
});
}
生成效果如下图所示:

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






