← Frontend / Flutter

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