📖
flutter フロアマップにピンを立ててStackについて理解する
はじめに
フロアマップにピンを立ててピンチイン・アウトができるアプリを作る。
Stackは左上からの位置を指定してWidgetを配置することができる。マップの画像表示にはphoto_viewを使用する。ピンチイン・アウトはphoto_viewが計らってくれる。
完成イメージ
実装
方針
マップとピンの画像をStackに配置する
Stack(
children: [
// マップ画像
PhotoView(
image: Image.asset("images/map.png").image,
・・・
),
// ピン画像
Positioned(
child: Image.asset("images/marker.png"),
・・・
),
],
);
下の図の黄色い部分がStackで画面に見える部分。マップを移動させるとこの部分がずれるイメージ。Stackの左上が(0,0)となるので図2の場合、ピンのY座標がマイナス値となって画面から見えなくなる。
Stackのどこにピンを置くかを求めてこの値をピンに随時反映する
各画像のサイズを整理
各画像や画面のサイズは以下。各画像のサイズは画面上のサイズ。この条件で画面の左上ピッタリにピンを立てる。
- Stackのサイズ = 400 x 800
- ピン画像のサイズ = 50 x 50
- マップ画像のサイズ = 400 x 1000
また、上の図ではマップ画像とStackの中心が一致してるので、Stackに収まらない部分が上下100ずつということがわかる。
ピンの画像サイズが50x50なので、マップ画像を基準にするとピンの位置は(25,50)でぴったり左上になる。マップが拡大縮小されたとき・表示している位置が変わったときに、Stackを基準としたマーカーの位置を求めて随時反映する。
Stack(
children: [
// マップ画像
PhotoView(
・・・
),
// ピン画像
Positioned(
// Stackの左上からの位置。この値を随時求めて反映する
left: pinLeft,
top: pinTop,
・・・
),
],
);
ピンの位置を求める
上でStackに収まらない部分の長さが100とわかってるので、ピンのY座標が以下の式で計算できる。
マップの表示位置(中心からずれたY軸の長さ) - 収まらない高さ + ピンのY座標 - ピンの高さ
PhotoViewにPhotoViewControllerをセットして、画像の位置や拡大・縮小値の変化を監視してマーカーの位置を随時更新する
Stack(
children: [
PhotoView(
controller: photoController,
・・・
),
Positioned(
left: pinLeft,
top: pinTop,
・・・
),
],
);
・・・
void initState() {
photoController = PhotoViewController()
..outputStateStream.listen(
(event) {
・・・
setState(() {
// event.positionから画像の中心からのズレが取得できる
// overW、overH = はみ出した長さ
// pinX、pinY = マップ画像を基準にしたピンの位置
// pinW、pinH = ピン画像のサイズ
// ピンの位置を更新
pinLeft = event.position.dx - overW + pinX - pinW / 2;
pinTop = event.position.dy - overH + pinY - pinH;
});
},
);
}
その他、細かいところでスケールによってピン画像のサイズを調整したり。最初の表示ではピンが画面の中心になるように位置を調整したり。
完成版
Discussion