原文链接:Flutter Chart Magic with fl_chart - 原文作者 Ece
本文采用意译的方式,针对该作者的四篇系列文翻译
在这些文章中,我们将展示怎么使用 fl_chart 来创建图表不同类型。我们将从简单的事情开始,比如线性图表和饼状图表,然后我们深入其他高级特性,使用图表探索炫酷的事物。
fl_chart
就像一个魔法棒一样在 Flutter
中绘图。它很容易被使用,即使我才刚开始用。它让我们创造各种炫酷类型图表,并以令人兴奋的方式展示我们的数据。
Flutter
中线性/折线图表在贯穿我们整个数据故事的一次视觉之旅。它们将点关联起来,来展示数据如何变化或增长。我们可以想象是我们数据的冒险,非常适合展示趋势或者起伏。最好的部分是什么?我们可以让这些图表看起来很棒,并和我们应用风格相搭。
添加下面的依赖到 pubspec.yaml
文件中。
fl_chart: ^0.65.0
创建 HomePage
。
在 home_page.dart
文件中,让我们创建三个按钮来演示我们的案例。在 routes.dart
文件中,我们已经设定了导航到我们的案例,你们可以查看源码来获取更详细的内容。
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:line_chart/utils/routes.dart';
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
Navigator.of(context).pushNamed(Routes.fistLineChart);
},
child: const Text(
'First Chart',
style: TextStyle(
color: Color(0xFF5F6F52),
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
),
const Gap(25),
ElevatedButton(
onPressed: () {
Navigator.of(context).pushNamed(Routes.secondLineChart);
},
child: const Text(
'Second Chart',
style: TextStyle(
color: Color(0xFF5F6F52),
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
),
const Gap(25),
ElevatedButton(
onPressed: () {
Navigator.of(context).pushNamed(Routes.thirdLineChart);
},
child: const Text(
'Third Chart',
style: TextStyle(
color: Color(0xFF5F6F52),
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
),
],
),
),
);
}
}
导入包并开始编码。
import 'package:fl_chart/fl_chart.dart';
随后,我们将创建第一个例子,遵循类似的代码结构,并将“视图”和“小部件”明确分开。我们从“视图”部分开始。
在 lib
文件夹中的 views
下创建 first_line_chart.dart
文件。
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:line_chart/widgets/first_line_chart_widget.dart';
class FirstLineChartPage extends StatefulWidget {
const FirstLineChartPage({super.key});
@override
State<FirstLineChartPage> createState() => _FirstLineChartPageState();
}
class _FirstLineChartPageState extends State<FirstLineChartPage> {
late bool showingData;
@override
void initState() {
super.initState();
showingData = true;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.grey.shade200,
),
body: AspectRatio(
aspectRatio: 1,
child: Stack(
children: [
Column(
children: [
const Gap(50),
const Text(
'First Line Chart',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
const Gap(25),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: FirstLineChartWidget(
showingData: showingData,
),
),
),
],
),
Positioned(
top: 40,
left: 10,
child: IconButton(
onPressed: () {
setState(() {
showingData = !showingData;
});
},
icon: const Icon(Icons.refresh),
),
),
],
),
),
);
}
}
接下来,在折线图中,我们探索第二个例子。
在 lib
文件夹的 views
下创建 second_line_chart.dart
文件。
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:line_chart/widgets/second_line_chart_widget.dart';
class SecondLineChartPage extends StatelessWidget {
const SecondLineChartPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.grey.shade200,
),
body: const AspectRatio(
aspectRatio: 1,
child: Column(
children: [
Gap(50),
Text(
'Second Line Chart',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
Gap(25),
Expanded(
child: Padding(
padding: EdgeInsets.only(left: 10, right: 15),
child: SecondLineChartWidget(),
),
),
],
),
),
);
}
}
最后的压轴戏,最后的一个例子有一点动画效果。
在 lib
文件夹的 views
下创建 third_line_chart.dart
文件。
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:line_chart/widgets/third_line_chart_widget.dart';
class ThirdLineChartPage extends StatelessWidget {
const ThirdLineChartPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.grey.shade200,
),
body: const Column(
children: [
Gap(50),
Text(
'Third Line Chart',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
Gap(25),
Expanded(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 10),
child: ThirdLineChartWidget(),
),
),
],
),
);
}
}
代码引入步骤同 折线图表
在 lib
文件夹的 views
下创建 first_bar_chart.dart
文件。
import 'package:bar_chart/widgets/first_bar_chart_widget.dart';
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
class FirsBarChartPage extends StatefulWidget {
const FirsBarChartPage({super.key});
@override
State<FirsBarChartPage> createState() => _FirsBarChartPageState();
}
class _FirsBarChartPageState extends State<FirsBarChartPage> {
bool isPlaying = false;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.grey.shade200,
),
body: AspectRatio(
aspectRatio: 1,
child: Stack(
children: [
Column(
children: [
const Gap(50),
const Text(
'First Bar Chart',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
),
),
const Gap(25),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: FirstBarChartWidget(
isPlaying: isPlaying,
),
),
),
],
),
Padding(
padding: const EdgeInsets.only(top: 40, right: 25),
child: Align(
alignment: Alignment.topRight,
child: IconButton(
onPressed: () {
setState(() {
isPlaying = !isPlaying;
if (isPlaying) {
refreshState();
}
});
},
icon: Icon(isPlaying ? Icons.pause : Icons.play_arrow),
),
),
),
],
),
),
);
}
Future<dynamic> refreshState() async {
setState(() {});
await Future<dynamic>.delayed(
const Duration(milliseconds: 300),
);
if (isPlaying) {
await refreshState();
}
}
}
我们来探索第二个例子!
在 lib
文件夹的 views
下创建 second_bar_chart.dart
文件。
import 'package:bar_chart/widgets/second_bar_chart_widget.dart';
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
class SecondBarChartPage extends StatelessWidget {
const SecondBarChartPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.grey.shade200,
),
body: const AspectRatio(
aspectRatio: 1,
child: Column(
children: [
Gap(50),
Text(
'Second Bar Chart',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
),
),
Gap(25),
Expanded(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 10),
child: SecondBarChartWidget(),
),
),
],
),
),
);
}
}
我们到了最后一个重磅的例子...
在 lib
文件夹的 views
下创建 third_bar_chart.dart
文件。
import 'package:bar_chart/widgets/third_bar_chart_widget.dart';
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
class ThirdBarChartPage extends StatelessWidget {
const ThirdBarChartPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.grey.shade200,
),
body: const AspectRatio(
aspectRatio: 1,
child: Column(
children: [
Gap(50),
Text(
'Third Bar Chart',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
),
),
Gap(25),
Expanded(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 10),
child: ThirdBarChartWidget(),
),
),
],
),
),
);
}
}
代码引入步骤同 折线图表
在 lib
文件夹的 views
下创建 first_pie_chart.dart
文件。
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:pie_chart/widgets/first_pie_chart_widget.dart';
class FirstPieChartPage extends StatelessWidget {
const FirstPieChartPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.grey.shade200,
),
body: const AspectRatio(
aspectRatio: 1,
child: Column(
children: [
Gap(50),
Text(
'First Pie Chart',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
),
),
Gap(35),
Expanded(
child: FirstPieChartWidget(),
),
],
),
),
);
}
}
在 indicator.dart
文件中,我们设定了指示器挂件来展示饼状图的章节信息。
第二个例子也是饼状图最后的一个例子...
在 lib
文件夹的 views
下创建 second_pie_chart.dart
文件。
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:pie_chart/widgets/second_pie_chart_widget.dart';
class SecondPieChartPage extends StatelessWidget {
const SecondPieChartPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.grey.shade200,
),
body: const AspectRatio(
aspectRatio: 1.3,
child: Column(
children: [
Gap(50),
Text(
'Second Pie Chart',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
),
),
Gap(50),
Expanded(
child: SecondPieChartWidget(),
),
],
),
),
);
}
}
代码引入步骤同 折线图表
在 lib
文件夹的 views
下创建 first_scatter_chart.dart
文件。
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:scatter_chart/widgets/first_scartter_chart_widget.dart';
class FirstScatterChartPage extends StatefulWidget {
const FirstScatterChartPage({super.key});
@override
State<FirstScatterChartPage> createState() => _FirstScatterChartPageState();
}
class _FirstScatterChartPageState extends State<FirstScatterChartPage> {
bool showFlutter = false;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.grey.shade200,
),
body: GestureDetector(
onTap: () {
setState(() {
showFlutter = !showFlutter;
});
},
child: AspectRatio(
aspectRatio: 1,
child: Column(
children: [
const Gap(50),
const Text(
'First Scatter Chart',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
),
),
const Gap(25),
Expanded(
child: FirstScatterChartWidget(
showFlutter: showFlutter,
),
),
],
),
),
),
);
}
}
第二个也是最后一个散点图案例...
在 lib
文件夹的 views
下创建 second_scatter_chart.dart
文件。
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:scatter_chart/widgets/second_scatter_chart_widget.dart';
class SecondScatterChartPage extends StatelessWidget {
const SecondScatterChartPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.grey.shade200,
),
body: const AspectRatio(
aspectRatio: 1,
child: Column(
children: [
Gap(50),
Text(
'Second Scatter Chart',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
),
),
Gap(25),
Expanded(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 10),
child: SecondScatterChartWidget(),
),
),
],
),
),
);
}
}
衷心感谢我们已经踏上 fl_chart
这个让人激动的冒险之旅!🚀