ないぱかの記録

ないぱかからあるぱかになれるか

【Flutter】破線(切り取り線)を実装する

概要

破線を表示できるWidgetを実装してみたので備忘録として残します。

開発環境

Tool Version
macOS 12.2.1
Visual Studio Code 1.65.2
Flutter 2.10.3
Dart 2.16.0

前提

下記のような画面を元にAとBの間に破線を実装します。

実装

実装方法は2通り。

カスタマイズ性を求めない場合

まずは、カスタマイズ性をそれほど求めない場合の実装についてです。

例えば破線の間のスペース間隔や領域をはみ出した部分をどうするかなどを気にしない場合はこちらで問題ないかと思います。

以下実装コードです。

import 'package:flutter/material.dart';

/// 破線
class DashedLine extends StatelessWidget {
  const DashedLine({
    Key? key,
    this.dashedWidth = 5,
    this.dashedHeight = 1,
    this.color = Colors.black,
  }) : super(key: key);

  /// 破線単体の幅
  final double dashedWidth;

  /// 破線の高さ
  final double dashedHeight;

  /// 破線の色
  final Color color;

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (BuildContext context, BoxConstraints constraints) {
        // 破線単体の数
        // 表示する領域の最大幅 / (破線の幅 * 2)
        // 破線同士の間隔は破線の幅と同じ幅となる
        final dashedCount = (constraints.maxWidth / (2 * dashedWidth)).floor();
        return Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: List.generate(dashedCount, (_) {
            return Container(
              width: dashedWidth,
              height: dashedHeight,
              color: color,
            );
          }),
        );
      },
    );
  }
}

やっていることは「表示する領域の最大幅」と「破線の幅」から表示可能な破線単体の数を割り出して、その数の分だけ指定された幅と高さと色のContainerを表示しているだけです。

こちらをサンプルの画面に適用して切り取り線ぽくしてみると下記のような形になります。

カスタマイズ性を求める場合

続いて、カスタマイズ性を求めたい場合の実装についてです。

例えば破線の間のスペース間隔や描画領域をはみ出した部分をどうするかなどを設定できるようにしたい場合はこちらの実装をおすすめします。

以下実装コードです。

import 'package:flutter/material.dart';

/// 破線
class DashedLine extends StatelessWidget {
  const DashedLine({
    Key? key,
    this.dashedWidth = 10,
    this.dashedHeight = 5,
    this.dashedSpace = 5,
    this.color = Colors.black,
  }) : super(key: key);

  /// 破線単体の幅
  final double dashedWidth;

  /// 破線の高さ
  final double dashedHeight;

  /// 破線の間隔
  final double dashedSpace;

  /// 破線の色
  final Color color;

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      height: dashedHeight,
      child: CustomPaint(
        painter: _DashedLinePainter(
          dashedWidth: dashedWidth,
          dashedHeight: dashedHeight,
          dashedSpace: dashedSpace,
          color: color,
        ),
      ),
    );
  }
}

class _DashedLinePainter extends CustomPainter {
  const _DashedLinePainter({
    required this.dashedWidth,
    required this.dashedHeight,
    required this.dashedSpace,
    required this.color,
  });

  /// 破線単体の幅
  final double dashedWidth;

  /// 破線の高さ
  final double dashedHeight;

  /// 破線の間隔
  final double dashedSpace;

  /// 破線の色
  final Color color;

  @override
  void paint(Canvas canvas, Size size) {
    var dashedStartX = 0.0;
    final paint = Paint()
      ..color = color
      ..strokeWidth = dashedHeight;
    while (dashedStartX < size.width) {
      // 始点
      final startOffset = Offset(dashedStartX, 0);
      // 終点
      final endOffset = Offset(dashedStartX + dashedWidth, 0);
      // 始点から終点にかけて描画
      canvas.drawLine(startOffset, endOffset, paint);
      // 始点のX座標を更新
      dashedStartX = dashedStartX + dashedWidth + dashedSpace;
    }
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return false;
  }
}

CustomPainterを使用して破線単体の始点から終点までをcolorで描画する、というのを画面いっぱいはみ出すまで繰り返すような形です。

こちらもサンプルの画面に適用して切り取り線ぽくしてみると下記のような形になります。

まとめ

今回は破線を実装方法をまとめてみました。

あまりCustomPainterを使う機会がなかったので勉強になりました!

参考

stackoverflow.com

サンプルリポジトリ

github.com