💻

[Flutter] プレビュー写真をタップするとファイルチューザーが出てきて画像を差し替えられるUIの実装例

2020/08/10に公開

よくあるプロフィール設定画面のような感じで、

  • プロフィール画像をタップしたらファイルチューザーが出てくる
  • ファイルチューザーでローカルの画像ファイルを選択するとプレビューが書き換えられる
  • その後保存ボタンを押すと新しい画像がサーバーに送信される

といったUIのFlutterでの実装例をお伝えしたいと思います。

動作結果のイメージはこんな感じです。

前振りもへったくれもなく結論だけ書くと、以下のようなコードで実現できます。

class Profile extends StatefulWidget {
  
  createState() => _ProfileState();
}

class _ProfileState extends State<Profile> {
  File _imageFile;

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('プロフィール画像設定'),
      ),
      body: Builder(
        builder: (BuildContext context) {
          return ListView(
            children: [
              Container(
                margin: EdgeInsets.only(top: 16.0),
                child: GestureDetector(
                  child: Center(
                    child: Container(
                      width: 150,
                      height: 150,
                      child: _imageFile != null
                          ? Image.file(
                              _imageFile,
                              fit: BoxFit.cover,
                            )
                          : Image.network(
                              'https://via.placeholder.com/150',
                              fit: BoxFit.cover,
                            ),
                    ),
                  ),
                  onTap: () async {
                    setState(() => _imageFile = await FilePicker.getFile(type: FileType.image));
                  },
                ),
              ),
              Container(
                margin: EdgeInsets.only(top: 10.0),
                child: Center(
                  child: RaisedButton(
                    child: Text('保存'),
                    onPressed: () {
                      // do something, and
                      Scaffold.of(context).showSnackBar(SnackBar(
                        content: Text('プロフィール画像を変更しました'),
                      ));
                    },
                  ),
                ),
              ),
            ],
          );
        },
      ),
    );
  }
}

コードを読んで「あーハイハイ」となった方はもう卒業で大丈夫です笑

ちょっとだけ説明を書くと、

File _imageFile;

にプレビュー中の画像ファイルの File オブジェクトを持つようにして、そのデータの有無に応じて

_imageFile != null
  ? Image.file(
      _imageFile,
      fit: BoxFit.cover,
    )
  : Image.network(
      'https://via.placeholder.com/150',
      fit: BoxFit.cover,
    ),

という具合で、プレビュー中の画像ファイルを表示するかダミー画像を表示するかを切り替えています。

そして、画像を GestureDetector でラップして onTap を生やし、そこでファイルの選択をさせています。

onTap: () async {
  setState(() => _imageFile = await FilePicker.getFile(type: FileType.image));
},

ファイルチューザーを開いてローカルのファイルを取得する処理には file_picker を使っています。

あとは ContainerCenter でラップしてサイズを 150x150 に固定しているのがポイントです。

Center() でラップしないと

こんな感じになってしまいます。

さらに Container でラップしないと

こんな感じで拡大されてしまいます。

この辺りのレイアウトの調整はFlutterの鬼門の一つですね。(個人的には)

あとそもそも SnackBar を使うために Builder でラップして context を変えている点については

[Flutter] SnackBarを使おうとすると Scaffold.of() called with(略)と言われるときの対応

をご参照ください。

GitHubで編集を提案

Discussion