Flutterでの開発をスムーズに行うためのTips集

mono 
mono 
Dec 31, 2018 · 26 min read

以下のドキュメントにかぶる内容も含まれていますが、僕が良いなと思っているやり方を共有します。

本記事の内容は、ある程度書いていたり色々記事を読んでいると自然と身についていく類のものですが、初めから色々知っていると捗るのと、すでに慣れている場合でも少しは新しい発見があるかもしれません。

また、Flutterの効率良い学び方 にも書いた通り、自分でFlutterコードを書く分にはAndroid Studioの方が捗ると感じているためそれに沿った説明になっていますが、VS Codeでも大体通ずる内容です。また、キーボードショートカットはmacOSでのデフォルトとなっているので、環境が違う場合は随時読み替えてください。

[追記 2019/03/03] 最近はVS Codeに絞るのも良いかなと思ってきています

新規アプリ作成

基本はその通りで良いですが、ターミナル経由の場合、 flutter create app_name だけだと以下のようなデフォルト値が設定されてしまい、あとから好みのものに変えるのがけっこうな手間です。

  • 所属(アプリIDの前半部分に利用される): com.example
  • flutter_driver (UIテスト): 無し

そのため、次のように自分の好みのものをメモしたりコマンドヒストリーから呼び出せるようにしておくと便利です。

flutter create \
--org com.mono0926 \
--with-driver-test \
app_name

ちなみに、この設定は以下のセッション動画でオススメされていたものです。

ただ、個人的にはAndroid Studioの”New Flutter Project”でポチポチ選択していくのが好みです(しかしこの場合flutter_driver指定はできなさそう🤔)。

“New Flutter Project”

VS Codeの場合は、次のようにDartの拡張の設定で指定できます。

VS CodeのDartの拡張の設定

ネイティブアプリ側の言語設定は、現在では以下がデフォルトです(以前は逆でしたが変わりました)。

  • iOS: Swift (not Objective-C)
  • Android: Kotlin(not Java)

大抵の場合この選択のままの方が良いですが、どうしても変えたい時があればプロジェクト作成時に注意して選択しましょう。

特に、ネイティブ言語の種類は、後から変えたい場合、作り直すかそれに準じた作業(設定に応じて生じる差分を手動で適用など)が必要になるので、少し面倒なことになります。

また、個人的には最近はさらに工夫してプロジェクト生成後にいつも行う調整をスクリプトで適用する仕組みを整えることもしていて、ちょっとしたプロジェクトを作る機会が多い場合は捗るようになります。

プロジェクトのルートに analysis_options.yaml を追加

dartfmt を活用

To automatically format the code in the current source code window, right-click in the code window and select Reformat Code with dartfmt. You can add a keyboard shortcut to this in Keymap section of IntelliJ Preferences.

ただAndroid Studioでdartfmtをかける方法として、右クリックして”Reformat Code with dartfmt”を選ぶかoption + command + Lのショートカットで実行するやり方が紹介されていて、いまいちに感じます。

右クリックしてdartfmt

一々その操作をするのではなく、セーブすると自動的にdartfmtをかけてくれる以下の設定をする方が遥かに快適です。サブメニューの”Organize imports on save”は、セーブ時にimportの並び順整理および不使用なものの削除をしてくれて、こちらはオンにするか好みに依ると思います(基本的には便利なものの、一時的にコメントアウトしたものに対応するimport分がカットされて面倒なこともたまにあるので)。

別解として、Save Actionsというプラグイン を使う方法もありますが、基本的には追加のプラグイン無しで使える上の方法で充分だと思います。

また、フォーマットのかかっていない既存プロジェクトに対して、一斉に実行したい場合は以下のコマンドでできます。
( -w 無しでdryrunになりますがgit管理などしていれば特に怖がらずに -w 付きで実行してしまった問題ないです)

dartfmt -w lib

Dartの末尾コンマは大事

コンマの入れ具合については、Flutter/Dart コーディング スタイル の内容くらいがバランス良い気がしています。

Alt + EnterによるAssist・Quick Fix活用

Alt + Enter

Flutterコードを書くにあたって多用するのが、Widgetでラップおよび逆にWidgetを削除する機能です。これを知らないと特にネストの深いWidgetの編集の際に無駄な苦労を強いられます。

Widgetでラップおよび逆にWidgetを削除

同様のことは右にあるFlutter Outlineからの右クリックでも可能です。

Flutter Outlineからの右クリック

Flutter Performance タブの活用

Performance タブ

以下の記事を書く際にも多用しました。

しかし、たまに表示がおかしいことがあるので、フルリロードしたり、それでも直らない時はIDEを再起動したりしています🤔

UIのプレビュー機能は無いの?

きちんと動くようになると便利なので期待しています。次のIssueを購読しておくと、動きがあると分かります。

