62
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

Flutter でよく使う(と思う)コードまとめ

最近 Flutter アプリをいくつか仕事で作っていて、よく使う Widget や API がまとまってきたので書いてみます。

調べればすぐ出てくるような内容ではありますが、「これから Flutter アプリ作ってみたいけど何をどう使ったら何ができるんだろう」、とフワフワしている方へ、「こんな時これが使えるよ」をいくつかまとめて紹介できればと思います。

このページをとっかかりとして、載せているリンク先で理解を深めつつ使ってみる、という流れを想定しています。(くれぐれもこのページの内容をそのままコピペして満足しないように!)

なお、全て package:flutter/material.dart を使っている場合の話ですので、 widgetscupertino では話が違う場合もあるかもしれません。

では、どうぞ。

画面遷移

次の画面に遷移する

Navigator.push(context, MaterialPageRoute(builder: (context) => NextPage()));

前の画面に戻る、ダイアログを閉じる

Navigator.pop();

次の画面に遷移し、元の画面は終了する(バックキーで戻れなくする)

Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) => NextPage()));

詳しくは以下のドキュメントを参照してください。

Navigate to a new screen and back - Flutter

リスト操作系

Flutter というよりは Dart の内容ですが、 RowColumnchildren を組み立てる時に良く使うので、書いておきます。

データの配列から Widget の配列を作る


["Azure", "AWS", "GCP"].map((cloudName) => Text(cloudName)).toList();
// -> [Text("Azure"), Text("AWS"), Text("GCP")]

もしくは


["Azure", "AWS", "GCP"].map((cloudName) { return Text(cloudName); }).toList();
// -> [Text("Azure"), Text("AWS"), Text("GCP")]

↑ を Columnchildren なんかで使う場合、作った3つの Text 以外にも children に含めたい場合がありますが、
Dart には Ruby でいう flatten がないため、カスケード呼び出しを使って以下のように書いています。(他にもっと良い書き方があったら教えてください)

Column(
    children: [
        SomeWidget(),
        OtherWidget(),
    ]..addAll(
        ["Azure", "AWS", "GCP"].map((cloudName) { return Text(cloudName); }).toList();
    )
)

絞り込み

["Azure", "AWS", "GCP"].where((cloudName) => cloudName.startsWith("A"));
// -> ["Azure", "AWS"]

Dart のリスト操作は以下のブログが参考になりました。

Top 10 Array utility methods you should know (Dart) - Jermaine Oppong

イベント

Widget にクリックイベントをつける(Ripple あり)

Widget のタップ時に Ripple エフェクトをつけるには、 InkWell が便利です。

InkWell(
    onTap: () {
        // 何か処理
    },
    child: SomeWidget()
}

Android ネイティブの Ripple エフェクトの面倒臭さを考えると信じられない手軽さですね。

ただ onTap:(){} で処理を書いてしまうのはもしかしたら Flutter 的ではないかもしれないので、以下のように書くと画面と処理を分離できて良いと思います。

InkWell(
    onTap: _doSomething,
    child: SomeWidget()
}

注意点として、 InkWell の親 Widget に背景色がついてしまっていると Ripple してくれないようなので、 InkWellMaterial で包み、 type: MaterialType.transparency をつけてあげる必要があったりします。

Container(
    color: Colors.blue.shade50,
    child: Material(
        type: MaterialType.transparency,
        child: InkWell(
            child: Text("テキストテキスト"),
            onTap: () {},
        ),
    ),
),

と書いて改めて調べてみたら、他にもいくつか方法はありそうですので、参考になりそうなリンクだけ貼っておきます。

InkWell not showing ripple effect - stackoverflow

Widget にクリックイベントをつける(Ripple なし)

GestureDetector(
    onTap: _doSomething,
    child: SomeWidget()
}

GestureDetector には onForcePressStartonPanStart など、 InkWell にはないイベントに反応するためのパラメータが用意されているため、より細かくイベントをとりわけたい場合はこちらを使います。

前のページに戻るイベントをキャッチする

ナビゲーションバーの戻るアイコンタップや、 Android のバックキー押下による「前のページに戻る」イベントをキャッチしたい場合、 WillPopScopeScaffold を囲ってあげます。


WillPopScope(
    onWillPop: () async {
        showDialog(
            context: context,
            builder: (buildContext) {
                return SimpleMessageDialog( // これはメッセージを出すための自作クラス(詳細は割愛)
                    message: "前の画面に戻りますか?",
                    onPositiveTapped: () { Navigator.pop(context); },
                );
            },
        );
        return false; // return false することで自動的に戻る処理を無効にする
    },
    child: Scaffold(
        body: BodyWidget(),
    ),
),

↑の例のように、戻る前に確認ダイアログを入れたい、データを保存しておきたい、などの場合、これが使えます。

アニメーション

AnimatedContainer

幅や高さ、色、その他装飾系の値を段階的に変化させてアニメーションさせたい場合、 AnimatedContainer が使えます。

AnimatedContainer(
    height: _showAll ? 300 : 60,
    duration: Duration(milliseconds: 100),
        child: Text("テキストを${_showAll ? '全部' : '一部'}表示しています"),
    ),
),

上の例の場合、通常の Container を使うと、 setState()_showAll の値を切り替えるたびに高さが一瞬で切り替わりますが、 AnimatedContainer を使うと Duration で指定した時間を使って少しずつ高さが変わります。(つまりアニメーションしている感じになります)

AnimationController

AnimatedContainerContainer に設定できる値の範囲でアニメーションが可能ですが(それでも十分だったりしますが)、より様々な Widget にアニメーションを適用したい場合は AnimationController が使えます。

