You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 09:31:13