2019年9月に、Flutter開発チーム内部でプロトタイプとして開発中のHotUIという機能についての文書が公開され、将来これに近い形のものがIDEに統合されると予想されるので、それがとても楽しみです。

以下の機能などが検討されているようで、ここに関しては SwiftUI に対して明確に劣っている部分で不満だったので、対応されると嬉しいです。

  • Widget全体およびその中の一部分のプレビュー表示
  • プレビューUIからのコード変更操作

初回実装時は、シミュレーターでのレイアウト結果をHot Reloadで確認しながら実装すれば良いだけなのでプレビューが欲しいと思うことはあまりないですが、後から各種ソースコードがどういうUIを構築しているのか確認したいときなどにプレビューがあるととても捗るはずです。

常用したいパッケージ

quiver

例えば、0から順に10個の要素が欲しい時(いわゆるrange関数)に、次のようなコードを書く必要があります。

List<int>.generate(10, (i) => i)

quiverをインストールしておけば、次のようにシンプルに書けて良いです。

import 'package:quiver/iterables.dart';
range(0, 10)

Flutterフレームワークではテストで使われているため dev_dependencies に指定されていてそのまま参照できますがが、アプリコードとして使いたい場合は別途アプリの pubspec.yamldependencies に指定が必要です。

JavaScriptにおけるLodash のようなものですが、Dart本体には最近のJavaScriptよりも便利なメソッドの不足を感じるのでより必要度が高い気がしています。

というわけで、Dartコード書いててちょっと不便だと思ったらquiverで簡単に書けないか疑うと良いと思います。主要な機能は README に列挙されています。

同様に、Dart本体には入ってないがパッケージで補填できるものとして、 tuple などがあります。

ちなみに、Dart本体改善について今後期待できそうなものは以下でよくまとめられています。

rxdart

ちょっとしたStreamの処理ならRxDart無くとも済みますが、BLoCパターンを使う場合は必須だと思っています。

