👁️

[Flutter] Linter選定と、厳しめにするメリットとは??

2024/10/15に公開

今回、Flutter(Dart)のプロジェクトでLinterの調査を行い、厳しめのルールを適用した経験をもとに、学びを共有しようと思います。特に、これからLinterを強化しようと考えている方のイメージ構築に役立てばいいなとおもいます!

こんな人におすすめ

・デフォルトのLinterしか使ったことがない。
・Linterの種類が多すぎて選定に迷っている。
・Linterを強めたらどうなるのか、効果や実際の経験を知りたい。

Linter Packages

Flutter のプロジェクトを作成すると デフォルトで flutter_lintsが適用されています。

デフォルトのLinter以外にも、プロジェクトのニーズに合ったLinterパッケージを選定することが可能です。

そのため、まずはどのようなLinterパッケージが存在するのかを知ることが重要です。以下のリンクを参考に、Linterパッケージを見てみると面白いです。

https://fluttergems.dev/linter/

Linter rules

Dartで使用できるLintルールの一覧は、以下のリンクで確認できます。
https://dart.dev/tools/linter-rules

どんな基準で選んだのか

すべてのLinterを持ってくるという選択肢もありでしたが、少々冗長に感じられたため、OSSで用いらているようなパッケージで、厳しいものという条件で探しました。

結論、very_good_analysis を用いることにしました。

https://pub.dev/packages/very_good_analysis

理由

  • Pub Likesが多くの開発者に支持されている。
  • GitHubが、継続的にメンテナンスされているため、信頼性が高く、最新のDartやFlutterの更新にも対応している。
  • 採用されてるルールが全Linterルールの8割以上をカバーしており、広範なコーディングスタイルに対応できる。

なぜ厳しいものから選んだかというと、好みに合わなければ、その設定を個別で省いていけばいいからです。

厳しめ → 徐々に緩める って感じの方針です。

結論。厳しめにしたらどうなったか?

既にある程度進行しているプロジェクトに厳しめのLintルールを適用すると、Linterから大量の警告が出ることになります。しかし、Linterを使ってコードの品質をチェックする習慣がつくと、コードの統一感が増し、バグも減り、プロジェクト全体の品質が大幅に向上します。

それに加えて、厳しいLint設定でも、VSCodeのsettings.jsoneditorconfigを活用することで、自動化されたフォーマットや修正が可能になり、コーディング作業も効率化されます。これらのツールの設定をしっかり行うことが、厳しめのLint設定を実装する上で必須です。

導入後のイメージ

全部を紹介するのは、数が多すぎるので、数個をピックアップします。

『こんな感じで警告がでて修正していくんだろうな。。。』

とイメージしていただければ幸いです。

⭐️バグの予測が可能になる

unawaited_futures

Future を待たずに処理を続行すると予期しない動作になる可能性があるため、await を忘れずに使用することを推奨します。

before

FirebaseAnalytics.instance.setAnalyticsCollectionEnabled(true);

after

await FirebaseAnalytics.instance.setAnalyticsCollectionEnabled(true);

use_build_context_synchronously

このLintルールは、非同期処理中にBuildContextを使用する際、ウィジェットがアンマウントされている可能性がある場合に警告を出します。非同期処理で時間がかかっている間に画面が変更されると、contextが不安定になり、アプリがクラッシュする原因となることがあります。このルールは、そのようなケースを防ぐために、BuildContextを安全に使用するよう促します。

before

onPressed: () async {
  await Future<void>.delayed(const Duration(seconds: 1));
  Navigator.of(context).pop();
},

after

onPressed: () async {
  await Future<void>.delayed(const Duration(seconds: 1));
  if (context.mounted) { ← 追加
    Navigator.of(context).pop();
  }
},

cancel_subscriptions

例えば、以下の定義があった場合、disposeされるような設定がないとメモリリークを引き起こします。

  StreamSubscription<int>? _subscription;

以下を追加すると警告が消えます。

@override
  void dispose() {
   // ウィジェットが破棄されるときに購読をキャンセルする
  _subscription?.cancel();
  super.dispose();
}

⭐️チームで一貫したコーディングスタイルになる

