3

我的页面中有这个自定义路径

class TestPathPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..style = PaintingStyle.stroke
      ..strokeWidth = 2.0
      ..color = Colors.black;

    var x = size.width;
    var y = size.height;
    print(x);

    var path = Path()
      ..moveTo(x, y / 4)
      ..lineTo(x * 0.95, y / 4)
      ..lineTo(x * 0.95, y / 3)
      ..lineTo(x * 0.99, y / 3)
      ..lineTo(x * 0.99, y / 3.7)
      ..lineTo(x * 0.955, y / 3.7)
      ..lineTo(x * 0.955, y / 3.15)
      ..lineTo(x * 0.98, y / 3.15)
      ..lineTo(x * 0.98, y / 3.5)
      ..lineTo(x * 0.94, y / 3.5) //  <==== I want to display a Checkbox here
      ..lineTo(x * 0.94, y / 2)
      ..lineTo(x * 0.91, y / 2)
      ..lineTo(x * 0.91, y / 1.65)
      ..lineTo(x * 0.94, y / 1.65)
      ..lineTo(x * 0.94, y / 1.4)
      ..lineTo(x * 0.91, y / 1.4);

    canvas.drawPath(path, paint);
  }

  @override
  bool shouldRepaint(TestPathPainter oldDelegate) => false;
}

如何在此路径的某处绘制/渲染小部件(例如复选框)?

我尝试使用堆叠的小部件并定位复选框,但这在每个设备上看起来都不一样。

4

1 回答 1

2

在此处输入图像描述

将小部件放置在屏幕上特定位置的最简单方法是使用堆栈和Positioned小部件。

但是,我们面临的问题是定位(top, right, bottom, left)是指child小部件的侧面而不是中心。

所以,我们需要调整定位。

解决方案 1 - 使用SizedBox > Center

Positioned(
  left: point.dx * width - 24,
  top: point.dy * height - 24,
  child: SizedBox(
    width: 48,
    height: 48,
    child: Center(
      child: Checkbox(
        value: true,
        onChanged: (_) {},
      ),
    ),
  ),
),

48已选择以确保我们大于完整尺寸(包括由materialTapTargetSizevisualDensity.

这将我们引向第二个解决方案。

解决方案 2:计算Checkbox

虽然 Checkbox 有一个static const width18但它可能会根据materialTapTargetSize和而有所不同visualDensity

如果我们看一下GitHub 上的源代码CheckBox

在此处输入图像描述

我们可以定义一个ComputeCheckBoxSize

double computeCheckBoxSize(BuildContext context) {
  final ThemeData themeData = Theme.of(context);
  final MaterialTapTargetSize effectiveMaterialTapTargetSize =
      themeData.checkboxTheme.materialTapTargetSize ??
          themeData.materialTapTargetSize;
  final VisualDensity effectiveVisualDensity =
      themeData.checkboxTheme.visualDensity ?? themeData.visualDensity;
  Size size;
  switch (effectiveMaterialTapTargetSize) {
    case MaterialTapTargetSize.padded:
      size = const Size(kMinInteractiveDimension, kMinInteractiveDimension);
      break;
    case MaterialTapTargetSize.shrinkWrap:
      size = const Size(
          kMinInteractiveDimension - 8.0, kMinInteractiveDimension - 8.0);
      break;
  }
  size += effectiveVisualDensity.baseSizeAdjustment;
  return size.longestSide;
}

我们的Positioned小部件可以进一步简化为:

Positioned(
  left: point.dx * width - checkBoxSize / 2,
  top: point.dy * height - checkBoxSize / 2,
  child: Checkbox(
    value: true,
    onChanged: (_) {},
  ),
)

完整的源代码

import 'dart:math' show Random;
import 'package:flutter/material.dart';

void main() {
  runApp(
    MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Flutter Demo',
      home: HomePage(),
    ),
  );
}

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    double checkBoxSize = computeCheckBoxSize(context);
    return Scaffold(
      body: LayoutBuilder(
        builder: (context, constraints) {
          final height = constraints.biggest.height;
          final width = constraints.biggest.width;
          return Stack(
            children: [
              Container(color: Colors.amber.shade100),
              Positioned.fill(child: CustomPaint(painter: TestPathPainter())),
              ...points
                  .map(
                    (point) => Positioned(
                      left: point.dx * width - checkBoxSize / 2,
                      top: point.dy * height - checkBoxSize / 2,
                      child: Checkbox(
                        value: true,
                        onChanged: (_) {},
                      ),
                    ),
                  )
                  .toList(),
            ],
          );
        },
      ),
    );
  }
}

double computeCheckBoxSize(BuildContext context) {
  final ThemeData themeData = Theme.of(context);
  final MaterialTapTargetSize effectiveMaterialTapTargetSize =
      themeData.checkboxTheme.materialTapTargetSize ??
          themeData.materialTapTargetSize;
  final VisualDensity effectiveVisualDensity =
      themeData.checkboxTheme.visualDensity ?? themeData.visualDensity;
  Size size;
  switch (effectiveMaterialTapTargetSize) {
    case MaterialTapTargetSize.padded:
      size = const Size(kMinInteractiveDimension, kMinInteractiveDimension);
      break;
    case MaterialTapTargetSize.shrinkWrap:
      size = const Size(
          kMinInteractiveDimension - 8.0, kMinInteractiveDimension - 8.0);
      break;
  }
  size += effectiveVisualDensity.baseSizeAdjustment;
  print(size);
  return size.longestSide;
}

class TestPathPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..style = PaintingStyle.stroke
      ..strokeWidth = 2.0
      ..color = Colors.black;

    final path = Path()
      ..moveTo(
        points[0].dx * size.width,
        points[0].dy * size.height,
      );
    points.sublist(1).forEach(
          (point) => path.lineTo(
            point.dx * size.width,
            point.dy * size.height,
          ),
        );
    canvas.drawPath(path, paint);
  }

  @override
  bool shouldRepaint(TestPathPainter oldDelegate) => false;
}

final random = Random();
final List<Offset> points = List.generate(
  10,
  (index) => Offset(.1 + random.nextDouble() * .8, .1 + index * .8 / 9),
);
于 2021-02-21T14:15:07.873 回答