🐥
【Flutter】CustomPainterで作るオリジナルBreadcrumb
はじめに
この記事はFlutterで CustomPainter を使った breadcrumb
を実装した時のメモになります。
CustomPainterを使用することで、通常のWidgetでは難しいカスタム描画を行えます。
ゴール
以下の様な breadcrumb
を描画する事をゴールとして進めてみたいと思います。
Widgetを駆使してやってもできるかもしれませんが、今回はあくまで CustomPainter を使用して描画させる実装で進めていきます。
環境構築や準備
各バージョンや環境
MBA: M3 24GB macOS 14.6.1
Flutter: 3.24.3
VSCode: 1.94.0
実装
1. まずは単純な円を描画する
StatelessWidget
クラスを継承したCustomPainterSample
クラスを作成
1-1. 土台として この時点ではCustomPaintにpainterを指定せず、デフォルトの描画を確認します。
import 'package:flutter/material.dart';
class CustomPainterSample extends StatelessWidget {
const CustomPainterSample({super.key});
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Custom Painter Sample')),
body: SizedBox(
width: double.infinity,
height: 120,
child: Container(
color: Colors.amber,
padding: const EdgeInsets.all(5),
child: CustomPaint(
child: Container(
color: Colors.red,
),
),
),
),
);
}
}
CustomPaintの Container
がどんな風に描画されるのか分かりやすくする為に色を指定しました。
CustomPainter
を継承したPainterクラスを作成
1-2. import 'package:flutter/material.dart';
class CustomPainterSample extends StatelessWidget {
const CustomPainterSample({super.key});
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Custom Painter Sample')),
body: SizedBox(
width: double.infinity,
height: 120,
child: Container(
color: Colors.amber,
padding: const EdgeInsets.all(5),
child: CustomPaint(
painter: CirclePainter(), // 追加!
child: Container(
color: Colors.red.withOpacity(0.5), // 分かりやすく半透明にしました
),
),
),
),
);
}
}
class CirclePainter extends CustomPainter {
void paint(Canvas canvas, Size size) {
final Paint paint = Paint();
// 半径10の円を描画
paint.color = Colors.green;
canvas.drawCircle(Offset(size.width / 2, size.height / 2), 10, paint);
}
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
実行結果は以下の様になりました。
CustomPaint
の child
に指定したWidgetが一番手前に来るみたいです。
また pain
メソッドで渡ってくる size
はCustomPaint(↑だと薄い赤色部分)のサイズが渡ってきます。
2. 均一に円を並べる
最後に breadcrumb
っぽくなるように直線の上に、今回は固定数の円を並べる様な処理を実装してみたいと思います。
import 'package:flutter/material.dart';
class CustomPainterSample extends StatelessWidget {
const CustomPainterSample({super.key});
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Custom Painter Sample')),
body: SizedBox(
width: double.infinity,
height: 120,
child: Container(
color: Colors.amber,
padding: const EdgeInsets.all(5),
child: CustomPaint(
painter: CirclePainter(), // 追加!
child: Container(
color: Colors.red.withOpacity(0.5), // 分かりやすく半透明にしました
),
),
),
),
);
}
}
class CirclePainter extends CustomPainter {
final double radius = 10;
final double strokeWidth = 3;
void paint(Canvas canvas, Size size) {
final Paint paint = Paint();
paint.color = Colors.blue;
final double w = (size.width - (radius * 2)) / 6.0;
paint.strokeWidth = strokeWidth;
canvas.drawLine(
Offset(0, size.height / 2), Offset(size.width, size.height / 2), paint);
for (int i = 0; i < 7; i++) {
drawOutlineCircle(canvas, Offset(w * i + radius, size.height / 2));
}
}
// 円(塗りつぶし)
void drawCircle(Canvas canvas, Offset c) {
final Paint paint = Paint();
paint.color = Colors.blue;
canvas.drawCircle(c, radius, paint);
}
// 円(外線)
void drawOutlineCircle(Canvas canvas, Offset c) {
final Paint line = Paint()
..color = Colors.blue
..strokeCap = StrokeCap.round
..style = PaintingStyle.stroke
..strokeWidth = strokeWidth;
canvas.drawCircle(c, radius, line);
final Paint paint = Paint();
paint.color = Colors.white;
canvas.drawCircle(c, radius - (strokeWidth / 2), paint);
}
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
実行結果は以下の様になりました。
中央の水平線に沿って7つの円が均等に並んでいるのが確認できます。
CustomPaintの Container
を外すと👇の様になります。
まとめ
あまり無いとは思いますが、CustomPainterを使って breadcrumb
っぽいものを作る時の参考に少しでもなればと思います。
Discussion