如何在Flutter主程序中调用CustomPainter类的midX、midY变量?
解决CustomPainter中变量对外暴露的问题
问题背景
自定义PlotPainter(继承自CustomPainter)的paint方法中定义了midX和midY(对应坐标轴中点),需要在Flutter主程序中调用这两个变量的值,但使用static修饰符会触发报错,需其他实现方案。
可行解决方案
方案1:将midX、midY设为PlotPainter的实例变量
把midX和midY从paint方法的局部变量提升为类的实例变量,在paint方法中完成赋值,主程序通过持有PlotPainter的实例来访问这两个值。
修改后的PlotPainter代码:
class PlotPainter extends CustomPainter { // 定义实例变量,初始值设为0 double midX = 0; double midY = 0; @override void paint(Canvas canvas, Size size) { final a_Length = size.width / 12; // 给实例变量赋值 midY = size.height / 2; midX = size.width / 2; final paint = Paint() ..style = PaintingStyle.fill ..color = Colors.black ..strokeWidth = 1.8; final dottedline = Paint() ..strokeCap = StrokeCap.square ..strokeWidth = 1 ..color = Colors.greenAccent; final textPainterx = TextPainter( text: const TextSpan( text: 'x', style: TextStyle( color: Colors.black, fontSize: 15, ), ), textDirection: TextDirection.ltr, textAlign: TextAlign.center); final textPaintery = TextPainter( text: const TextSpan( text: 'y', style: TextStyle( color: Colors.black, fontSize: 13, ), ), textDirection: TextDirection.ltr, textAlign: TextAlign.center); // X axis canvas.drawLine(Offset(0, midY), Offset(size.width, midY), paint); //y Axis canvas.drawLine(Offset(midX, 0), Offset(midX, size.height), paint); textPainterx.layout(); textPaintery.layout(); // Draw the text X at the X axis textPainterx.paint(canvas, Offset(size.width - 7, midY + 1)); // Draw the text y at the Y axis textPaintery.paint(canvas, Offset(midX + 5, 0)); } @override bool shouldRepaint(CustomPainter oldDelegate) => false; }
主程序修改(改为StatefulWidget):
class MyHomePage extends StatefulWidget { const MyHomePage({super.key}); @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { final PlotPainter _plotPainter = PlotPainter(); double? displayMidX; double? displayMidY; @override void initState() { super.initState(); // 等待第一帧绘制完成后获取值 WidgetsBinding.instance.addPostFrameCallback((_) { setState(() { displayMidX = _plotPainter.midX; displayMidY = _plotPainter.midY; }); }); } @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( body: Column( children: [ Center( child: Container( alignment: Alignment.center, height: MediaQuery.of(context).size.width, width: MediaQuery.of(context).size.width, color: Colors.grey, padding: const EdgeInsets.symmetric( horizontal: 10, vertical: 10), child: LayoutBuilder( builder: (_, constraints) => Container( width: constraints.maxWidth, height: constraints.maxHeight, color: Colors.white, child: Stack(children: [ // plotting X Y axis Container( width: constraints.maxWidth, height: constraints.maxHeight, child: CustomPaint(painter: _plotPainter)), ]), )), ), ), Expanded( child: SingleChildScrollView( scrollDirection: Axis.vertical, child: Column(children: [ Text("mid pixel value for x axis:${displayMidX ?? 'Loading...'}"), Text("mid pixel value for y axis:${displayMidY ?? 'Loading...'}"), ]), ), ) ], ), ), ); } }
方案2:通过回调函数传递值
在PlotPainter中定义一个回调函数,当paint方法计算出midX和midY后,调用回调把值传递给主程序。
修改后的PlotPainter:
class PlotPainter extends CustomPainter { final Function(double midX, double midY) onMidValuesCalculated; PlotPainter({required this.onMidValuesCalculated}); @override void paint(Canvas canvas, Size size) { final a_Length = size.width / 12; final midY = size.height / 2; final midX = size.width / 2; // 调用回调传递值 onMidValuesCalculated(midX, midY); final paint = Paint() ..style = PaintingStyle.fill ..color = Colors.black ..strokeWidth = 1.8; final dottedline = Paint() ..strokeCap = StrokeCap.square ..strokeWidth = 1 ..color = Colors.greenAccent; final textPainterx = TextPainter( text: const TextSpan( text: 'x', style: TextStyle( color: Colors.black, fontSize: 15, ), ), textDirection: TextDirection.ltr, textAlign: TextAlign.center); final textPaintery = TextPainter( text: const TextSpan( text: 'y', style: TextStyle( color: Colors.black, fontSize: 13, ), ), textDirection: TextDirection.ltr, textAlign: TextAlign.center); // X axis canvas.drawLine(Offset(0, midY), Offset(size.width, midY), paint); //y Axis canvas.drawLine(Offset(midX, 0), Offset(midX, size.height), paint); textPainterx.layout(); textPaintery.layout(); // Draw the text X at the X axis textPainterx.paint(canvas, Offset(size.width - 7, midY + 1)); // Draw the text y at the Y axis textPaintery.paint(canvas, Offset(midX + 5, 0)); } @override bool shouldRepaint(CustomPainter oldDelegate) => false; }
主程序修改(使用StatefulWidget接收值):
class MyHomePage extends StatefulWidget { const MyHomePage({super.key}); @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { double? displayMidX; double? displayMidY; @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( body: Column( children: [ Center( child: Container( alignment: Alignment.center, height: MediaQuery.of(context).size.width, width: MediaQuery.of(context).size.width, color: Colors.grey, padding: const EdgeInsets.symmetric( horizontal: 10, vertical: 10), child: LayoutBuilder( builder: (_, constraints) => Container( width: constraints.maxWidth, height: constraints.maxHeight, color: Colors.white, child: Stack(children: [ // plotting X Y axis Container( width: constraints.maxWidth, height: constraints.maxHeight, child: CustomPaint( painter: PlotPainter( onMidValuesCalculated: (midX, midY) { setState(() { displayMidX = midX; displayMidY = midY; }); }, ), )), ]), )), ), ), Expanded( child: SingleChildScrollView( scrollDirection: Axis.vertical, child: Column(children: [ Text("mid pixel value for x axis:${displayMidX ?? 'Loading...'}"), Text("mid pixel value for y axis:${displayMidY ?? 'Loading...'}"), ]), ), ) ], ), ), ); } }
方案3:主程序提前计算mid值并传递给PlotPainter
因为midX是size.width/2,midY是size.height/2,主程序在LayoutBuilder中已经能拿到constraints的尺寸,可以直接计算这两个值,再传递给PlotPainter使用,这样主程序本身就持有这些值,无需从Painter中获取。
修改后的PlotPainter:
class PlotPainter extends CustomPainter { final double midX; final double midY; PlotPainter({required this.midX, required this.midY}); @override void paint(Canvas canvas, Size size) { final a_Length = size.width / 12; final paint = Paint() ..style = PaintingStyle.fill ..color = Colors.black ..strokeWidth = 1.8; final dottedline = Paint() ..strokeCap = StrokeCap.square ..strokeWidth = 1 ..color = Colors.greenAccent; final textPainterx = TextPainter( text: const TextSpan( text: 'x', style: TextStyle( color: Colors.black, fontSize: 15, ), ), textDirection: TextDirection.ltr, textAlign: TextAlign.center); final textPaintery = TextPainter( text: const TextSpan( text: 'y', style: TextStyle( color: Colors.black, fontSize: 13, ), ), textDirection: TextDirection.ltr, textAlign: TextAlign.center); // X axis canvas.drawLine(Offset(0, midY), Offset(size.width, midY), paint); //y Axis canvas.drawLine(Offset(midX, 0), Offset(midX, size.height), paint); textPainterx.layout(); textPaintery.layout(); // Draw the text X at the X axis textPainterx.paint(canvas, Offset(size.width - 7, midY + 1)); // Draw the text y at the Y axis textPaintery.paint(canvas, Offset(midX + 5, 0)); } @override bool shouldRepaint(CustomPainter oldDelegate) => false; }
主程序修改:
class MyHomePage extends StatefulWidget { const MyHomePage({super.key}); @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { double? displayMidX; double? displayMidY; @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( body: Column( children: [ Center( child: Container( alignment: Alignment.center, height: MediaQuery.of(context).size.width, width: MediaQuery.of(context).size.width, color: Colors.grey, padding: const EdgeInsets.symmetric( horizontal: 10, vertical: 10), child: LayoutBuilder( builder: (_, constraints) { // 主程序计算mid值 final midX = constraints.maxWidth / 2; final midY = constraints.maxHeight / 2; setState(() { displayMidX = midX; displayMidY = midY; }); return Container( width: constraints.maxWidth, height: constraints.maxHeight, color: Colors.white, child: Stack(children: [ // plotting X Y axis Container( width: constraints.maxWidth, height: constraints.maxHeight, child: CustomPaint( painter: PlotPainter(midX: midX, midY: midY), )), ]), ); }), ), ), Expanded( child: SingleChildScrollView( scrollDirection: Axis.vertical, child: Column(children: [ Text("mid pixel value for x axis:${displayMidX ?? 'Loading...'}"), Text("mid pixel value for y axis:${displayMidY ?? 'Loading...'}"), ]), ), ) ], ), ), ); } }
总结
- 如果需要在Painter内部计算复杂值,方案1或方案2更合适;
- 如果mid值只是尺寸的简单计算,方案3最直接,避免了跨组件传递的复杂度。
内容的提问来源于stack exchange,提问作者Tan K
相关产品推荐
相关产品推荐

