随着前两篇文章的学习,我今天继续给大家演示下简单的自定义之折线波动图,心率图,价格走势图。
这里,我们创建一个自定义的StatefulWidget,用于显示动态的价格线。
我们将使用CustomPaint和CustomPainter来绘制价格线。
在lib/main.dart
文件中添加以下代码:
import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('动态价格线')),
body: DynamicPriceLine(),
),
);
}
}
class DynamicPriceLine extends StatefulWidget {
@override
_DynamicPriceLineState createState() => _DynamicPriceLineState();
}
class _DynamicPriceLineState extends State<DynamicPriceLine> {
List<double> prices = List.generate(100, (index) => 50);
Timer _timer;
@override
void initState() {
super.initState();
_timer = Timer.periodic(Duration(milliseconds: 100), (timer) {
setState(() {
prices.removeAt(0);
double lastPrice = prices.last;
double newPrice = lastPrice + (Random().nextDouble() * 4 - 2);
prices.add(newPrice);
});
});
}
@override
void dispose() {
_timer.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return CustomPaint(
painter: LinePainter(prices),
);
}
}
class LinePainter extends CustomPainter {
final List<double> prices;
LinePainter(this.prices);
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.blue
..strokeWidth = 2
..style = PaintingStyle.stroke;
final double minY = prices.reduce(min);
final double maxY = prices.reduce(max);
final double rangeY = maxY - minY;
final double scaleX = size.width / (prices.length - 1);
final double scaleY = size.height / rangeY;
for (int i = 1; i < prices.length; i++) {
canvas.drawLine(
Offset((i - 1) * scaleX, size.height - (prices[i - 1] - minY) * scaleY),
Offset(i * scaleX, size.height - (prices[i] - minY) * scaleY),
paint,
);
}
}
@override
bool shouldRepaint(covariant LinePainter oldDelegate) =>
oldDelegate.prices != prices;
}
现在,运行你的应用程序,你将看到一个类似心率图的动态价格线。
发现价格线不动,界面没有实时展示。
运行发现并不能满足我们的需求,我们要一个整个视图要自动向左边移动,始终展示最新的绘制线。
思考之后我决定将使用Transform.translate
来实现视图的移动效果。
class DynamicPriceLine extends StatefulWidget {
@override
_DynamicPriceLineState createState() => _DynamicPriceLineState();
}
class _DynamicPriceLineState extends State<DynamicPriceLine> {
List<double> prices = List.generate(100, (index) => 50);
Timer _timer;
double offsetX = 0;
@override
void initState() {
super.initState();
_timer = Timer.periodic(Duration(milliseconds: 100), (timer) {
setState(() {
offsetX -= 2;
prices.removeAt(0);
double lastPrice = prices.last;
double newPrice = lastPrice + (Random().nextDouble() * 4 - 2);
prices.add(newPrice);
});
});
}
@override
void dispose() {
_timer.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Transform.translate(
offset: Offset(offsetX, 0),
child: CustomPaint(
painter: LinePainter(prices),
),
);
}
}
class LinePainter extends CustomPainter {
final List<double> prices;
LinePainter(this.prices);
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.blue
..strokeWidth = 2
..style = PaintingStyle.stroke;
final double minY = prices.reduce(min);
final double maxY = prices.reduce(max);
final double rangeY = maxY - minY;
final double scaleX = size.width / (prices.length - 1);
final double scaleY = size.height / rangeY;
for (int i = 1; i < prices.length; i++) {
canvas.drawLine(
Offset((i - 1) * scaleX, size.height - (prices[i - 1] - minY) * scaleY),
Offset(i * scaleX, size.height - (prices[i] - minY) * scaleY),
paint,
);
}
}
@override
bool shouldRepaint(covariant LinePainter oldDelegate) =>
oldDelegate.prices != prices;
}
我们使用了Transform.translate
来实现视图的移动效果。
每次更新价格数据时,offsetX
将减去一个固定值(例如2),从而使视图向左移动。
现在运行你的应用程序,你将看到一个动态价格线,并且视图会自动向左移动以始终显示最新的绘制线。
上图就是基本的自定义显示效果,没有录屏,原谅我,如果是动态的更直观。
当然这都是最基本的自定义,需要扩展的大家下来实现吧。