Flutterのテンプレートにlinterが入りますよ
追記 (2021/9/10)
無事、Flutter 2.5.0 が Stable になり、flutter_lints が Stable にやってきました 🙌
本編
こんにちはこんばんわ、すぎっと ٩( ᐛ )و です
今日のテーマは Linter です。リンターと読みます。
実は先日、Flutter の Issue サーフィン (謎趣味)をしていたところ、こんなものを見つけました。
そしてこの PR です。
ということはそろそろアナウンスがくるかな〜と思っていたら、
来ました ٩( ᐛ )و
内容については後ほど触れようと思いますが、まずは先に結論だけ示しておきます。
これによって何が起きたかと言うと、
"flutter create my_app"で作ることができる Flutter のテンプレートアプリに Linter が導入されるようになりました
Flutter 公式がテンプレートを変更してきました。これは事件ですね!
早速、どんなことが変わったのか見ていこうと思います。
Linter って何?
ソースコード警察 👮🏻♂️ です。
Flutter や Dart 以外のフレームワークや言語に慣れ親しんだ方なら、それら各方面での Linter を使っていると言う方は少なくないと思います。ここでは Linter ってはじめての方に向けて少しだけ簡単な説明をしておこうと思います。
まず、 Linter の役割は 静的解析 と言われるものです。ここでいう 静的 とは、アプリを実行する前 の段階でソースコードを解析し、必要な情報を教えてくれるものを指します。自動テストも静的解析と呼ばれたりしますので、静的解析というのは結構広義なものなのかなぁと思います。定義としては、「実行可能なプログラムを実行せずとも、プログラムの問題(または問題になりそうな箇所)を解析して見つけ出そう」というものだと思います。正確な定義はググってください。
つまり、Linter とは 静的解析ツール の 1 つであり、Flutter アプリをビルドすることなく、そのソースコードを解析し一定のルールに従わない箇所を提示してくれるプログラムを指します。そういう意味で、アプリ開発における「警察」って言われたりしますね。(私だけ・・・?)
さて、サラッと書きましたが「一定のルールに従わない箇所」がミソです。
通常、Linter はルールにしたがってソースコードを解析するプログラムそれ自体を指します。したがって、Linter を使う側であるアプリ開発者は、Linter という静的解析プログラムに対して 「ルール」を提供してあげる ことがその役割と言うことになります。では、Flutter ではどうやってルールを提供するのでしょうか。そもそもルールって何なのでしょうか。
この記事ではその辺りの説明を交えつつ、今回新しく Flutter のテンプレートに追加されたルールについて触れていこうと思います。
どうして Linter が必要なの?
プログラミング言語毎の性質にもよりますが、どの言語であってもソースコードというのはその目的に対して比較的自由なものです。そのため、どんな言語であっても、ベストプラクティスな書き方、公式推奨/非推奨の書き方、というものがあります。プログラマーは日々勉強を重ねながら、より良い書き方というのを更新し続けているような生き物だと思います。
では、組織に属したプログラマー集団が 1 つのアプリを作るとします。
それなりに成熟した組織であれば、コーディング規約 というローカルルールのようなものをドキュメントとして持っていると思います。通常、その組織に新たに参画し、プロジェクトにアサインされたときはその規約に目を通し、規約に従った実装が求められます。集団に属するというのはそういうものです。
昔はそれで良かったのですが、最近はコーディング規約という文書を読んで人がそれに従うというのは、実装者・レビュアー双方にとって負荷が高い と言われます。当然のように見落としは頻発しますし、それによってソースコードはどんどん汚れていき、終いにはその見落としが パフォーマンスを著しく低下させる といったことがあります。
プログラマーはいかに手を抜くかということに全力を尽くす生き物です。Linter はまさにそれです。コーディング規約というルールをプログラムが解釈可能な形で定義してあげることで、その ソースコードを常に監視できる 状態にしました。これによって、実装者もレビュアーも規約にしたがっているかどうかというチェックに気を使わなくて良くなりました。
このルールに従うという行為ですが、単にソースコードの見た目が揃うということに加えて、一定の品質を担保できる というメリットがあります。これがとても大きなメリットになります。
たとえば Flutter で言えば、
「書き変わる見込みのない Widget には const をつけておこうね」
と言うものがあります(以下)。
これは、Widget に対応する RenderObject の生成コストがバカにならないので、Widget の定義が変わらないなら保持して再利用した方がパフォーマンス的に良いよね、と言うものです。Flutter のパフォーマンス改善としてまずやっておこうぜ、と言われるものですね。
こういったものをルールとして、Linter というプログラムを実行することで、実装者が const をつけ忘れたとしても Linter が "const つけや〜" と教えてくれるようになります。
こんな感じですね
Flutter の Linter って前からあったの?
もちろんありました。
- pedantic
- effective_dart
が有名だったのですが、いずれも deprecated になりました。
deprecated になったのはなぜか?
まず pedantic の readme にこんなことが記載されていました。
This package is deprecated.
Before it was deprecated, it was the way to get analysis options matching those used internally at Google. This was useful because it was a good starting point for which lints to enable.
Instead, please see package:lints which is the lint recommendation from the Dart team, or package:flutter_lints which extends that for Flutter. These are better than the Google lint set because they take into account the needs of all Dart users, not just Google engineers.
また、effective_dart にもこんな記載がありました。
This package is deprecated.
Before it was deprecated, it was the way to provide analysis options corresponding to the guidelines in Effective Dart.
Instead, please see official packages: package:lints which is the lint recommendation from the Dart team, or package:flutter_lints which extends that for Flutter.
いずれのパッケージも、Flutter がオフィシャルに 推奨ルール を示したので、そっちを使ってね、と言っています。
私も pedantic には大変お世話になったのですが、感謝の気持ちをもって、新しいパッケージへお引っ越ししました。
Linter ってどうやって設定するの?
analysis_options.yaml を書けば良いのです。
この記事の最初にリンクを貼りましたが、再掲します。
すべてがここで説明されていますので、概要だけ説明します。
- flutter_lints: ^1.0.0 を入れます!(パッケージのバージョンは随時チェックしてください)
- analysis_options.yaml を Flutter プロジェクトのトップにおきます。(pubspec.yaml と同じ階層です)
以上です。
参考に、Flutter 2.3.0 - 12.0pre で導入された analysis_options.yaml はこんなファイルです。
flutter_lints にはどんなルールがあるの?
flutter_lints には Flutter が公式に推奨しているルールセットが定義されています。先に提示したテンプレートの analysis_options.yaml は、パッケージとして追加された flutter_lints から推奨ルールセットを取り込んでいます。include: package:flutter_lints/flutter.yaml
この部分ですね。
もしルールを追加したい場合は、analysis_options.yaml にルールを追記すれば、
flutter_lints の推奨ルール + 独自の指定ルール
で運用できます。
ということはですね、flutter_lints が提案している 推奨ルールセット に何が含まれているのかを把握しておいた方が良いですね。
・・・
とは言いましたが、ものすごい数があります。
正直言って、どんなルールがあるかをチェックするより、怒られたら直す というスタンスでいいと思います。
一応見ておきましょう。
flutter_lints
ここにある通りです。
おやおや、 include: package:lints/recommended.yaml
とありますね。といことは、lints/recommended.yaml
からルールを取り込んでいますね。
lints/recommended
めっちゃ多い・・・( ᐛ )
しかも include: package:lints/core.yaml
してありますねぇ。
lints/core
やっと終わりました。
ルールの定義
各ルールの意味が知りたい場合は以下のページから参照できます。また、独自にルールを追加したい場合も以下の Dart lints から選択します。
Linter に怒られたけど、どうやって直したらいいのかわからないよ〜!
というときは、以下のページからエラーメッセージのキーワードを検索してあげれば Good & Bad なコードが書かれていますので、理解の手助けになると思います。
たとえばさきほどの const の件であれば、
prefer_const_constructors がルールの名前ですね。これで検索してあげてください。
追記: 誤解を防ぐために
mono さんの pedantic_mono が flutter_lints をベースとするものにパワーアップされました。仕事が早い…
その中で、こんなことをおっしゃられていました。
あ〜自分の記事がこの誤解を招いてたらいやだなぁと思ったので追記します。
ちゃんと読めば flutter_lints がデフォルトになっただけで、analysis_options.yaml に独自に選んだルールを足せばよいというのは伝わると思うのですが、走り読みをすると
そうか〜🤤
pedantic と effective_dart が deprecated になったのか〜🤤
これからは flutter_lints なのか 〜🤤
みたいな、パッケージ選定の話みたいに捉えられかねないなぁと思ったからです。
大事なのはあくまで "ルールセット" です。
パッケージに何を使うかというのは、自分が採用したい ルールセット を基準に選んでください。
その際のベースラインのようなものとして、flutter_lints が出てきた、と思ってください。
- これまで Linter を導入してこなかった方へ
- まずは flutter_lints からはじめてみてはいかがでしょうか? Linter 無しは卒業しましょう。
- これまで Linter を使っていた方へ
- flutter_lints が持つルールと、いま使っているルールを比べてみてください。そして、必要に応じて追加してください。
- もっと Linter を使いこなしたい方へ
- Linter のルール集を作っているパッケージはいくつかあります。それらを参考に自分のプロジェクトに導入可能なルールがあれば積極的に採用しましょう。また、使用しているパッケージが推奨するルールがある場合もあります。そういったものも参考にしてみてください。
参考
flutter_lints だけで有効になるルール集
- always_require_non_null_named_parameters
- annotate_overrides
- avoid_empty_else
- avoid_function_literals_in_foreach_calls
- avoid_init_to_null
- avoid_null_checks_in_equality_operators
- avoid_print
- avoid_relative_lib_imports
- avoid_renaming_method_parameters
- avoid_return_types_on_setters
- avoid_returning_null_for_void
- avoid_shadowing_type_parameters
- avoid_single_cascade_in_expression_statements
- avoid_types_as_parameter_names
- avoid_unnecessary_containers
- avoid_web_libraries_in_flutter
- await_only_futures
- camel_case_extensions
- camel_case_types
- constant_identifier_names
- control_flow_in_finally
- curly_braces_in_flow_control_structures
- empty_catches
- empty_constructor_bodies
- empty_statements
- exhaustive_cases
- file_names
- hash_and_equals
- implementation_imports
- iterable_contains_unrelated_type
- library_names
- library_prefixes
- list_remove_unrelated_type
- no_duplicate_case_values
- no_logic_in_create_state
- non_constant_identifier_names
- null_closures
- overridden_fields
- package_names
- package_prefixed_library_names
- prefer_adjacent_string_concatenation
- prefer_collection_literals
- prefer_conditional_assignment
- prefer_const_constructors
- prefer_const_constructors_in_immutables
- prefer_const_declarations
- prefer_const_literals_to_create_immutables
- prefer_contains
- prefer_equal_for_default_values
- prefer_final_fields
- prefer_for_elements_to_map_fromIterable
- prefer_function_declarations_over_variables
- prefer_generic_function_type_aliases
- prefer_if_null_operators
- prefer_initializing_formals
- prefer_inlined_adds
- prefer_is_empty
- prefer_is_not_empty
- prefer_is_not_operator
- prefer_iterable_whereType
- prefer_null_aware_operators
- prefer_spread_collections
- prefer_typing_uninitialized_variables
- prefer_void_to_null
- provide_deprecation_message
- recursive_getters
- sized_box_for_whitespace
- slash_for_doc_comments
- type_init_formals
- unnecessary_brace_in_string_interps
- unnecessary_const
- unnecessary_getters_setters
- unnecessary_new
- unnecessary_null_in_if_null_operators
- unnecessary_overrides
- unnecessary_string_escapes
- unnecessary_string_interpolations
- unnecessary_this
- unrelated_type_equality_checks
- use_full_hex_values_for_flutter_colors
- use_function_type_syntax_for_parameters
- use_key_in_widget_constructors
- use_rethrow_when_possible
- valid_regexps
- void_checks
pedantic_mono で追加されているルール集
pedantic_mono では、flutter_lints のルールに 加えて、下記のルールを採用されています。
- always_declare_return_types
- always_put_control_body_on_new_line
- avoid_bool_literals_in_conditional_expressions
- avoid_catches_without_on_clauses
- avoid_catching_errors
- avoid_classes_with_only_static_members
- avoid_double_and_int_checks
- avoid_dynamic_calls
- avoid_field_initializers_in_const_classes
- avoid_implementing_value_types
- avoid_js_rounded_ints
- avoid_multiple_declarations_per_line
- avoid_positional_boolean_parameters
- avoid_private_typedef_functions
- avoid_returning_null
- avoid_returning_this
- avoid_setters_without_getters
- avoid_slow_async_io
- avoid_type_to_string
- avoid_unused_constructor_parameters
- avoid_void_async
- cancel_subscriptions
- cascade_invocations
- cast_nullable_to_non_nullable
- close_sinks
- comment_references
- deprecated_consistency
- directives_ordering
- flutter_style_todos
- invariant_booleans
- join_return_with_assignment
- lines_longer_than_80_chars
- literal_only_boolean_expressions
- no_adjacent_strings_in_list
- no_default_cases
- null_check_on_nullable_type_parameter
- omit_local_variable_types
- one_member_abstracts
- only_throw_errors
- package_api_docs
- parameter_assignments
- prefer_asserts_in_initializer_lists
- prefer_constructors_over_static_methods
- prefer_final_in_for_each
- prefer_final_locals
- prefer_foreach
- prefer_int_literals
- prefer_interpolation_to_compose_strings
- prefer_single_quotes
- sort_child_properties_last
- sort_constructors_first
- sort_pub_dependencies
- sort_unnamed_constructors_first
- test_types_in_equals
- throw_in_finally
- tighten_type_of_initializing_formals
- type_annotate_public_apis
- unawaited_futures
- unnecessary_await_in_return
- unnecessary_lambdas
- unnecessary_null_aware_assignments
- unnecessary_null_checks
- unnecessary_nullable_for_final_variable_declarations
- unnecessary_parenthesis
- unnecessary_statements
- unsafe_html
- use_is_even_rather_than_modulo
- use_late_for_private_fields_and_variables
- use_named_constants
- use_setters_to_change_properties
- use_string_buffers
- use_to_and_as_if_applicable
まとめ
Linter はコードに秩序をもたらします。秩序はソースコードに安心と信頼をもたらします。パフォーマンス面でも最低ラインをクリアできます。これまで Linter を使っていなかった方にはぜひ導入を検討していただきたいですね。
ちなみに、flutter create my_app
で Linter が導入されるのは 2.3.0-12.0.pre 以降です。
この記事執筆時点では、Stable はまだ 2.2.3 なので、チャンネルを変更すれば試してみることができます。
それまでは手動で導入することになりますが、近い将来 Stable にもやってくる、テンプレートの変更に沿って対応するところからスタートしてみてはいかがでしょうか。
ではでは。
すぎっと ٩( ᐛ )و
Discussion
すごく参考になりました!ありがとうございます!