var animationController = AnimationController(duration: const Duration(milliseconds: 100), vsync: this);
var animation = Tween<double>(begin: 0, end: 100).animate(_animationController)
    ..addListener(() {
        // アニメーション中、この関数が連続して呼ばれる
        setState(() {
            _iconSize = _animation.value; // _animation.value が 0 から 100 まで増える
        });
    });
animationController.forward();

これは Widget を直接変化させるのではなく、指定した値 (begin, end) の範囲が指定した時間 (duration) で変化し続けるので、その値を Widget の好きな場所に使ってあげる、というものです。

Widget そのものを変化させるのではなく、あくまで変化するのは数値のみで、それをどう使うかはこちら次第、というのが柔軟で分かりやすくて良いですね。

アニメーションについての詳細は以下の公式ドキュメントを参照してください。

Animations tutorial - Flutter

レイアウト

マージンを入れる

SomeWidget(),
SizedBox(height: 16),
OtherWidget(),

Flutter の Widget にはマージンを設定するパラメータが用意されていないため、「 Widget と Widget の間」を表したい場合は SizedBox を利用するのが便利です。( youtube の The Boring Flutter Development Show でちらっと使っていたのですが、ちょっとどの回か忘れてしまいました、、、)

「 Widget の内側の余白」を表したい場合は用意されている padding パラメータを使いましょう。

横幅いっぱいになったら折り返す

複数の Widget を横に並べていき、端まできたら改行するようなレイアウト(CSSでいう Flexbox のような)を作りたい場合は、 Row ではなく Wrap が使えます。


Wrap(
    spacing: 8.0, // 横のマージン
    runSpacing: 8.0,  // 縦のマージン
    children: <Widget>[
        Text("#タピオカ"),
        Text("#今日もタピオカ"),
        Text("#映える"),
        Text("#渋谷で"),
        Text("#大切な仲間と"),
        Text("#タピオカ"),
        Text("#タピオカ"),
    ]
)

通信系

HTTP POST


var response = await http.post(
    "https://www.example.com/path/to/api",
    body: {
        "nickname": "chooyan",
        "description": "A freelance mobile app developer",
    },
);

http プラグインを入れる必要があるので pubspec.yaml に書いておく。

multipart/form-data による画像アップロード

画像に限らず、ファイルアップロードには http.MultipartRequest が使えます。

var uri = Uri.parse("https://www.example.com/path/to/upload/api");

var request = http.MultipartRequest("POST", uri);
request.fields["id"] = loginId;

var stream = http.ByteStream.fromBytes(imageData);
var multipartFile = http.MultipartFile("image", stream, imageData.length, contentType: MediaType("multipart", "form-data"), filename: "uploadfile.png");
request.files.add(multipartFile);

var response = await request.send();

Widget 系

スクロールできるようにする

画面からはみ出るくらい長い Widget をスクロールできるようにしたい場合、 SingleChildScrollView で囲ってあげるのが一番カンタンです。


SingleChildScrollView(
    child: Column(
        children: <Widget>[
            Some(),
            Long(),
            Contents(),
        ]
    )
)

たまに itemCount が 1 の ListView を使っている例を見かけますが、本来の使い方とは違うためこちらを使うのが良いかと思います。

丸画像


Container(
    width: 120,  
    height: 120,
    decoration: BoxDecoration(
        shape: BoxShape.circle,
        image: DecorationImage(
            fit: BoxFit.fill,
            image: FileImage(imageFile), // 場合に応じて NetworkImage などに変更
        ),
    ),
);

まだ開発してないけどここに何か入るよ

開発前だけど、枠だけ決まっている場合は Placeholder が便利です。


Container(
    height: 300,
    child: Placeholder(),
)

枠を確保できるだけでなく、一目で「開発中」であることが分かるようになったり、「あ、デザインには書いてあるこの Widget 入れ忘れてるじゃん」な事故がなくなったりします。

よく見る資料

The Boring Flutter Development Show

Google の開発者がノーカットで Flutter でアプリを作る様を眺める動画です。
若干英語が分からない感はあるものの、コードを見ているだけでも「あ、こんなのあるんだ、調べてみよう」が出てくるので、休憩がてら見てみても効果があると思います。

ちゃんと聞いているとそれはそれで「なるほど、こんなこと考えながらコード書いていくのか」を観察できるので、そちらもオススメです。

Flutter 公式サイト

なんのひねりもなく公式サイトです。 Flutter はかなりドキュメントが整っていて、且つ読みやすく書かれていますので、ここを読み物として読んでいるだけでだいぶいろいろ勉強になります。チュートリアルや API リファレンスもこの中です。

ただし日本語訳が皆無なので、若干の英語に対する耐性は必要です。

A curated list of samples

実際に Flutter で作られたアプリのソースコードがまとまっています。

中途半端なアプリもありますが、それでも lib 下のフォルダ構成をどう作るか、どんな Widget をどんなところで使っているのか、など参考になる部分は多々あります。


以上、他にも何かあれば随時追記します。
みなさまからもコメントで「こんなのよく使うよ」を教えていただけるとありがたいです。


宣伝

フリーランスでいただいた仕事として、先日 AppBox というアプリを Flutter で開発してリリースしました。

Android
https://play.google.com/store/apps/details?id=jp.co.oakhouse.appbox&hl=ja

iOS
https://apps.apple.com/us/app/oak-appbox/id1477787005?l=ja

オークハウスが管理するシェアハウスの住人向けのアプリなので利用シーンは限られますが、 Flutter でこんなのサクッと作れるよ!というイメージとして活用していただけたら嬉しいです。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
62
Help us understand the problem. What are the problem?