📖

flutter フロアマップにピンを立ててStackについて理解する

2021/06/27に公開

はじめに

フロアマップにピンを立ててピンチイン・アウトができるアプリを作る。

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;
          });
        },
      );
}

その他、細かいところでスケールによってピン画像のサイズを調整したり。最初の表示ではピンが画面の中心になるように位置を調整したり。

完成版
https://github.com/nrikiji/flutter_floor_map/

Discussion