eol_at_end_of_file

ファイルの末尾に改行を追加することを推奨します。これにより、POSIX標準に準拠した形式になります。

こちらは、editorconfigや vscodeのsetting.jsonの設定を導入するといいかと思います。
https://editorconfig.org/

prefer_const_constructors

定数コンストラクタが使用できる場合、それを使用することを推奨します。コンストラクタに const を追加することでパフォーマンスが向上します。

before

final _patrolTesterConfig = PatrolTesterConfig();

after

const _patrolTesterConfig = PatrolTesterConfig(); 

always_put_required_named_parameters_first

必須の名前付きパラメータを常に他の任意のパラメータよりも先に置くことを推奨します。これにより、コードの読みやすさや保守性が向上します。

before

void exampleFunction({
String? optionalParam,
required String requiredParam}) {
}

after

void exampleFunction({
required String requiredParam,
String? optionalParam}){
}

気に入らないLintルールは外そう

実際に使っていき、いくつかは合わないなと感じたので以下のように、linterの設定が適用されないようにしました。

include: package:very_good_analysis/analysis_options.yaml

analyzer:
  errors:
    invalid_annotation_target: ignore
  exclude:
    - "**/*.freezed.dart"
    - "**/*.g.dart"
    - "**/*.mocks.dart" ....

linter:
  rules:
    public_member_api_docs: false
    no_default_cases: false
    one_member_abstracts: false
   .....

public_member_api_docs

抽象クラスやパブリックメンバーを定義したときに、/// を使ってドキュメントコメントを書くことを強制する警告です。
こちらのルールは、外す人が多いのではないかと感じています。

no_default_cases

このルールは、switch 文で default ケースを避け、すべての可能なケースを明示的に列挙することを推奨するものです。
しかし、switch文に default ケースを追加する方が好ましい場合があり、個人的な好みでこのルールは無効にしています。

※ これらのルールは、プロジェクトやチームの方針に合わせてカスタマイズし、柔軟に適用することが重要です。チーム全体の合意を得ながら、オリジナルのLint設定を調整していきます。

部分的にLinterの設定を無効化する

特定の行だけでLinterの警告を無効化するには、ignore コメントを使います。例えば、print 文の使用を許可する場合、以下のように設定します。

// ignore: avoid_print
print("This is a debug message");

このように、ignore を使うことで、特定のコードに対してLintの警告を無効化し、柔軟に対応できます。

ここでこの警告はいらないなってときに、活用しましょう。

vscodeのsetting.jsonを活用する

不要インポートを削除してくれたり、フォーマットや簡単な修正を行なってくれます。
こちらは、vscodeを使っているなら開発効率がぐんと伸びます。

"editor.codeActionsOnSave": {
  "quickfix.insertSemicolon": "always",
  "source.organizeImports": "always",
  "source.addMissingImports": "always",
  "source.fixAll": "always"
}
"editor.formatOnSave": true,
  "editor.formatOnType": true,
  "editor.formatOnPaste": true,
  "editor.rulers": [
    80
  ],

PR提出時に GitHub Actionsで flutter analyze を実行する

PR時に flutter analyzeを実行し、その結果を Danger でPRにコメントするようにしています。
これにより、Linterのエラーを簡単に確認できる仕組みを整えています。

例えば、コード内で一箇所だけ const を抜かしてみた場合、以下の警告が出ます。

$ flutter analyze

Analyzing melos_starter...                                              
   info • Use 'const' with the constructor to improve performance ...
1 issue found. (ran in 3.6s)

この結果を、GitHubのPRに自動的にコメントとして投稿します。
これにより、手間をかけずにLinterエラーを確認でき、レビュー作業がスムーズになります。

詳しい設定方法は、こちらの記事が参考になりましたので、ご紹介します。

https://zenn.dev/yorifuji/articles/flutter-github-actions-template#check.yml

最後に

やはり、コーディングルールが統一されていると、書き方が統一されて品質が向上します。
その結果、警告に注意を払うようになり、コードの見た目がよくなっていきます

個人的には、厳しめが好みですが、プロジェクトのメンバーと相談してみんなが書きやすいルールにしていこうと考えました!

Discussion