DartPad ではRxDartを使えないので、そこでStream扱いたい時は標準のStreams APIで我慢しています( ´・‿・`)

intl

弊パッケージ

それぞれ、記事も書きました。

ドキュメント不足なものが多いですが、他にも色々公開しています。

他にも常用するレベルの便利なパッケージが多いですが、公式ドキュメントの Commonly Used Dart Libraries など見るのもおすすめです。

アノテーション活用

@override

@overrideに関する警告

@required

@requiredな引数欠損の警告

@immutable

まず、Widget基底クラスについているため、

Widgetのソース

final ではないフィールドが宣言されていると警告が出ます。

@immutableなのに非finalなフィールドが宣言されている時の警告

もちろん、自作クラスに付与して同じく不変であることを示せます。

ただ、 final は再代入不可であって、例えばListの中身が不変であることなどは保証できず、その場合は UnmodifiableListView などを使います。

しかし、これは変更操作をコンパイル時に防ぐものではなく、例えば次のような操作をすると、

final x = UnmodifiableListView([1]);
x.add(2);

次のような実行時エラーとなります。もう少し確実に防ぎたいものですが🤔

UnmodifiableListViewに変更操作した時の実行時エラー

その他、以下なども活用するとミス防止に役立ちます。

  • @mustCallSuper: 継承先で super.xxx のように override したメソッドを呼び忘れると警告表示
  • @protected: メソッドを、継承先での利用のみ許可して、外から呼ぶと警告表示
  • @visibleForTesting: そのパッケージの lib フォルダあるいはテスト用の test フォルダ以外から呼ぶと警告表示

Builder Widgetで入れ子のBuildContextを提供

SnackBarの表示例

次のように書くと普通に動くと思いきや、、、

次のようなエラーが出てしまいます🤯

SnackBarの扱いを間違えた時に出るエラー

エラーに臆さずによく読んでみると、次のように書かれています。

  • Scaffold.of()がScaffoldを含まないcontextで呼ばれている
  • Builderを使うのが最もシンプルな回避策
  • Widget分割するのが(パフォーマンス観点で)効率的
  • GlobalKeyをScaffoldに入れて、currentStateプロパティでScaffoldStateを取得する

このエラーメッセージの言う通り、確かにcontextはHomeに渡ってきているものであり、Scaffold配下のものではありません。

(このようにFlutterでは実行時エラーが出た時に原因と解決策など含めた丁寧なエラーが吐かれるので、適当にググって場当たり的な対処をする前にまずはよく読んで考えましょう。)

Widget分割

次のように右クリックのRefactorから選んでも良いですが、

右クリックからのWidget分割

command + shift + Aのクイックアクションから選ぶ方が効率的です。

クイックアクションからのWidget分割

分割すると次のようになって、SnackBarも無事に表示できます。ついでにScaffold配下をconstにできて、パフォーマンス的にもベターになりました。

Builder Widget

各種Widgetでもchildではなくbuilder引数が提供されているものがありますが、それらは利用者がBuilderを別途使わずに済むような配慮と言えます。

そもそもBuildContextとは?

左: Widgetのbuildメソッド | 右: 呼び出し元のElementがbuildメソッドに自身を渡している
Element の継承関係

つまりBuildContextからその上位のElementツリー構造を辿れます。元々のエラーは、そこから必要なWidget(この例の場合Scaffold)を辿れないが故に発生しており、そのWidget配下のBuildContext(Element)にアクセスする手段として以下のいずれかを行なった、と考えると分かりやすいです。

  • 別Widgetに切り出す
  • Builder Widgetを経由する

より詳しくは、Flutter の Widget ツリーの裏側で起こっていること を参照してください。

PlaceholderでUIのTODO的な部分を示す

Placeholderの利用例

コード:

Containerなどで済ませてしまうこともできますが、意図的にも見た目的にも作業途中であることが明確になるので、Placeholderを活用した方が良いです。

FlutterのWidgetは、他にもこんなのまで用意されているのかと驚くことがちょくちょくありますが、Flutter Widget of the Weekのプレイリスト などしっかり目を通したりすると追いやすいです。

キーボードショートカットの覚え方

まず、IDEのHelpメニューを見る(command + shift + / でも表示可能)と、次のようにテキスト入力欄がありそこにテキストを入力するとやりたい操作を検索できます。
(上述のcommand + shift + Aのクイックアクションのショートカットもここに記載されていますね。)

ヘルプメニュー
ヘルプメニュー経由でのコマンド検索

このようにやりたいコマンドを選びつつ頻度の高いものはキーボードショートカットも覚えてすぐ実行できるようにしています。

なので、まずは以下程度の最小限のものだけ覚えて、触りながら必要に応じて順次覚えていけば良いと思いますし、頻度の低いものはQuick Action経由などで済ませてしまって十分だと思っています。

command + shift + O: ファイル検索

shift2連打: 全検索

command + N: ひな形生成

チートシート

基本的にDebug実行で良い気がする

そのため、Runで実行中にブレークポイントを止めたくなったら、Debugで再起動するハメになります。

次のように、あえてDebugではなくRunにする理由が無いように思うので、常時Debugで良い気がしています🤔

“Run”と”Debug”の違い

Xcodeなどではこれら区別されておらず、Runでブレークポイントも止まって、Flutterも分けずで良い気がしていますが、なぜ分けているのでしょう🤔
(「デフォルトデバッグ実行で、オプションとしてデバッガーをアタッチしないモードもある」くらいが使いやすい気がします。)

XcodeのRunコマンド

紛らわしいですが、上述の”Run”・”Debug”はモードとしてはともに”Debug Mode”であり、モードは他に以下もあって、これらはそれぞれ適した場面で使う必要があります。

  • Profile Mode: パフォーマンスチューニングの際などにプロファイリングする時用
  • Release Mode: リリースビルド用(JITではなくAOTになってHot Reloadが効かなくなる代わりに速くなる)
Profile ModeとRelease Mode

パフォーマンスが落ちるなど実際にリリースする状態とは異なるため、通常開発する際の”Debug Mode”時は次のように右上にDEBUGバナーが表示されます。

MaterialApp WidgetのdebugShowCheckedModeBannerfalse にすることで、Debug Modeでも非表示にすることもできますが、区別が付かなくなってミスの原因になりかねないので、通常デフォルトの true とした方が良いと思います(スクショの見栄えを良くしたい時など一時的にfalseにする程度にとどめるのが良いと思います)。

ビルドのモードについて、詳しくは以下に載っています:

Live Templates活用

Live Templates

例えば、StatefulWidgetのひな形を書きたい場合、 stful と入力(途中まで打って候補から選択でもOK)してタブかエンターを押すと次のように一瞬でStatefulWidgetのコードが出力されて、そのまま最低限必要なところのみ追加で入力します。

Live Templates活用例

自作することもできるので、ボイラープレート的なコードを書くことが何回かあったら既存のテンプレートを参考にしながらその都度登録すると良いです。

自作したテンプレートは以下に保存されるので、チームメンバー間や自分マシン間などで共有すると便利です。

~/Library/Preferences/AndroidStudio3.5/templates

source_genbuild_runner によるコード生成もあり

詳しくは次の記事を見てください。


ちょっと雑多な感じになりましたが、ざっとTipsをまとめてみました。まだ書き忘れていることがありそうで、気付き次第追記していきます🐶

本記事は具体的なTips集でしたが、Flutterを体系的に学ぶおすすめの流れは以下ご覧ください。

Flutter 🇯🇵

Flutterに関する日本語記事を書いていきます🇯🇵

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store