17_最佳实践与常见坑
Flutter 开发最佳实践和常见问题
Flutter 开发中的常见错误、性能优化技巧、代码组织规范。
常见错误与解决
1. setState after dispose
// ❌ 错误:组件已销毁后仍调用 setState
Future<void> loadData() async {
final data = await fetchApi();
setState(() { _data = data; }); // 可能报:setState called after dispose()
}
// ✅ 解决
Future<void> loadData() async {
final data = await fetchApi();
if (mounted) setState(() { _data = data; }); // 先检查组件是否还在
}
2. 忘记 dispose(内存泄漏)
// ❌ 以下对象创建后必须在 dispose 中释放
class _MyState extends State<MyWidget> {
final _textCtrl = TextEditingController();
final _animCtrl = AnimationController(vsync: this, duration: ...);
final _scrollCtrl = ScrollController();
StreamSubscription? _sub;
Timer? _timer;
// ✅ 全部在 dispose 释放
@override
void dispose() {
_textCtrl.dispose();
_animCtrl.dispose();
_scrollCtrl.dispose();
_sub?.cancel();
_timer?.cancel();
super.dispose();
}
}
3. initState 中不能 await
// ❌ 错误:initState 是同步方法
@override
void initState() {
super.initState();
await loadData(); // 语法错误!
}
// ✅ 解决:调用异步函数,但不 await initState 本身
@override
void initState() {
super.initState();
_loadData(); // 调用但不 await
}
Future<void> _loadData() async {
final data = await fetchApi();
if (mounted) setState(() { _data = data; });
}
4. async gap 后 BuildContext 失效
// ❌ 警告:Use of BuildContext across async gaps
onPressed: () async {
await someAsyncOperation();
Navigator.of(context).pop(); // context 可能已失效
}
// ✅ 解决:提前保存引用
onPressed: () async {
final nav = Navigator.of(context); // 提前取出
await someAsyncOperation();
nav.pop();
}
5. 在 build() 中创建对象
// ❌ 每次重建都创建新对象(如 AnimationController 需 vsync,不能在 build 中创建)
Widget build(BuildContext context) {
final controller = AnimationController(...); // 错误!
return ...;
}
// ✅ 在 initState 中创建,dispose 中释放
late AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(vsync: this, duration: ...);
}
性能优化技巧
// ① 使用 const Widget(编译期缓存,永不重建)
const Text('固定文字') // ✅
const SizedBox(height: 16) // ✅
const Divider() // ✅
// ② ListView.builder 代替 ListView(虚拟化渲染)
ListView.builder( // ✅ 按需渲染
itemCount: items.length,
itemBuilder: (ctx, i) => ItemWidget(items[i]),
)
// ③ 缩小 Consumer 监听范围
Consumer<CartModel>( // ✅ 只重建购物车部分
builder: (ctx, cart, _) => CartBadge(count: cart.count),
)
// ④ 用 context.select 只订阅需要的字段
final count = context.select<CartModel, int>((cart) => cart.count);
// 只有 count 变化才重建,而不是整个 CartModel 变化
// ⑤ RepaintBoundary:隔离重绘区域
RepaintBoundary(
child: ExpensiveWidget(), // 这个 Widget 的重绘不影响其他区域
)
// ⑥ 耗时计算放到 Isolate(类比 Web Worker)
Future<List<Post>> parsePosts(String json) async {
return compute(_parsePostsInIsolate, json); // 在独立线程解析
}
// 顶层或 static 函数(Isolate 中不能有闭包)
List<Post> _parsePostsInIsolate(String json) {
return (jsonDecode(json) as List).map((e) => Post.fromJson(e)).toList();
}
// ⑦ 图片按显示尺寸缓存
CachedNetworkImage(
memCacheWidth: 200, // 按显示宽度缓存,避免全尺寸占内存
memCacheHeight: 150,
)
代码组织规范
推荐目录结构:
lib/
├── main.dart
├── app.dart # MaterialApp + 路由 + 主题配置
├── core/
│ ├── constants/ # 常量:颜色、尺寸、字符串、API 路径
│ ├── theme/ # ThemeData 配置
│ ├── router/ # 路由定义
│ └── utils/ # 工具函数(日期格式化、字符串处理等)
├── data/
│ ├── models/ # 数据模型(fromJson/toJson)
│ ├── services/ # 网络请求服务(API 封装)
│ └── repositories/ # 数据层(合并网络 + 本地缓存)
└── presentation/
├── pages/ # 页面组件(每个路由一个文件)
├── widgets/ # 可复用 Widget
└── providers/ # 状态管理(ChangeNotifier / Notifier)
命名规范:
// 文件名:snake_case
// user_card.dart / api_service.dart / home_page.dart
// 类名:PascalCase
class UserCard extends StatelessWidget {}
class ApiService {}
// 变量/方法:camelCase
String userName = 'Alice';
void loadUserData() {}
// 常量:lowerCamelCase(Dart 不用 SCREAMING_SNAKE_CASE)
const double defaultPadding = 16.0;
const String apiBaseUrl = 'https://api.example.com';
// 私有成员:下划线前缀
int _count = 0;
void _increment() {}
Web vs Flutter 习惯对比
| 场景 | Web 习惯 | Flutter 写法 |
|---|---|---|
| 全局样式 | CSS 变量 | ThemeData |
| 组件间隔 | margin / gap |
SizedBox / Padding |
| 条件渲染 | v-if / && |
if (cond) widget,(集合 if) |
| 列表渲染 | v-for / .map() |
ListView.builder |
| 生命周期初始化 | created / mounted |
initState |
| 销毁回调 | beforeUnmount |
dispose |
| 响应式数据 | ref() / useState |
setState / ChangeNotifier |
| 全局状态 | Pinia / Redux | Provider / Riverpod |
| 路由跳转 | router.push('/page') |
Navigator.push(context, route) |
| 路由传参 | query / params | 构造函数参数 / arguments |
| 网络请求 | axios | dio |
| 本地存储 | localStorage | SharedPreferences |
| 打包 | npm run build |
flutter build apk/ios/web |
| 代码检查 | ESLint | flutter analyze |
| 格式化 | Prettier | dart format |