Flutter 开发代码规范(优化完善版)
目录
- 一、规范目标
- 二、目录结构规范(强制)
- 三、命名规范(强制)
- 四、代码风格规范(强制+建议)
- 五、状态管理规范(强制+建议)
- 六、GetX使用规范(强制+建议)
- 七、属性访问控制规范(强制)
- 八、性能优化规范(强制)
- 九、资源与配置规范(强制)
- 十、规范落地与维护
一、规范目标
- 统一团队开发风格,降低协作成本,提升代码可读性、可维护性;
- 规避常见性能问题,保障应用流畅性与稳定性;
- 符合 Dart/Flutter 官方最佳实践,兼容生态工具链(静态分析、测试、文档生成);
- 明确边界规则(强制约束+建议规范),平衡规范严谨性与开发效率。
二、目录结构规范(强制)
基于现有项目结构和新项目特点,采用功能模块化+核心共享的混合结构:
现有项目结构(适配当前项目)
lib/
├── main.dart # 应用入口
├── orientation.dart # 屏幕方向控制
├── time.dart # 时间相关功能
├── color/ # 颜色定义目录
│ └── color.dart
├── components/ # 公共组件目录
│ ├── general_widget/ # 通用组件
│ │ ├── ksAppBar.dart
│ │ ├── ksButton.dart
│ │ └── ksCard.dart
│ ├── analytics_debug/ # 分析调试组件
│ ├── echarts/ # 图表组件
│ ├── float_button/ # 浮动按钮组件
│ └── [其他单独组件]
├── const/ # 常量目录
│ └── resource.dart
├── entities/ # 实体模型目录
│ ├── pageView.dart
│ └── theme.dart
├── extension/ # 扩展目录
│ ├── build_context_extension.dart
│ ├── screen_extension.dart
│ └── widget_extension.dart
├── i18n/ # 国际化目录
│ ├── locales/
│ │ ├── en.json
│ │ └── zh.json
│ ├── app_localizations.dart
│ └── language_controller.dart
├── generated/ # 生成代码目录
│ └── json/
│ ├── CoursePlanData.g.dart
│ └── [其他生成文件]
├── mainClass/ # 主要业务类目录
│ ├── course_data_entity.dart
│ ├── myCourse.dart
│ ├── loginStudent.dart
│ ├── sharedPreferencesUtils.dart
│ └── [其他业务类]
├── pages/ # 页面目录
│ ├── home.dart
│ ├── backparent.dart
│ ├── Comment/ # 评论模块
│ ├── IntelligentEnglish/ # 智能英语模块
│ ├── SynchronousClassroom/ # 同步课堂模块
│ ├── chinese/ # 语文模块
│ ├── math/ # 数学模块
│ ├── programming/ # 编程模块
│ └── zhimi/ # 智米模块
├── routes/ # 路由目录
│ ├── app_routes.dart
│ ├── routes.dart
│ ├── getx_router_observer.dart
│ └── navigator_observer.dart
├── services/ # 服务目录
│ ├── apiResponseData/ # API响应数据
│ ├── analysis/ # 分析服务
│ ├── testWord/ # 测试单词
│ ├── Httputil.dart
│ ├── request.dart
│ └── [其他服务]
├── style/ # 样式目录
│ ├── c_base.dart
│ ├── c_color.dart
│ ├── c_font.dart
│ └── c_radii.dart
├── util/ # 工具目录
│ ├── httpRequest.dart
│ ├── localStorage.dart
│ ├── log_util.dart
│ ├── eventBus.dart
│ └── sensorsAnalytics.dart
└── common/ # 公共目录
├── value/ # 值定义
├── api.dart
├── check.dart
├── login.dart
└── [其他公共工具]
三、命名规范(强制)
完全遵循 Dart 官方命名约定,补充 Flutter 特有场景规则:
| 类型 | 命名规则 | 示例 | 禁止示例 |
|---|---|---|---|
| 类(Class/Widget) | 大驼峰(PascalCase),名词/名词短语 | HomePage、CustomButton、UserModel | home_page、custom_button |
| 函数/方法 | 小驼峰(camelCase),动词/动词短语 | getUserInfo()、showToast() | get_user_info()、ShowToast() |
| 变量/参数 | 小驼峰(camelCase),名词/名词短语 | userName、isLogin、itemCount | user_name、IsLogin |
| 常量(const/final) | 全大写+下划线(UPPER_SNAKE_CASE) | const MAX_COUNT = 10;、final API_BASE_URL = 'xxx' | maxCount、apiBaseUrl |
| 枚举(enum) | 枚举名:大驼峰;枚举值:小驼峰 | enum LoginType { phone, wechat, qq } | enum login_type { Phone, Wechat } |
| 路由名称 | 小写+下划线,格式:/模块/页面 | /user/login、/home/index | /User/Login、homeLogin |
| 文件名 | 小写+下划线(snake_case),与类名对应 | home_page.dart(对应 HomePage) | HomePage.dart、homePage.dart |
| 包名(package) | 小写+下划线,全小写无大写 | package:my_app/modules/user | package:MyApp/Modules/User |
| 私有属性/方法 | 下划线开头 + 小驼峰 | _userName、_calculateTotal() | privateUserName、calculateTotal() |
特殊命名补充
- 布尔变量建议前缀:
is、has、can、should(如isVisible、hasPermission); - 回调函数命名:后缀
Callback,如OnLoginCallback、OnItemClickCallback; - 状态管理相关:
- GetX控制器类名后缀
Controller(如UserController); - Bloc 类名后缀
Bloc(如CounterBloc),状态类后缀State;
- GetX控制器类名后缀
- 工具类:后缀
Utils或Helper(如DateUtils、StringHelper); - 扩展类:后缀
Extension(如StringExtension、DateTimeExtension); - 避免使用缩写(除非是通用缩写如
API、UI),禁止拼音命名(如shouyePage→homePage)。
四、代码风格规范(强制+建议)
4.1 基础格式(强制)
- 缩进:2 个空格(Dart 官方标准),禁止使用 Tab;
- 行宽:单行长不超过 120 字符(超出需换行),IDE 开启自动换行;
- 空行:
- 类/函数之间留 1 个空行;
- 函数内部逻辑块之间留 1 个空行;
- 避免连续空行(最多 1 个);
- 括号:
- 类、函数、条件语句的左括号与声明在同一行,右括号单独成行;
- 单行条件语句可省略括号(但逻辑复杂时必须加);
// 正确 if (isLogin) { navigateToHome(); } // 允许(单行简单逻辑) if (isLogin) navigateToHome(); // 错误 if (isLogin) { navigateToHome(); } - 分号:每条语句必须加分号(Dart 不是 JavaScript);
- 逗号:对象/数组最后一个元素后建议加逗号(IDE 自动格式化时更整洁);
final user = User( name: '张三', age: 20, // 末尾逗号建议保留 );
4.2 Widget 开发规范(强制)
- 优先使用 StatelessWidget:无状态组件性能更优,仅当需要维护状态时使用 StatefulWidget;
- Widget 拆分原则:
- 单个 Widget 代码不超过 150 行,超出则拆分为子 Widget;
- 重复出现的 UI 片段(如列表项、按钮)必须提取为公共组件;
- 避免嵌套过深(建议不超过 4 层),使用
Padding/SizedBox替代嵌套Container,用Wrap替代多层Row/Column;
// 错误(嵌套过深) Container( child: Padding( padding: EdgeInsets.all(16), child: Row( children: [ Container( child: Text('标题'), ), ], ), ), ); // 正确(拆分+简化嵌套) Padding( padding: EdgeInsets.all(Dimens.p16), child: Row( children: [ _buildTitleText(), ], ), ); Widget _buildTitleText() => Text(Strings.title); - build 方法规范:
- 禁止在
build中执行耗时操作(如网络请求、数据库查询、复杂计算); - 禁止在
build中创建临时对象(如List、Map、Style),需缓存为final变量或提取为常量;
// 错误(build 中创建 List) @override Widget build(BuildContext context) { return ListView( children: [Text('1'), Text('2')], // 每次 build 重建 List ); } // 正确(缓存为 final) final List<Widget> _listItems = [Text('1'), Text('2')]; @override Widget build(BuildContext context) { return ListView(children: _listItems); } - 禁止在
- const 构造函数:
- 无状态 Widget 且构造参数均为常量时,必须使用
const构造函数(减少重建开销);
// 正确 const CustomButton({super.key, required this.onTap}); // 调用时也需加 const(除非父组件非 const) const CustomButton(onTap: _handleTap); - 无状态 Widget 且构造参数均为常量时,必须使用
- 命名参数:
- Widget 构造函数仅使用命名参数(
{}包裹),必填参数加required关键字; - 参数顺序:核心功能参数(如
onTap、data)在前,样式参数(如color、size)在后;
// 正确 const CustomButton({ super.key, required this.onTap, // 必填参数在前 this.text = '', this.color = Colors.blue, // 样式参数在后 this.radius = 8.0, }); - Widget 构造函数仅使用命名参数(
4.3 语法规范(强制)
- Null Safety 强制启用:
- 禁止使用
dynamic类型(除非与第三方库兼容必须使用); - 可空类型必须显式声明(
String?),非空类型必须初始化(或用late延迟初始化); - 避免滥用
!空断言(优先用??、?.、if (xxx != null)安全访问);
// 错误 String name; // 非空类型未初始化 print(user!.name); // 滥用 ! 断言 // 正确 String? name; print(user?.name ?? '默认名称'); // 安全访问+默认值 - 禁止使用
- 集合使用:
- 优先使用不可变集合(
const List、const Map),可变集合需显式声明var或List<T>; - 禁止使用
List()无参构造,用[]替代(更简洁);
- 优先使用不可变集合(
- 循环与条件:
- 简单条件用
??、?.、if-else,复杂条件提取为布尔变量(提升可读性); - 列表遍历优先用
forEach(简单逻辑)或map(转换场景),避免嵌套循环;
- 简单条件用
- 扩展方法:
- 复用逻辑优先用
extension扩展(如StringExtension、DateTimeExtension),替代工具类静态方法;
// 正确(扩展方法) extension StringExtension on String { String capitalize() => isEmpty ? this : '${this[0].toUpperCase()}${substring(1)}'; } // 使用 'hello'.capitalize(); // 比 StringUtils.capitalize('hello') 更简洁 - 复用逻辑优先用
4.4 注释规范(强制+建议)
- 文档注释(强制):
- 公开类、函数、常量必须加文档注释(
///开头,支持 DartDoc 生成文档); - 注释需说明 功能用途 + 参数含义 + 返回值 + 异常场景(必要时);
/// 自定义按钮组件 /// /// [onTap]:点击回调(必填) /// [text]:按钮文本(默认空字符串) /// [color]:按钮背景色(默认蓝色) /// 返回:无 const CustomButton({ super.key, required this.onTap, this.text = '', this.color = Colors.blue, }); - 公开类、函数、常量必须加文档注释(
- 单行注释(建议):
- 复杂逻辑、特殊处理(如兼容适配、临时方案)需加单行注释(
//开头); - 注释说明 为什么这么做,而非 做了什么(代码本身应体现做了什么);
- 复杂逻辑、特殊处理(如兼容适配、临时方案)需加单行注释(
- 注释禁忌:
- 禁止注释无用代码(直接删除,版本控制可回溯);
- 禁止冗余注释(如
// 定义名称变量→String name;)。
五、状态管理规范(强制+建议)
5.1 方案选择(建议)
根据项目复杂度选择合适的状态管理方案,团队统一使用一种核心方案:
| 项目规模 | 推荐方案 | 适用场景 |
|---|---|---|
| 小型项目 | StatefulWidget + setState | 仅需局部状态(如单个页面开关、计数器) |
| 中型项目 | Provider/Riverpod | 跨组件状态共享(如用户信息、主题) |
| 当前项目 | GetX(推荐) | 快速开发,简化模板代码 |
5.2 分层原则(强制)
无论使用哪种方案,必须遵循 UI 层-状态层-数据层 分离:
- UI 层(Widget):仅负责渲染,不包含业务逻辑,通过状态层获取数据;
- 状态层(Controller/Bloc/Provider):管理状态,处理业务逻辑,不直接操作数据源;
- 数据层(Repository/API):负责数据获取(网络、本地存储),提供统一接口给状态层;
5.3 状态管理禁忌(强制)
- 禁止在 Widget 中直接调用网络/存储接口(必须通过数据层);
- 禁止全局状态滥用(优先使用局部状态,跨组件共享才用全局状态);
- 禁止状态层持有 Widget 引用(如
BuildContext),通过回调/事件通信; - GetX 规范:
- 控制器类名后缀
Controller(如UserController); - 一个控制器对应一个业务场景,避免超大控制器(拆分多个子控制器);
- 响应式变量使用
.obs后缀,通过Obx()或GetX()监听;
- 控制器类名后缀
六、GetX使用规范(强制+建议)
6.1 控制器规范
-
控制器命名与位置:
- 控制器类名后缀必须为
Controller(如UserController); - 控制器文件存放在对应模块的目录下,建议与页面文件同级或单独
controllers/目录; - 每个页面/功能对应一个控制器,避免超大控制器;
// pages/user/user_controller.dart class UserController extends GetxController { // 状态变量 final RxString userName = ''.obs; final RxBool isLoading = false.obs; final RxList<User> userList = <User>[].obs; // 计算属性 String get formattedName => userName.value.toUpperCase(); // 生命周期 @override void onInit() { super.onInit(); loadUserData(); } @override void onClose() { // 清理资源 super.onClose(); } // 方法 Future<void> loadUserData() async { isLoading.value = true; try { final users = await userRepository.getUsers(); userList.assignAll(users); } catch (e) { Get.snackbar('错误', '加载用户数据失败'); } finally { isLoading.value = false; } } } - 控制器类名后缀必须为
-
状态管理:
- 使用
.obs创建响应式变量,变量名使用Rx前缀约定; - 私有状态使用
_前缀,公开状态提供 getter 方法; - 使用
update()方法触发 UI 更新(非响应式变量变更时);
class UserController extends GetxController { // 响应式变量 final RxInt _counter = 0.obs; int get counter => _counter.value; // 非响应式变量 String _searchKeyword = ''; void increment() { _counter.value++; } void updateKeyword(String keyword) { _searchKeyword = keyword; update(); // 手动触发更新 } } - 使用
6.2 路由管理规范
-
路由定义:
- 使用 GetX 的路由管理系统,集中管理路由;
- 路由名称使用小写+下划线格式,与页面路径对应;
// routes/app_routes.dart class AppRoutes { static const String splash = '/'; static const String home = '/home'; static const String login = '/login'; static const String userDetail = '/user/detail'; static final List<GetPage> pages = [ GetPage( name: splash, page: () => SplashScreen(), ), GetPage( name: home, page: () => HomeScreen(), transition: Transition.fadeIn, ), GetPage( name: userDetail, page: () => UserDetailScreen(), transition: Transition.rightToLeft, ), ]; } -
路由跳转:
- 使用 GetX 提供的路由方法,保持统一;
- 带参数跳转时使用
arguments传递对象;
// 页面跳转 Get.toNamed(AppRoutes.home); Get.offNamed(AppRoutes.login); // 关闭当前页面 Get.offAllNamed(AppRoutes.home); // 关闭所有页面 // 带参数跳转 Get.toNamed( AppRoutes.userDetail, arguments: User(id: 1, name: '张三'), parameters: {'from': 'home'}, // URL参数 ); // 获取参数 final user = Get.arguments as User; final from = Get.parameters['from'];
6.3 依赖管理规范
- 依赖注入:
- 使用
Get.put()或Get.lazyPut()进行依赖注入; - 在控制器构造函数中注入依赖;
class UserController extends GetxController { final UserRepository userRepository; UserController(this.userRepository); // 或者使用Get.find()延迟获取 UserRepository get repo => Get.find<UserRepository>(); } // 在页面中初始化 class UserPage extends StatelessWidget { final UserController controller = Get.put(UserController(UserRepository())); // ... } - 使用
6.4 Widget使用规范
-
响应式Widget:
- 使用
Obx()、GetX()或GetBuilder包裹响应式部分; - 最小化响应范围,避免整个页面重建;
class UserProfile extends StatelessWidget { final UserController controller = Get.find<UserController>(); @override Widget build(BuildContext context) { return Column( children: [ // 使用Obx响应式更新 Obx(() => Text( '用户数: ${controller.userCount.value}', style: TextStyle( color: controller.isLoading.value ? Colors.grey : Colors.black, ), )), // 使用GetBuilder手动更新 GetBuilder<UserController>( builder: (controller) { return Text('用户名: ${controller.userName}'); }, ), // 列表使用Obx Obx(() => ListView.builder( shrinkWrap: true, itemCount: controller.userList.length, itemBuilder: (context, index) { final user = controller.userList[index]; return UserItem(user: user); }, )), ], ); } } - 使用
-
页面Widget规范:
- 页面可以使用
GetView<T>获取控制器; - 使用
Get.put()初始化控制器;
// pages/user/user_screen.dart class UserScreen extends GetView<UserController> { const UserScreen({super.key}); @override Widget build(BuildContext context) { // 自动获取控制器 return Scaffold( appBar: AppBar( title: Obx(() => Text('用户(${controller.userCount})')), ), body: _buildUserList(), ); } Widget _buildUserList() { return RefreshIndicator( onRefresh: controller.loadUsers, child: Obx(() => ListView.builder( itemCount: controller.userList.length, itemBuilder: (context, index) { return UserItem(user: controller.userList[index]); }, )), ); } } - 页面可以使用
6.5 工具类使用规范
- GetX工具类:
- 统一使用 GetX 提供的工具方法;
- 禁止混用原生方法与 GetX 方法;
// 正确 Get.snackbar('标题', '消息内容'); Get.dialog(ConfirmDialog()); Get.bottomSheet(OptionsSheet()); Get.toNamed('/detail'); // 避免混用 // 错误:混用GetX和原生方法 showDialog(context: context, builder: ...); // 避免使用 Navigator.push(context, ...); // 避免使用
6.6 GetX最佳实践
-
性能优化:
- 使用
worker监听状态变化,执行副作用; - 使用
debounce、interval避免频繁更新;
class SearchController extends GetxController { final RxString searchKeyword = ''.obs; @override void onInit() { super.onInit(); // 监听搜索关键词变化,延迟500ms执行搜索 debounce( searchKeyword, (_) => performSearch(), time: Duration(milliseconds: 500), ); } void performSearch() { // 执行搜索逻辑 } } - 使用
-
错误处理:
- 统一使用 GetX 的错误处理机制;
- 全局异常捕获;
// 全局配置 void main() { // 全局错误处理 Get.config( enableLog: true, defaultPopGesture: true, defaultTransition: Transition.cupertino, ); // 异常处理 FlutterError.onError = (details) { Get.snackbar('错误', details.exception.toString()); }; runApp(MyApp()); }
七、属性访问控制规范(强制)
7.1 私有属性使用原则
- 能私有不公开:所有属性默认设置为私有,只有在确实需要外部访问时才提供公共访问方式;
- 最小权限原则:只暴露必要的最小接口,隐藏内部实现细节;
- 封装性原则:通过 getter/setter 方法控制属性的访问和修改;
7.2 私有属性定义规范
-
命名规则:私有属性以
_下划线开头,采用小驼峰命名法;// 正确 class UserController extends GetxController { // 私有属性 final RxString _userName = ''.obs; final List<User> _userList = []; int _pageIndex = 0; // 公共访问方式 String get userName => _userName.value; List<User> get userList => List.unmodifiable(_userList); // 返回不可修改副本 // 公共修改方式(需要时) void updateUserName(String name) { if (name.isNotEmpty) { _userName.value = name; } } } -
响应式变量的私有化:
class UserController extends GetxController { // 私有响应式变量 final RxInt _privateCounter = 0.obs; final RxList<User> _privateList = <User>[].obs; // 只读公共访问 int get counter => _privateCounter.value; List<User> get userList => _privateList.toList(); // 返回副本 // 受控的修改方法 void incrementCounter() { if (_privateCounter.value < 100) { _privateCounter.value++; } } void addUser(User user) { if (!_privateList.contains(user)) { _privateList.add(user); } } }
7.3 不同场景下的属性访问控制
| 场景 | 推荐做法 | 示例 |
|---|---|---|
| 内部状态 | 完全私有,不提供外部访问 | int _internalState = 0; |
| 只读状态 | 私有变量 + 公共getter | final _data = Rx<Data>();Data get data => _data.value; |
| 受控修改 | 私有变量 + 公共修改方法 | void updateData(Data newData) { ... } |
| 完全公开 | 公共变量(极少情况) | final isLoading = false.obs; |
7.4 GetX控制器中的属性封装
class ProductController extends GetxController {
// 1. 完全私有,仅内部使用
final List<Product> _allProducts = [];
DateTime _lastUpdateTime = DateTime.now();
// 2. 响应式状态,只读外部访问
final RxList<Product> _filteredProducts = <Product>[].obs;
final RxBool _isLoading = false.obs;
// 公共只读访问
List<Product> get products => _filteredProducts.toList();
bool get isLoading => _isLoading.value;
bool get hasProducts => _filteredProducts.isNotEmpty;
// 3. 计算属性
double get totalPrice {
return _filteredProducts.fold(0, (sum, product) => sum + product.price);
}
// 4. 受控的修改方法
void addProduct(Product product) {
if (!_allProducts.contains(product)) {
_allProducts.add(product);
_applyFilters(); // 内部方法,不公开
update();
}
}
void removeProduct(Product product) {
_allProducts.remove(product);
_applyFilters();
update();
}
// 5. 私有内部方法
void _applyFilters() {
// 内部过滤逻辑
_filteredProducts.value = _allProducts.where(_filterCondition).toList();
}
bool _filterCondition(Product product) {
// 内部过滤条件
return product.isActive && product.price > 0;
}
// 6. 异步操作封装
Future<void> loadProducts() async {
_isLoading.value = true;
try {
final products = await ProductRepository.fetchProducts();
_allProducts.clear();
_allProducts.addAll(products);
_applyFilters();
} catch (e) {
Get.snackbar('错误', '加载产品失败: $e');
} finally {
_isLoading.value = false;
}
}
}
7.5 Widget中的属性封装
class ProductCard extends StatelessWidget {
final Product product;
final VoidCallback onTap;
// 私有计算属性
Color get _cardColor => product.isFeatured ? Colors.amber[50]! : Colors.white;
String get _priceText => '¥${product.price.toStringAsFixed(2)}';
const ProductCard({
super.key,
required this.product,
required this.onTap,
});
@override
Widget build(BuildContext context) {
return Card(
color: _cardColor,
child: InkWell(
onTap: onTap,
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 使用私有方法构建UI部分
_buildHeader(),
const SizedBox(height: 8),
_buildPrice(),
const SizedBox(height: 8),
_buildTags(),
],
),
),
),
);
}
// 私有构建方法
Widget _buildHeader() {
return Row(
children: [
Expanded(
child: Text(
product.name,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
if (product.isFeatured) _buildFeaturedBadge(),
],
);
}
Widget _buildPrice() {
return Text(
_priceText,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Theme.of(context).primaryColor,
),
);
}
Widget _buildTags() {
return Wrap(
spacing: 4,
children: product.tags.map((tag) => _buildTag(tag)).toList(),
);
}
Widget _buildTag(String tag) {
return Chip(
label: Text(tag),
backgroundColor: Colors.grey[200],
visualDensity: VisualDensity.compact,
);
}
Widget _buildFeaturedBadge() {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
decoration: BoxDecoration(
color: Colors.orange,
borderRadius: BorderRadius.circular(4),
),
child: const Text(
'推荐',
style: TextStyle(
color: Colors.white,
fontSize: 10,
fontWeight: FontWeight.bold,
),
),
);
}
}
7.6 检查清单
在编写代码时,请检查以下问题:
- ✅ 这个属性是否真的需要被外部访问?
- ✅ 如果不需要,是否已设置为私有(
_开头)? - ✅ 如果需要只读访问,是否提供了getter方法?
- ✅ 如果需要修改,是否提供了受控的修改方法?
- ✅ 集合类型返回时,是否返回了不可修改的副本?
- ✅ 是否避免了直接暴露内部可变状态?
八、性能优化规范(强制)
8.1 渲染优化
- 列表优化:
- 长列表必须使用
ListView.builder/GridView.builder(懒加载),禁止使用ListView(children: [...])(一次性加载所有子项); - 列表项高度固定时,设置
itemExtent提升性能;
- 长列表必须使用
- 图片优化:
- 图片资源按分辨率适配(
mipmap-mdpi/mipmap-hdpi等),避免使用超大图; - 网络图片使用缓存(如
cached_network_image),设置占位图/错误图; - 本地图片使用
AssetImage而非FileImage,并通过常量引用;
- 图片资源按分辨率适配(
- 减少重绘:
- 频繁变化的 Widget 用
RepaintBoundary包裹(隔离重绘区域); - 避免在
AnimatedBuilder外部构建无关 Widget;
- 频繁变化的 Widget 用
8.2 内存优化
- 资源释放:
- 订阅流(Stream)、控制器必须在
dispose或onClose中取消订阅; - 大文件(如视频、音频)使用后及时释放,避免内存泄漏;
- 订阅流(Stream)、控制器必须在
- 避免内存泄漏场景:
- 禁止匿名函数/闭包持有
BuildContext长期引用(如异步回调中); - 禁止静态变量持有 Widget/State 实例;
- 禁止匿名函数/闭包持有
8.3 代码执行优化
- 异步操作:
- 网络请求、文件读写必须用异步(
async/await),禁止同步阻塞; - 多个独立异步任务用
Future.wait并行执行(而非串行await);
- 网络请求、文件读写必须用异步(
- 计算优化:
- 复杂计算(如数据解析、加密)放在
isolate中执行(避免阻塞 UI 线程); - 重复计算结果缓存(如
final变量、memoizer工具)。
- 复杂计算(如数据解析、加密)放在
九、资源与配置规范(强制)
9.1 静态资源管理
- 图片:
- 存放路径:
assets/images/(按模块拆分); - 引用方式:通过常量类封装,禁止硬编码路径;
// const/resource.dart 或专门的 images.dart class AppImages { static const String userAvatar = 'assets/images/user/avatar.png'; static const String iconHome = 'assets/images/home/icon_home.png'; } // 使用 Image.asset(AppImages.userAvatar); - 存放路径:
- 字符串:
- 存放路径:通过国际化文件管理;
- 禁止硬编码字符串(便于统一修改和国际化);
- 颜色/尺寸:
- 颜色:通过
style/c_color.dart管理; - 尺寸:统一间距、字体大小,避免魔法数字;
- 颜色:通过
9.2 路由管理
- 路由配置:
- 集中管理路由表(
routes/app_routes.dart),使用命名路由; - 路由名称格式:
/模块/页面(如/user/login、/course/detail);
- 集中管理路由表(
- 路由跳转:
- 使用封装后的路由工具,统一处理动画、参数传递;
- 路由参数传递:优先使用强类型,避免
Map动态类型;
9.3 依赖管理
pubspec.yaml规范:- 依赖分组(
dependencies/dev_dependencies),并按功能排序; - 版本约束:核心依赖指定稳定版本,避免使用
any; - 定期更新依赖,并移除未使用的依赖;
- 依赖分组(
- 禁止重复依赖:如已使用
dio,禁止再引入http库;
十、规范落地与维护
- 定期复盘:每季度更新规范(适配 Flutter 新版本特性),团队内部分享违规案例与优化方案;
- 新人培训:将规范纳入新人入职培训,配套示例项目(展示规范落地效果)。