好的,如果让我以一个真正的、亲自动手的 Flutter 开发者身份,跟其他开发者聊聊,我会这样重新组织这段话------可能不完美,但绝对实用。
说实话,没有什么比丑陋、不一致的代码 ,以及那些不知怎么就溜到测试环境的**"隐形"Bug** 更让人丧失动力的了。如果你和哪怕一个队友一起捣鼓过一个中等规模的 Flutter 代码库,你肯定懂我在说什么:合并地狱、莫名其妙的警告 ,还有大家为了一个该死的组件格式怎么写而争论不休。我吃过太多亏了,所以我知道------如果你想让你的 Flutter 应用保持整洁,不变成一堆烂摊子,那么 lints 绝不是可选项。它们就像你平时忘了系的安全带......直到你撞车。
咱们今天就来聊聊真正实用的 Flutter lints 设置------不是那种"加个包就完事"的敷衍内容。我分享的这些,是实实在在帮助我(大部分时候)保持理智的方法,尤其是在我需要在不同功能间切换、审查同事的 PR,或者在昏昏欲睡的周五晚上努力不搞砸 CI 的时候。
我的 Flutter Lint 实战流程
说实话,这些命令已经刻在我肌肉记忆里了。如果你还没开始用它们,那你很可能错过了连自己都不知道的 Bug。
flutter analyze
这绝对是我的首选 。我总是会运行它,甚至在代码推送之前也不例外。为啥?因为它能捕获那些"卧槽"的瞬间------未使用的变量、意外的动态类型、缺失的 Key,以及各种各样的问题。如果我有一周没跑这个命令,我肯定会后悔。这是发现明显问题最简单、最快捷的方法。
dart fix --dry-run
你可以把它想象成你的代码算命师。它会告诉你 Dart 会自动修复哪些内容,但并不会真的改动你的代码,这样你就不必盲目地信任一个"黑箱"。我通常会在进行任何大型重构之前,先用它来快速预览一下潜在的影响。
dart format .
没人希望审查一个包含 200 行"格式化修改"的 PR,那简直是噪音。我每次都会运行这个命令,有时甚至会把它放到 pre-commit hook 里。是的,它会覆盖所有内容,但这正是它的目的:现在我们所有的代码看起来真正对齐、保持一致了。
dart fix --apply
一旦我对 dart fix --dry-run
建议的修改感到满意,我就会让它执行。这个命令一口气修复了大量烦人的问题。老实说,光是这一个命令就为我节省了大量手动查找和修正 lint 警告的时间。它能极大地提升效率。
像真实项目一样设置 Lints
步骤 1: 添加 lints 包
别想太多------直接用官方的就行:
yaml
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^6.0.0 # 坚持经常检查最新版本。 pub.dev是你的好朋友.
然后,运行 flutter pub get
。很简单。
步骤 2: 实际使用 analysis_options.yaml
这里就是你制定规则的地方了。有些人可能直接复制粘贴 README 里的内容,但如果你想以后少点麻烦,最好根据你的团队(或你自己)的习惯来调整它。
如果你的项目根目录下没有 analysis_options.yaml
,那就加一个。
先从基础配置开始,然后逐步加入你认为必不可少的规则:
yaml
include: package:flutter_lints/flutter.yaml
analyzer:
language:
strict-casts: true
strict-inference: true
strict-raw-types: true
linter:
rules:
prefer_const_constructors: true
prefer_final_locals: true
require_trailing_commas: true
always_declare_return_types: true
use_super_parameters: true
avoid_unnecessary_containers: true
use_key_in_widget_constructors: true
unawaited_futures: true
avoid_catching_errors: true
# 根据你的"感觉"来添加、删除或注释掉任何规则。
专业提示: 如果你对什么是"干净的代码"有自己的看法,那么 analysis_options.yaml
这个文件才是你和团队应该"争论"的地方,而不是在第四次合并冲突之后才来吵。
我实际使用的 Lint 规则(及原因)
Lint 规则多如牛毛。这里我将我总是启用的那些规则(以及我为什么费心启用它们)进行了分类,分为"让你的应用更不容易崩溃"和"让你的代码更不容易让队友讨厌你"两类。其中大部分都救过我,或者至少让我避免了令人尴尬的 PR 评论。
安全与健壮性规则
use_key_in_widget_constructors
(在 widget 构造函数中使用 key): 相信我,如果你曾因为缺少key
导致列表视图出现奇怪的问题,你绝对不会想再调试那种鬼东西。这条规则会强制你在自定义组件中总是添加key:
参数。我以前也忽视这个,直到有一次一个 UI 状态 Bug 花了我一个小时才追踪到。discarded_futures
&unawaited_futures
(被丢弃的 Future & 未等待的 Future): Flutter 是一个异步的世界。如果你忘记await
一个 Future(或者只是让它悬着),你可能会遇到 UI 更新只完成一半、静默失败,或者你以为已经完成的后台操作其实并没有。这些规则会指出你没有正确处理 Future 的问题------说真的,它们救我免于生产环境的 Bug。avoid_catching_errors
(避免捕获 Error): 有时你就是想随便加个try/catch
然后继续,但千万别捕获 Dart 的Error
!这条规则确保你不会吞掉编程错误(比如NoSuchMethodError
),这些错误是设计成大声崩溃的,这样你才会真正去修复它们。no_logic_in_create_state
(createState 中不含逻辑): 我以前习惯把设置代码都扔进createState
,但后来我的热重载就开始出问题,我还会无缘无故地丢失状态。经验教训啊,保持那个方法干净。use_build_context_synchronously
(同步使用 BuildContext): 经典的 Flutter 陷阱:你执行一个异步调用,然后尝试在await
之后使用context
------砰,报错。这条规则可以保护你免受此害,说实话,我真希望它能早点出现。avoid_unnecessary_containers
(避免不必要的 Container): 如果你像我一样,有时只是随便用Container
包裹一些东西。结果发现,这会让你的 Widget 树变得混乱,有时甚至会影响性能。这条规则会轻轻地拍你的手,然后说:"嘿,你真的需要这个吗?"enable_null_safety
(启用空安全): 空安全现在基本上是 Flutter 的标配了,但如果你的代码库比较老旧或者混杂,一定要仔细检查是否启用了它。与空相关的 Bug 绝对是最糟糕的。avoid_returning_null_for_void
(避免为 void 返回 null): Dart 的void
函数不需要返回null
。这条规则有助于让你的代码更符合惯用法,并且对任何阅读代码的人(包括未来的你)来说都更不容易混淆。
代码质量与可维护性规则
file_names
(文件命名): 大胆发言:我不会总是在处理旧代码时强制使用snake_case
,但如果你从头开始,那就直接用吧。一致的文件命名很枯燥,但非常值得。prefer_const_constructors
(偏好使用 const 构造函数): 这是最简单的性能提升。Flutter 对const
非常着迷。如果能用,就用它------它有助于构建优化,让应用更流畅。prefer_const_literals_to_create_immutables
(偏好使用 const 字面量创建不可变对象): 同理:所有东西都const
化,即使是你的列表和映射。如果它们不需要改变,就让它们变成const
。prefer_final_fields
/prefer_final_locals
(偏好使用 final 字段 / 偏好使用 final 局部变量): 每次我把一个变量设为可变时,总会有人(有时是我自己)不小心在后来改变它。把所有东西都final
化。这会让你的代码库更可预测,错误也少得多。always_use_package_imports
(总是使用 package 导入): 相对路径导入没问题------直到你开始移动文件。到处使用package:
导入帮我避免了太多愚蠢的合并冲突和构建失败。require_trailing_commas
(要求尾随逗号): 小细节,但它让每个 git diff 都更容易阅读,而且自动格式化工具也能更好地工作。camel_case_types
(驼峰式类型命名): 类和枚举应该使用CamelCase
。如果你曾经读过别人的代码,却分不清什么是类什么是方法,你就会明白为什么了。constant_identifier_names
(常量标识符命名): 为你的常量选择一种风格(camelCase
或UPPER_SNAKE_CASE
)并坚持下去。没人想在同一个文件里看到Myvar
旁边是MY_CONST
。curly_braces_in_flow_control_structures
(流控制结构中使用大括号): 这条规则是一个经典的 Bug 防御者:即使是单行的if
和循环,也要始终使用大括号。它能帮你避免那些咬过每个写 C 风格代码的人的"悬空 else"Bug。always_declare_return_types
(总是声明返回类型): 不要指望 Dart 分析器能完全理解你的意图。明确地声明------这会让阅读和重构代码的痛苦大大减少。no_leading_underscores_for_local_identifiers
(局部标识符不带前导下划线): 下划线是为私有成员保留的,而不是随便给局部变量用。这能让你的代码不那么混乱。unnecessary_constructor_name
(不必要的构造函数名): Dart 允许你命名构造函数,但如果你只是重复类名,那就没必要了。这条规则会标记出那些无意义的构造函数。empty_catches
(空 catch 块): 如果你捕获了一个异常却什么都不做,那你只是在为未来制造一堆麻烦。处理它,或者至少把它记录下来。unnecessary_null_checks
(不必要的空检查): Dart 的空安全功能非常智能。如果你看到代码中到处都是!
和?
,这条规则可以帮助你清理这些多余的检查。
真心话:Lints 是一种文化,不是一份清单
采纳 lints 更多地是为了在三个月后不至于讨厌自己的(或团队的)代码,而不是简单地遵循"规则"。从基础开始,添加那些对你的项目真正重要的规则,并不断调整。相信我,你会有更多时间去构建酷炫的功能,而不是去修复愚蠢的 Bug 或者争论空格和制表符。
如果你在每次提交前都没有运行这些检查?那就等着瞧吧------你会以一种痛苦的方式学到教训的(我就经历过)。
祝你编程愉快,愿你的构建总是绿色。
------一个曾经搞砸过不少 Flutter 项目,但至少 lints 总是先吼我的开发者。
参考资料
- 所有 Flutter 规则的汇总:linter-rules
- Flutter lint 包:flutter_lints