概要
どうも、@daiki1003です!先日、ある程度大きくなった自分のFlutterプロジェクトにlintを入れてみました。
悪い予感はしてましたが、まぁ出てくるわ出てくるわ笑
警告5,000みたいな感じでした。
ただ、僕自身こう言うのをちまちま直していく作業は好きだったりするので
苦もなく全部倒してやりました。
さて、この記事では
「Flutterのプロジェクトでlintを入れたいけどどうしたらいいかわからない…」
「lintを入れると、何を変えさせられるの?」
そんな疑問にお答えしたいと思います。
それでは早速行ってみましょー!
Flutterのlintって何を採用すれば良いの?
公式ではこちらのページに全て載っています。
pedantic_monoの導入
が、今回はmonoさんが下記パッケージを公開してくれていたので、ありがたく使わせていただきました。
どうやら結構厳し目なlintみたいです。
パッケージのインストール方法は下記記事で解説しています。
analysis_options.yamlの設定
analysis_options.yaml
を作成し、以下の様に1行追加します。
include: package:pedantic_mono/analysis_options.yaml
これだけで、静的解析が始まり警告がわんさか表示されるはずです。
Flutterプロジェクトにlintを入れた際の変更内容を一挙公開!
さて、導入も無事できたところで、修正内容を公開したいと思います。
エラー
こちらは無視して実行することができません。
Missing arguments type for …
BAD
onPressed: () { Navigator.of(context).push( ... ); }
GOOD
onPressed: () { Naviagator.of(context).push<void>( ... ); }
ジェネリックメソッドを呼び出す際は型を明示的に指定してね、というエラーです。
Missing parameter type for map literal.
BAD
Map hoge = {};
GOOD
Map hoge = <String, dynamic>{};
BETTER
final hoge = <String, dynamic>{};
Map
の型が明示的に指定されていないと怒られます。
後述しますがそもそも変数を型名で受け取るのは基本的に良くないとされているので
betterとして用意しました。
Missing parameter type for …
BAD
Future.delayed(...);
GOOD
Future<void>.delayed(...);
The argument type ‘dynamic’ can’t be assigned to the parameter type ‘void Function()’
BAD
class A { const A(this.onPressed); final Function onPressed; @override Widget build(BuildContext context) { return GestureDetector( onPressed: onPressed, ... ); } }
GOOD
class B { const A(this.onPressed); final VoidCallback onPressed; @override Widget build(BuildContext context) { return GestureDetector( onPressed: onPressed, ... ); } }
Function
ではなくVoidCallback
を使いました。
型の違いですね。
The argument type ‘dynamic’ can’t be assigned to the parameter type ‘void Function()’
BAD
User user = ModalRoute.of(context).settings.arguments;
GOOD
final user = ModalRoute.of(context).settings.arguments as User;
警告
警告は最悪無視しても実行は出来ます。
SizedBox for whitespaces.
BAD
Container( width: double.infinity, height: 240, child: ... );
GOOD
SizedBox( width: double.infinity, height: 240, child: ... );
SizedBox
は実はchild
を持っています。
良くある使い方だと、Widget間の余白を作る際にwidthかheightどちらかを指定する形かなと思います。
prefer const over final for declarations.
BAD
class AppColor { static final Color hoge = Color.fromRGBO(110, 110, 100, 1.0); }
GOOD
class AppColor { static const Color hoge = Color.fromRGBO(110, 110, 100, 1.0); }
finalよりconstが良いよと言う警告です。
ざっくり言うとfinalは実行時定数、constはコンパイル時定数です。
コンパイル時に定数にしてしまえるならそっちの方が良いよと言う警告ですね。
Only use double quotes for strings containing single quotes.
BAD
Text( "hoge", )
GOOD
Text( 'hoge', )
""
ではなく''
で文字列を定義しろということです。
`Future` results in async function bodies must be awaited or marked unawaited using `package:pedantic`
BAD
onPressed: () async { Navigator.of(context).pushNamed( 'user', ); }
GOOD
onPressed: () { Navigator.of(context).pushNamed( 'user', ); }
GOOD
onPressed: () async { await Navigator.of(context).pushNamed( 'user', ); }
GOOD
onPressed: () async { unawaited(Navigator.of(context).pushNamed( 'user', )); }
async
なメソッド内でFuture
を返すメソッドにはawait
を付けなさいよ
という警告です。
・そもそもasync
なメソッドにしない
・await
する
・unawaited
を明示的に呼び出す
のどれかで対処します。
Avoid async functions that return void.
BAD
void something() async { // ... }
GOOD
Future<void> something() async { // ... }
async
なメソッドの返り値は必ずFuture
にしようねという警告です。
Sort constructor declarations before other members.
BAD
class Hoge { final int a; final String b; const Hoge(this.a, this.b); }
GOOD
class Hoge { const Hoge(this.a, this.b); final int a; final String b; }
コンストラクタは一番最初に定義してねという警告です。
Omit type annotations for local variables.
BAD
final String a = 'hoge'; for (int i = 0; i < 10; ++i) { ... }
GOOD
final a = 'hoge'; for (var i = 0; i < 10; ++i) { ... }
ローカル変数を型名で定義するなということですね。
変更しないならfinal
、するならvar
で定義します。
Prefer const with constant constructors.
BAD
Container( child: Text('a'), )
GOOD
Container( child: const Text('a'), )
ウィジェットは基本的に何度もビルドされます。
ですが、const
キーワードをつけておけば
前回のビルド内容を再利用してくれるのでつけれるものは積極的につけていきましょう。
Prefer int literals over double literals.
BAD
SizedBox( width: 240.0, ) Tween(begin: 0.0, end: 1.0)
GOOD
SizedBox( width: 240, ) Tween(begin: 0, end: 1)
例え、double
型を受ける引数だったとしても与える値が
整数値なのであれば整数値で与えましょうということです。
Prefer using lowerCamelCase for constant names.
BAD
enum Hoge { THIS_IS, BAD, Enum, }
GOOD
enum Hoge { thisIs, good, enum, }
enum値の定義は先頭小文字のキャメルケースで定義します。
Separate the control structure expression from its statement.
BAD
if (somethingNew()) return; for (final item in items) { if (item.isEmpty) continue; ... }
GOOD
if (somethingNew()) { return; } for (final item in items) { if (item.isEmpty) { continue; } ... }
return
やcontinue
などを
ifと同じ行に書くなと言う警告です。
Use interpolation to compose strings and values.
BAD
final a = 'My name is ' + name;
GOOD
final a = 'My name is $name';
文字列連結の際に、+ではなく変数埋め込みを使いましょうという警告です。
No default cases.
enum Hoge { first, second, }
BAD
int something() { switch (hoge) { case Hoge.first: return 1; default: return 0; } }
GOOD
int something() { switch (hoge) { case Hoge.first: return 1; case Hoge.second: return 2; } throw UnimplementedError(); }
switch
文でdefault
節を使うなと言う警告です。
新しくHoge.third
が追加された時にBAD側だとdefault
で制御されてしまうので意図せぬ挙動を起こしやすいからだと思います。
個人的には、switch
文を抜けたらUnimplementedError
で受けるのが好きです。
Cascade consecutive method invocations on the same reference.
BAD
final user = User(); user.age = 18; user.name = 'ashdik';
GOOD
final user = User() ..age = 18 ..name = 'ashdik';
同じ変数に対しての代入は一つの文で行いましょうという警告です。
Avoid lines longer than 80 characters.
BAD
final hoge = ... // longer than 80 words.
GOOD
final hoge = ...;
1行に80文字以上書いてはいけません。
まとめ
どうでしょうか?
これ以外にもまだまだあるはずです。
頑張って綺麗なコードを書けるようにがんばりましょう 💪
また、こういう細々した作業が苦手な人は
僕が代わりにやることも可能なのでお声がけいただければと思います!
Twitterフォローお願いします
「次回以降も記事を読んでみたい!」「この辺分からなかったから質問したい!」
そんな時は、是非Twitter (@daiki1003)やInstagram (@ashdik_flutter)のフォローお願いします♪
Twitterコミュニティ参加お願いします
Twitterコミュニティ「Flutter lovers」を開設しました!参加お待ちしております😁
☕️ Buy me a coffee
また、記事がとても役に立ったと思う人はコーヒーを奢っていただけると非常に嬉しいです!
コメント