《如何做好 Code Review》中我论述过代码审查对于保证代码品质的重要性,最近开始采用Git Hooks的方式为Flutter项目增加提交前的强制自我审查。这样做的好处在于将机械化检查交给电脑,把思考的部分留给大脑。

我认为代码提交前需要做的最基本检查包括格式和代码规范。前者交给Prettier,后者由Analyze负责。

Lefthook

我采用Lefthook实现 Git 钩子,如果你有更好的选择,欢迎推荐分享。

1
2
# Mac
brew install lefthook && lefthook install

安装 Lefthook 后,在项目根目录下运行lefthook install命令来生成lefthook.yml文件,并在其中的pre-commit > commands节点下添加两项配置:

1
2
3
4
5
prettier:
glob: "*.dart"
run: dart format {staged_files} && git add {staged_files}
linter:
run: dart analyze

Analyze

flutter analyze通过flutter_lints插件对Dart代码进行静态语言检查,检查规则配置在analysis_options.yaml文件中。但是打开该文件可以看到,Flutter 并没有为我们配置任何默认规则。我把我正在采用的规则配置放在文末,欢迎酌情采纳。

接下来我们可以测试一下配置能否正常工作。比如,将main()替换为:

1
2
3
4
5
void main() {
const title= 'Flutter';
runApp(const MyApp());}

aa() {}

然后运行命令:

1
git add .&&git commit -m 'Commit for analysis.'

接下来你会发现,Prettier 将代码格式化为:

1
2
3
4
5
6
void main() {
const title = 'Flutter';
runApp(const MyApp());
}

aa() {}

但是静态检查没有通过,因为title变量没有被使用、函数aa没有返回类型等:

1
2
3
4
5
6
7
8
EXECUTE > linter
Analyzing demo_lint_flutter...

info • The value of the local variable 'title' isn't used • lib/main.dart:4:9 • unused_local_variable
info • The function 'aa' should have a return type but doesn't • lib/main.dart:8:1 • always_declare_return_types
info • Type annotate public APIs • lib/main.dart:8:1 • type_annotate_public_apis

3 issues found. (ran in 2.0s)

强烈建议你在接下来的每一个 Flutter 项目里都坚持这样做,不要提交 Smelling Code。

analysis_options.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
include: package:flutter_lints/flutter.yaml

analyzer:
strong-mode:
implicit-casts: false
errors:
missing_required_param: warning
missing_return: warning
todo: ignore

linter:
rules:
always_declare_return_types: true
always_require_non_null_named_parameters: true
annotate_overrides: true
avoid_bool_literals_in_conditional_expressions: true
avoid_catching_errors: true
avoid_classes_with_only_static_members: true
avoid_empty_else: true
avoid_escaping_inner_quotes: true
avoid_field_initializers_in_const_classes: true
avoid_function_literals_in_foreach_calls: true
avoid_implementing_value_types: true
avoid_init_to_null: true
avoid_multiple_declarations_per_line: true
avoid_null_checks_in_equality_operators: true
avoid_positional_boolean_parameters: true
avoid_print: true
avoid_private_typedef_functions: true
avoid_redundant_argument_values: true
avoid_relative_lib_imports: true
avoid_return_types_on_setters: true
avoid_returning_null_for_future: true
avoid_returning_null_for_void: true
avoid_setters_without_getters: true
avoid_shadowing_type_parameters: true
avoid_single_cascade_in_expression_statements: true
avoid_type_to_string: true
avoid_types_as_parameter_names: true
avoid_unnecessary_containers: true
avoid_unused_constructor_parameters: true
avoid_void_async: true
avoid_web_libraries_in_flutter: true
await_only_futures: true
camel_case_extensions: true
camel_case_types: true
cancel_subscriptions: true
cast_nullable_to_non_nullable: true
constant_identifier_names: true
control_flow_in_finally: true
curly_braces_in_flow_control_structures: true
depend_on_referenced_packages: true
deprecated_consistency: true
empty_catches: true
empty_constructor_bodies: true
empty_statements: true
eol_at_end_of_file: true
exhaustive_cases: true
file_names: true
hash_and_equals: true
implementation_imports: true
invariant_booleans: true
iterable_contains_unrelated_type: true
join_return_with_assignment: true
leading_newlines_in_multiline_strings: true
library_names: true
library_prefixes: true
list_remove_unrelated_type: true
missing_whitespace_between_adjacent_strings: true
no_adjacent_strings_in_list: true
no_duplicate_case_values: true
no_logic_in_create_state: true
no_runtimeType_toString: true
non_constant_identifier_names: true
noop_primitive_operations: true
null_check_on_nullable_type_parameter: true
null_closures: true
overridden_fields: true
package_names: true
package_prefixed_library_names: true
parameter_assignments: true
prefer_asserts_in_initializer_lists: true
prefer_collection_literals: true
prefer_conditional_assignment: true
prefer_const_constructors: true
prefer_const_constructors_in_immutables: true
prefer_const_declarations: true
prefer_const_literals_to_create_immutables: true
prefer_constructors_over_static_methods: true
prefer_contains: true
prefer_equal_for_default_values: true
prefer_final_fields: true
prefer_final_in_for_each: true
prefer_final_locals: true
prefer_for_elements_to_map_fromIterable: true
prefer_function_declarations_over_variables: true
prefer_generic_function_type_aliases: true
prefer_if_elements_to_conditional_expressions: true
prefer_if_null_operators: true
prefer_initializing_formals: true
prefer_inlined_adds: true
prefer_interpolation_to_compose_strings: true
prefer_is_empty: true
prefer_is_not_empty: true
prefer_is_not_operator: true
prefer_iterable_whereType: true
prefer_null_aware_method_calls: true
prefer_spread_collections: true
prefer_typing_uninitialized_variables: true
prefer_void_to_null: true
provide_deprecation_message: true
recursive_getters: true
require_trailing_commas: true
sized_box_for_whitespace: true
slash_for_doc_comments: true
sort_child_properties_last: true
sort_unnamed_constructors_first: true
test_types_in_equals: true
throw_in_finally: true
tighten_type_of_initializing_formals: true
type_annotate_public_apis: true
type_init_formals: true
unnecessary_await_in_return: true
unnecessary_brace_in_string_interps: true
unnecessary_const: true
unnecessary_getters_setters: true
unnecessary_new: true
unnecessary_null_aware_assignments: true
unnecessary_null_checks: true
unnecessary_null_in_if_null_operators: true
unnecessary_nullable_for_final_variable_declarations: true
unnecessary_overrides: true
unnecessary_parenthesis: true
unnecessary_raw_strings: true
unnecessary_statements: true
unnecessary_string_escapes: true
unnecessary_string_interpolations: true
unnecessary_this: true
unrelated_type_equality_checks: true
unsafe_html: true
use_build_context_synchronously: true
use_full_hex_values_for_flutter_colors: true
use_function_type_syntax_for_parameters: true
use_named_constants: true
use_late_for_private_fields_and_variables: true
use_rethrow_when_possible: true
use_setters_to_change_properties: true
use_string_buffers: true
use_test_throws_matchers: true
valid_regexps: true
void_checks: true