一、IntrinsicHeight
1.在最坏的情况下,这个渲染对象可能导致树深度为 O(N²)
的时间复杂度。IntrinsicHeight
会触发 渲染树中其所有子树节点
的 getMaxIntrinsicHeight
方法来获取尺寸。
2.通过源码可知:在父级约束是紧约束的情况下 IntrinsicHeight
是毫无作用
的。
3.IntrinsicHeight
的功能实现,就是通过为子级在高度上施加紧约束实现的。
二、时光轴
思路:左边的直线和虚线圆点都是都是通过自定义Decoration实现的,右边的内容可以自定义,我这边就是放了个颜色块。因为右边的高度不定,导致左边的直线高度不定,这里采用IntrinsicHeight套在顶层,就可以让左边的widget跟随右边的高度变化。
三、自定义Decoration
1.写个类实现Decoration
@override
BoxPainter createBoxPainter([VoidCallback? onChanged]) {
return DashBoxPainter(decoration: this);
}
2.他需要BoxPainter,也需要实现一个类
///Offset 表示左上角的位置,configuration.size是可以拿到对应的控件大小的
@override
void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {
}
四、整体的代码实现
import 'package:dash_painter/dash_painter.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class TimeLineNode extends StatelessWidget {
double contentHeight;
TimeLineNode({Key? key, required this.contentHeight}) : super(key: key);
final double marginTop = 10;
final double dashLineWidth = 20;
final double offset = 20 + 10;
final double lineWidth = 2;
final double circleRadius = 5;
@override
Widget build(BuildContext context) {
return IntrinsicHeight(
child: Row(
children: [
_buildDecoration(),
Expanded(
flex: 5,
child: Container(
color: Colors.red,
height: contentHeight,
)),
const Spacer(
flex: 2,
)
],
),
);
}
Widget _buildDecoration() {
return Container(
margin: const EdgeInsets.only(left: 20),
width: dashLineWidth,
///不用写高,因为高根据布局最大显示
decoration: DashDecoration(
circleColor: Colors.blueAccent,
lineColor: Colors.white,
circleRadius: circleRadius,
color: Colors.white,
circleOffset: Offset(lineWidth / 2, offset + 10 / 2)),
);
}
}
class DashDecoration extends Decoration {
final Color color;
final Color circleColor;
final Color lineColor;
final Offset circleOffset;
final double circleRadius;
const DashDecoration(
{required this.color,
required this.circleColor,
required this.lineColor,
required this.circleOffset,
required this.circleRadius});
@override
BoxPainter createBoxPainter([VoidCallback? onChanged]) {
return DashBoxPainter(decoration: this);
}
}
class DashBoxPainter extends BoxPainter {
final DashDecoration decoration;
const DashBoxPainter({required this.decoration});
///Offset 表示左上角的位置,configuration.size是可以拿到对应的控件大小的
@override
void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {
canvas.save();
final Paint paint = Paint()..style = PaintingStyle.stroke;
final Path path = Path();
// canvas.drawCircle(offset, 5, Paint()..color = Colors.blue);
debugPrint("offset ${offset.dx} dy : ${offset.dy}");
///为什么上面canvas需要保存,这里的canvas有平移
canvas.translate(offset.dx, offset.dy);
// canvas.drawCircle(offset, 5, Paint()..color = Colors.yellow);
//绘制直线
canvas.drawLine(
Offset(-decoration.circleOffset.dx, -decoration.circleOffset.dy),
Offset(-decoration.circleOffset.dx, configuration.size!.height),
paint
..color = decoration.lineColor
..strokeWidth = 2);
//绘制虚线
path
..moveTo(0, decoration.circleOffset.dy)
..relativeLineTo(configuration.size!.width, 0);
const DashPainter(span:2,step:3).
paint(canvas, path, paint..color = decoration.color..strokeWidth =1);
//绘制圆点
final Paint paint2 = Paint()..color = Colors.white;
canvas.drawCircle(
Offset(decoration.circleOffset.dx, decoration.circleOffset.dy),
decoration.circleRadius,
paint2);
canvas.drawCircle(
Offset(decoration.circleOffset.dx, decoration.circleOffset.dy),
decoration.circleRadius * 0.6,
paint2..color = decoration.circleColor);
canvas.restore();
}
}
调用代码
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
// home: OverlayPage(),
home: Scaffold(
backgroundColor: Colors.black,
body: Container(
margin: const EdgeInsets.only(top: 46),
child: Column(children : [
TimeLineNode(contentHeight:50),
const SizedBox(height: 10,),
TimeLineNode(contentHeight:100),
const SizedBox(height: 10,),
TimeLineNode(contentHeight:150),
]))),
);
}
}