Closed6
Flutterで系統図を作成し表示する
きっかけ
QCで出てくる系統図をFlutterの中で使いたいが、dart packageで調べても系統図を作成できるライブラリがぱっと見つからない。
自作するためにスクラップで確認します。
環境構築
まずはアプリを初期化します。
系統図の英語はsystem_diagram
なのかな?
flutter create system_diagram
cd system_diagram
図を作成
作図用のツールとしてはCustomPainterが一番に思い立ったので実験してみます。
目標は以下のような感じ。
Flutterで実装。
class _TestPainter extends CustomPainter {
void paint(Canvas canvas, Size size) {
/// paintの定義
final paint = Paint();
/// Firstの箱
paint.color = Colors.green;
canvas.drawRect(const Rect.fromLTWH(0, 0, 40, 100), paint);
/// FirstとSecondの間の線
paint.strokeWidth = 3;
paint.color = Colors.grey;
canvas.drawLine(const Offset(40, 50), const Offset(150, 50), paint);
/// Secondの箱
paint.color = Colors.green;
canvas.drawRect(const Rect.fromLTWH(150, 0, 40, 100), paint);
/// FirstとThirdの間の線
paint.color = Colors.grey;
canvas.drawLine(const Offset(40, 50), const Offset(150, 170), paint);
/// Thirdの箱
paint.color = Colors.green;
canvas.drawRect(const Rect.fromLTWH(150, 120, 40, 100), paint);
}
/// Repaintはfalse
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return false;
}
}
出来たことはできました。
ただし、以下の課題があります。
- サイズや位置の指定が手間
- 文字が表示できない
- ボタンの表示が出来ない
うーん。他の方法を考えます。
参考サイト
ここまでのコード全文
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return MaterialApp(
title: '系統図テスト',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: '系統図テスト'),
debugShowCheckedModeBanner: false,
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: ListView(
/// 水平方向に伸びるListview
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.all(10),
children: <Widget>[
/// Container系のWidgetでCustomPaintをラップする
SizedBox(
width: 400,
height: 400,
child: CustomPaint(
painter: _TestPainter(),
),
),
],
),
);
}
}
class _TestPainter extends CustomPainter {
void paint(Canvas canvas, Size size) {
/// paintの定義
final paint = Paint();
/// Firstの箱
paint.color = Colors.green;
canvas.drawRect(const Rect.fromLTWH(0, 0, 40, 100), paint);
/// FirstとSecondの間の線
paint.strokeWidth = 3;
paint.color = Colors.grey;
canvas.drawLine(const Offset(40, 50), const Offset(150, 50), paint);
/// Secondの箱
paint.color = Colors.green;
canvas.drawRect(const Rect.fromLTWH(150, 0, 40, 100), paint);
/// FirstとThirdの間の線
paint.color = Colors.grey;
canvas.drawLine(const Offset(40, 50), const Offset(150, 170), paint);
/// Thirdの箱
paint.color = Colors.green;
canvas.drawRect(const Rect.fromLTWH(150, 120, 40, 100), paint);
}
/// Repaintはfalse
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return false;
}
}
graphviewを使ってみた
Dart Packagesにgraphviewというのを見つけたので、使ってみます。
なお、作者はVerified Publisherではないです。
Githubのページにexampleがあったので、そのまま動かしてみます。
いくつかエラーが出ていたので、修正して実行。
Tree View(BuchheimWalker)
を選択。
いい感じですね。
ソースコードを見るとWidgetを指定する場所もあったので、おそらくElevatedButtonなども配置できるのではないでしょうか?
結論 : 簡単な系統図ならgraphview
で十分
WidgetとCustomPaintを組み合わせる
簡単な系統図ならgraphviewでも十分ですが、オリジナルで作成したいとき、WidgetとCustomPaintを組み合わせる方法もあります。
Widget
body: InteractiveViewer(
boundaryMargin: const EdgeInsets.all(10),
child: Center(
child: Column(
children: <Widget>[
/// 1番目のボタン
ElevatedButton(
onPressed: () {},
child: const Text('first'),
),
/// 線
SizedBox(
width: screenSize.width,
height: 50,
child: CustomPaint(
painter: _LinePainter(),
// painter: _TestPainter(),
),
),
/// 2番目と3番目
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
/// 2番目のボタン
ElevatedButton(
onPressed: () {},
child: const Text('second'),
),
/// 3番目のボタン
ElevatedButton(
onPressed: () {},
child: const Text('third'),
),
],
),
],
),
),
),
CustomPainter
class _LinePainter extends CustomPainter {
void paint(Canvas canvas, Size size) {
/// paintの定義
final paint = Paint()
..color = Colors.grey
..strokeWidth = 2;
/// Lineを描く
canvas.drawLine(
Offset(size.width / 2, 0), Offset(size.width / 3, size.height), paint);
canvas.drawLine(Offset(size.width / 2, 0),
Offset(size.width * 2 / 3, size.height), paint);
}
/// Repaintはfalse
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return false;
}
}
画面はこんな感じ。
個人的にはカスタマイズしやすいので、いいなあと思ってます。
ただし、数が増えると複雑なアルゴリズムになります。
このスクラップは2022/06/17にクローズされました