Flutter 开发代码规范(优化完善版)

Flutter 开发代码规范(优化完善版)

📋 目录

一、规范目标

  1. 统一团队开发风格,降低协作成本,提升代码可读性、可维护性;
  2. 规避常见性能问题,保障应用流畅性与稳定性;
  3. 符合 Dart/Flutter 官方最佳实践,兼容生态工具链(静态分析、测试、文档生成);
  4. 明确边界规则(强制约束+建议规范),平衡规范严谨性与开发效率。

二、目录结构规范(强制)

基于 Flutter 工程特性,采用 功能+分层 混合结构,核心目录集中在 lib/ 下。

项目结构示例

perl 复制代码
lib/
├── app/                  # 应用入口与全局配置
│   ├── app.dart          # 根组件(MyApp)
│   ├── routes.dart       # 路由配置(路由表+守卫)
│   ├── theme.dart        # 主题配置(亮色/暗色模式、全局样式)
│   └── config.dart       # 全局常量(环境变量、接口域名、第三方密钥)
├── core/                 # 核心能力层(通用业务+基础依赖)
│   ├── network/          # 网络请求(封装、拦截器、接口定义)
│   │   ├── api.dart      # 接口方法(按业务模块拆分,如 user_api.dart)
│   │   ├── client.dart   # dio 实例封装(拦截器、超时配置)
│   │   └── model/        # 接口模型(请求/响应实体,用 freezed/json_serializable)
│   ├── storage/          # 本地存储(统一封装)
│   │   ├── sp_utils.dart # SharedPreferences 封装
│   │   └── hive_utils.dart # Hive 数据库封装
│   ├── state/            # 全局状态管理(如 AppBloc、UserProvider)
│   └── utils/            # 工具类(无业务依赖)
│       ├── log_utils.dart # 日志工具(替代 print)
│       ├── toast_utils.dart # 提示工具
│       └── date_utils.dart # 日期工具
├── features/             # 业务功能模块(按功能拆分,内部自治)
│   ├── home/             # 首页模块
│   │   ├── presentation/ # UI 层(页面+组件)
│   │   │   ├── home_page.dart # 页面组件
│   │   │   └── widgets/  # 模块内私有组件
│   │   ├── state/        # 模块内状态(如 HomeBloc、HomeProvider)
│   │   └── domain/       # 业务逻辑(实体、仓库接口)
│   └── user/             # 用户模块(登录/注册/个人中心)
│       ├── presentation/
│       ├── state/
│       └── domain/
├── common/               # 全局公共资源
│   ├── widgets/          # 全局复用组件(如 Button、Input、EmptyView)
│   ├── res/              # 静态资源(集中管理,避免硬编码)
│   │   ├── images.dart   # 图片路径常量(AssetImage 封装)
│   │   ├── strings.dart  # 字符串常量(支持国际化)
│   │   ├── colors.dart   # 颜色常量(主题色+功能色)
│   │   └── dimens.dart   # 尺寸常量(间距、字体大小)
│   └── styles/           # 全局样式(文本样式、圆角、阴影)
└── main.dart             # 程序入口(仅初始化 App 根组件)

目录规范说明

  1. 禁止lib/ 根目录直接存放业务代码(仅保留 main.dart);
  2. features/ 按业务模块拆分,模块内遵循 UI-状态-业务 分层,确保高内聚低耦合;
  3. 公共组件/工具需区分 全局(common)模块私有(features/xxx/widgets),避免滥用全局;
  4. 静态资源(图片、字符串等)必须通过常量类引用,禁止硬编码 (如 Image.asset('images/icon.png')Image.asset(Images.icon))。

三、命名规范(强制)

完全遵循 Dart 官方命名约定,补充 Flutter 特有场景规则:

类型 命名规则 示例 禁止示例
类(Class/Widget) 大驼峰(PascalCase),名词/名词短语 HomePageCustomButtonUserModel home_pagecustom_button
函数/方法 小驼峰(camelCase),动词/动词短语 getUserInfo()showToast() get_user_info()ShowToast()
变量/参数 小驼峰(camelCase),名词/名词短语 userNameisLoginitemCount user_nameIsLogin
常量(const/final) 全大写+下划线(UPPER_SNAKE_CASE) const MAX_COUNT = 10;final API_BASE_URL = 'xxx' maxCountapiBaseUrl
枚举(enum) 枚举名:大驼峰;枚举值:小驼峰 enum LoginType { phone, wechat, qq } enum login_type { Phone, Wechat }
路由名称 小写+下划线,格式:/模块/页面 /user/login/home/index /User/LoginhomeLogin
文件名 小写+下划线(snake_case),与类名对应 home_page.dart(对应 HomePage HomePage.darthomePage.dart
包名(package) 小写+下划线,全小写无大写 package:my_app/features/user package:MyApp/Features/User

特殊命名补充

  1. 布尔变量 建议前缀:ishascanshould(如 isVisiblehasPermission);
  2. 回调函数 命名:后缀 Callback,如 OnLoginCallbackOnItemClickCallback
  3. 状态管理 相关:Bloc 类名后缀 Bloc(如 CounterBloc),状态类后缀 State(如 CounterState);
  4. GetX控制器 :类名后缀 Controller(如 HomeControllerUserController);
  5. 避免使用缩写 (除非是通用缩写如 APIUI),禁止拼音 命名(如 shouyePagehomePage)。

四、代码风格规范(强制+建议)

4.1 基础格式(强制)

  1. 缩进:2 个空格(Dart 官方标准),禁止使用 Tab;

  2. 行宽:单行长不超过 120 字符(超出需换行),IDE 开启自动换行;

  3. 空行

    • 类/函数之间留 1 个空行;
    • 函数内部逻辑块之间留 1 个空行;
    • 避免连续空行(最多 1 个);
  4. 括号

    • 类、函数、条件语句的左括号与声明在同一行,右括号单独成行;
    • 单行条件语句可省略括号(但逻辑复杂时必须加);
    dart 复制代码
    // 正确
    if (isLogin) {
      navigateToHome();
    }
    
    // 允许(单行简单逻辑)
    if (isLogin) navigateToHome();
    
    // 错误
    if (isLogin)
    {
      navigateToHome();
    }
  5. 分号:每条语句必须加分号(Dart 不是 JavaScript);

  6. 逗号 :对象/数组最后一个元素后建议加逗号(IDE 自动格式化时更整洁);

    dart 复制代码
    final user = User(
      name: '张三',
      age: 20, // 末尾逗号建议保留
    );

4.2 Widget 开发规范(强制)

  1. 优先使用 StatelessWidget:无状态组件性能更优,仅当需要维护状态时使用 StatefulWidget;

  2. Widget 拆分原则

    • 单个 Widget 代码不超过 100 行,超出则拆分为子 Widget(私有组件放 widgets/ 目录);
    • 重复出现的 UI 片段(如列表项、按钮)必须提取为公共组件;
    • 避免嵌套过深(建议不超过 4 层),使用 Padding/SizedBox 替代嵌套 Container,用 Wrap 替代多层 Row/Column
    dart 复制代码
    // 错误(嵌套过深)
    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);
  3. build 方法规范

    • 禁止在 build 中执行耗时操作(如网络请求、数据库查询、复杂计算);
    • 禁止在 build 中创建临时对象(如 ListMapStyle),需缓存为 final 变量或提取为常量;
    dart 复制代码
    // 错误(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);
    }
  4. const 构造函数

    • 无状态 Widget 且构造参数均为常量时,必须使用 const 构造函数(减少重建开销);
    dart 复制代码
    // 正确
    const CustomButton({super.key, required this.onTap});
    
    // 调用时也需加 const(除非父组件非 const)
    const CustomButton(onTap: _handleTap);
  5. 命名参数

    • Widget 构造函数仅使用命名参数({} 包裹),必填参数加 required 关键字;
    • 参数顺序:核心功能参数(如 onTapdata)在前,样式参数(如 colorsize)在后;
    dart 复制代码
    // 正确
    const CustomButton({
      super.key,
      required this.onTap, // 必填参数在前
      this.text = '',
      this.color = Colors.blue, // 样式参数在后
      this.radius = 8.0,
    });

4.3 语法规范(强制)

  1. Null Safety 强制启用

    • 禁止使用 dynamic 类型(除非与第三方库兼容必须使用);
    • 可空类型必须显式声明(String?),非空类型必须初始化(或用 late 延迟初始化);
    • 避免滥用 ! 空断言(优先用 ???.if (xxx != null) 安全访问);
    dart 复制代码
    // 错误
    String name; // 非空类型未初始化
    print(user!.name); // 滥用 ! 断言
    
    // 正确
    String? name;
    print(user?.name ?? '默认名称'); // 安全访问+默认值
  2. 集合使用

    • 优先使用不可变集合(const Listconst Map),可变集合需显式声明 varList<T>
    • 禁止使用 List() 无参构造,用 [] 替代(更简洁);
  3. 循环与条件

    • 简单条件用 ???.if-else,复杂条件提取为布尔变量(提升可读性);
    • 列表遍历优先用 forEach(简单逻辑)或 map(转换场景),避免嵌套循环;
  4. 扩展方法

    • 复用逻辑优先用 extension 扩展(如 StringExtensionDateTimeExtension),替代工具类静态方法;
    dart 复制代码
    // 正确(扩展方法)
    extension StringExtension on String {
      String capitalize() => isEmpty ? this : '${this[0].toUpperCase()}${substring(1)}';
    }
    
    // 使用
    'hello'.capitalize(); // 比 StringUtils.capitalize('hello') 更简洁

4.4 注释规范(强制+建议)

  1. 文档注释(强制)

    • 公开类、函数、常量必须加文档注释(/// 开头,支持 DartDoc 生成文档);
    • 注释需说明 功能用途 + 参数含义 + 返回值 + 异常场景(必要时);
    dart 复制代码
    /// 自定义按钮组件
    /// 
    /// [onTap]:点击回调(必填)
    /// [text]:按钮文本(默认空字符串)
    /// [color]:按钮背景色(默认蓝色)
    /// 返回:无
    const CustomButton({
      super.key,
      required this.onTap,
      this.text = '',
      this.color = Colors.blue,
    });
  2. 单行注释(建议)

    • 复杂逻辑、特殊处理(如兼容适配、临时方案)需加单行注释(// 开头);
    • 注释说明 为什么这么做 ,而非 做了什么(代码本身应体现做了什么);
  3. 注释禁忌

    • 禁止注释无用代码(直接删除,版本控制可回溯);
    • 禁止冗余注释(如 // 定义名称变量String name;)。

五、状态管理规范(强制+建议)

5.1 方案选择(建议)

根据项目复杂度选择合适的状态管理方案,团队统一使用一种核心方案:

项目规模 推荐方案 适用场景
小型项目 StatefulWidget + setState 仅需局部状态(如单个页面开关、计数器)
中型项目 Provider/Riverpod 跨组件状态共享(如用户信息、主题)
大型项目 Bloc/Cubit 复杂业务逻辑(如表单提交、流程控制)
快速迭代 GetX(推荐使用) 个人项目或小团队,需简化模板代码

5.2 分层原则(强制)

无论使用哪种方案,必须遵循 UI 层-状态层-数据层 分离:

  1. UI 层(Widget):仅负责渲染,不包含业务逻辑,通过状态层获取数据;
  2. 状态层(Bloc/Provider/Controller):管理状态,处理业务逻辑,不直接操作数据源;
  3. 数据层(Repository/API):负责数据获取(网络、本地存储),提供统一接口给状态层;

5.3 状态管理禁忌(强制)

  1. 禁止在 Widget 中直接调用网络/存储接口(必须通过数据层);
  2. 禁止全局状态滥用(优先使用局部状态,跨组件共享才用全局状态);
  3. 禁止状态层持有 Widget 引用(如 BuildContext),通过回调/事件通信;
  4. Bloc 规范
    • 事件(Event)命名后缀 Event(如 LoginEvent),状态(State)命名后缀 State
    • 一个 Bloc 对应一个业务场景,避免超大 Bloc(拆分多个子 Bloc);
    • 状态必须是不可变的(使用 freezed 生成不可变类)。

六、GetX使用规范(强制+建议)

6.1 项目结构适配

当使用GetX时,建议在features/模块内调整目录结构,遵循GetX推荐的三层架构:

bash 复制代码
features/user/
├── data/                  # 数据层
│   ├── models/           # 数据模型
│   ├── repositories/     # 数据仓库
│   └── datasources/      # 数据源(本地/远程)
├── domain/               # 业务逻辑层
│   ├── usecases/        # 用例
│   └── repositories/    # 仓库接口
└── presentation/         # 表现层
    ├── controllers/     # 控制器
    ├── views/          # 页面视图
    ├── widgets/        # 私有组件
    └── bindings/       # 绑定类

6.2 Controller规范

  1. 控制器命名与位置

    • 控制器类名后缀必须为Controller(如UserController);
    • 控制器文件存放在对应模块的presentation/controllers/目录;
    • 每个页面/功能对应一个控制器,避免超大控制器;
    dart 复制代码
    // features/user/presentation/controllers/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;
        }
      }
    }
  2. 状态管理

    • 使用.obs创建响应式变量,变量名使用Rx前缀约定;
    • 私有状态使用_前缀,公开状态提供getter方法;
    • 使用update()方法触发UI更新(非响应式变量变更时);
    dart 复制代码
    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.3 路由管理规范

  1. 路由定义

    • 使用GetX的路由管理系统,集中管理路由;
    • 路由名称使用小写+下划线格式,与页面路径对应;
    dart 复制代码
    // 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(),
          binding: SplashBinding(),
        ),
        GetPage(
          name: home,
          page: () => HomeScreen(),
          bindings: [HomeBinding(), UserBinding()],
          transition: Transition.fadeIn,
        ),
        GetPage(
          name: userDetail,
          page: () => UserDetailScreen(),
          binding: UserDetailBinding(),
          transition: Transition.rightToLeft,
        ),
      ];
    }
  2. 路由跳转

    • 使用GetX提供的路由方法,保持统一;
    • 带参数跳转时使用arguments传递对象;
    dart 复制代码
    // 页面跳转
    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.4 依赖管理规范

  1. Binding类使用

    • 每个控制器对应一个Binding类,处理依赖注入;
    • Binding类名后缀为Binding,与控制器对应;
    dart 复制代码
    // features/user/presentation/bindings/user_binding.dart
    class UserBinding extends Bindings {
      @override
      void dependencies() {
        // 懒加载注入
        Get.lazyPut<UserRepository>(
          () => UserRepositoryImpl(Get.find<ApiClient>()),
          fenix: true, // 页面关闭后不会被销毁,下次进入时重用
        );
        
        // 立即注入
        Get.put<UserController>(
          UserController(Get.find<UserRepository>()),
          permanent: true, // 全局单例
        );
      }
    }
  2. 依赖获取

    • 使用Get.find<T>()获取依赖,避免直接new对象;
    • 在控制器构造函数中注入依赖;
    dart 复制代码
    class UserController extends GetxController {
      final UserRepository userRepository;
      
      UserController(this.userRepository);
      
      // 或者使用Get.find()延迟获取
      UserRepository get repo => Get.find<UserRepository>();
    }

6.5 Widget使用规范

  1. 响应式Widget

    • 使用Obx()GetX()GetBuilder包裹响应式部分;
    • 最小化响应范围,避免整个页面重建;
    dart 复制代码
    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);
              },
            )),
          ],
        );
      }
    }
  2. 页面Widget规范

    • 页面继承GetView<T>获取控制器;
    • 使用Get.put()初始化控制器;
    dart 复制代码
    // features/user/presentation/views/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: controller.obx(
            (state) => _buildUserList(),
            onLoading: const CircularProgressIndicator(),
            onError: (error) => ErrorView(error: error),
            onEmpty: const EmptyView(),
          ),
        );
      }
      
      Widget _buildUserList() {
        return RefreshIndicator(
          onRefresh: controller.loadUsers,
          child: ListView.builder(
            itemCount: controller.userList.length,
            itemBuilder: (context, index) {
              return UserItem(user: controller.userList[index]);
            },
          ),
        );
      }
    }

6.6 工具类使用规范

  1. GetX工具类

    • 统一使用GetX提供的工具方法;
    • 禁止混用原生方法与GetX方法;
    dart 复制代码
    // 正确
    Get.snackbar('标题', '消息内容');
    Get.dialog(ConfirmDialog());
    Get.bottomSheet(OptionsSheet());
    Get.toNamed('/detail');
    
    // 避免混用
    // 错误:混用GetX和原生方法
    showDialog(context: context, builder: ...); // 避免使用
    Navigator.push(context, ...); // 避免使用
  2. 国际化

    • 使用GetX的国际化方案;
    • 统一管理翻译文件;
    dart 复制代码
    // 使用
    Text('title'.tr);
    Text('greeting'.trParams({'name': '张三'}));
    
    // 切换语言
    Get.updateLocale(Locale('zh', 'CN'));

6.7 GetX最佳实践

  1. 性能优化

    • 使用worker监听状态变化,执行副作用;
    • 使用debounceinterval避免频繁更新;
    dart 复制代码
    class SearchController extends GetxController {
      final RxString searchKeyword = ''.obs;
      
      @override
      void onInit() {
        super.onInit();
        
        // 监听搜索关键词变化,延迟500ms执行搜索
        debounce(
          searchKeyword,
          (_) => performSearch(),
          time: Duration(milliseconds: 500),
        );
        
        // 每隔1秒执行一次
        interval(
          searchKeyword,
          (_) => logSearch(),
          time: Duration(seconds: 1),
        );
      }
      
      void performSearch() {
        // 执行搜索逻辑
      }
    }
  2. 状态持久化

    • 使用GetStorage进行轻量级数据持久化;
    • 重要数据使用shared_preferenceshive
    dart 复制代码
    final box = GetStorage();
    
    // 保存数据
    box.write('user_token', 'abc123');
    
    // 读取数据
    String token = box.read('user_token');
    
    // 监听数据变化
    box.listen(() {
      print('数据发生变化');
    });
  3. 错误处理

    • 统一使用GetX的错误处理机制;
    • 全局异常捕获;
    dart 复制代码
    // 全局配置
    void main() {
      // 全局错误处理
      Get.config(
        enableLog: true,
        defaultPopGesture: true,
        defaultTransition: Transition.cupertino,
      );
      
      // 异常处理
      FlutterError.onError = (details) {
        Get.snackbar('错误', details.exception.toString());
      };
      
      runApp(MyApp());
    }

七、性能优化规范(强制)

7.1 渲染优化

  1. 列表优化
    • 长列表必须使用 ListView.builder/GridView.builder(懒加载),禁止使用 ListView(children: [...])(一次性加载所有子项);
    • 列表项高度固定时,设置 itemExtent 提升性能;
  2. 图片优化
    • 图片资源按分辨率适配(mipmap-mdpi/mipmap-hdpi 等),避免使用超大图;
    • 网络图片使用缓存(如 cached_network_image),设置占位图/错误图;
    • 本地图片使用 AssetImage 而非 FileImage,并通过 images.dart 常量引用;
  3. 减少重绘
    • 频繁变化的 Widget 用 RepaintBoundary 包裹(隔离重绘区域);
    • 避免在 AnimatedBuilder 外部构建无关 Widget;

7.2 内存优化

  1. 资源释放
    • 订阅流(Stream)、Bloc 必须在 dispose 中取消订阅(使用 cancel()BlocListener 自动管理);
    • 大文件(如视频、音频)使用后及时释放,避免内存泄漏;
  2. 避免内存泄漏场景
    • 禁止匿名函数/闭包持有 BuildContext 长期引用(如异步回调中);
    • 禁止静态变量持有 Widget/State 实例;

7.3 代码执行优化

  1. 异步操作
    • 网络请求、文件读写必须用异步(async/await),禁止同步阻塞;
    • 多个独立异步任务用 Future.wait 并行执行(而非串行 await);
  2. 计算优化
    • 复杂计算(如数据解析、加密)放在 isolate 中执行(避免阻塞 UI 线程);
    • 重复计算结果缓存(如 final 变量、memoizer 工具)。

八、资源与配置规范(强制)

8.1 静态资源管理

  1. 图片

    • 存放路径:assets/images/(按模块拆分,如 assets/images/user/);
    • 引用方式:通过 common/res/images.dart 常量封装,禁止硬编码路径;
    dart 复制代码
    // common/res/images.dart
    class Images {
      static const String userAvatar = 'assets/images/user/avatar.png';
      static const String iconHome = 'assets/images/home/icon_home.png';
    }
    
    // 使用
    Image.asset(Images.userAvatar);
  2. 字符串

    • 存放路径:common/res/strings.dart(支持国际化时用 flutter_localizations);
    • 禁止硬编码字符串(便于统一修改和国际化);
  3. 颜色/尺寸

    • 颜色:区分主题色(primaryColor)、功能色(successColor/errorColor),禁止直接使用 Colors.blue(统一维护);
    • 尺寸:统一间距(p8/p16)、字体大小(font14/font18),避免魔法数字;

8.2 路由管理

  1. 路由配置

    • 集中管理路由表(app/routes.dart),使用命名路由(禁止直接 Navigator.push(Builder...));
    • 路由名称格式:/模块/页面(如 /user/login/order/detail);
    dart 复制代码
    // app/routes.dart
    class Routes {
      static const String home = '/home/index';
      static const String login = '/user/login';
      
      static final Map<String, WidgetBuilder> routes = {
        home: (context) => const HomePage(),
        login: (context) => const LoginPage(),
      };
    }
  2. 路由跳转

    • 使用封装后的路由工具(如 RouterUtils.push(context, Routes.login)),统一处理动画、参数传递;
    • 路由参数传递:优先使用强类型(如通过 ModalRoute.of(context)?.settings.arguments 封装模型),避免 Map 动态类型;
  3. 路由守卫

    • 全局路由守卫(如未登录拦截跳登录页)统一在 MaterialApp.onGenerateRoute 中处理;

8.3 依赖管理

  1. pubspec.yaml 规范
    • 依赖分组(dependencies/dev_dependencies),并按功能排序(如网络、状态管理、工具类);
    • 版本约束:核心依赖(如 flutter_blocdio)指定稳定版本(如 ^8.1.0),避免使用 any
    • 定期更新依赖(flutter pub upgrade),并移除未使用的依赖(flutter pub clean + dart pub deps 检查);
  2. 禁止重复依赖:如已使用 dio,禁止再引入 http 库(统一网络请求工具)。

九、测试与质量规范(强制+建议)

9.1 测试规范(建议)

  1. 测试分层
    • 单元测试 :测试工具类、业务逻辑(如 utils/domain/ 层),覆盖率≥80%;
    • Widget 测试 :测试核心 UI 组件(如 common/widgets/),验证渲染正确性;
    • 集成测试:测试关键业务流程(如登录→首页→下单);
  2. 测试文件存放
    • 单元测试/Widget 测试:与被测试文件同目录,命名为 xxx_test.dart
    • 集成测试:放在 integration_test/ 目录下;

9.2 静态代码检查(强制)

  1. 启用 lint 规则

    • 基础规则:使用 flutter_lints(官方推荐);
    • 增强规则:集成 dart_code_metrics,自定义强制规则(如禁止 print、禁止 build 中创建对象);
  2. 配置文件 :在 analysis_options.yaml 中配置规则,示例:

    yaml 复制代码
    include: package:flutter_lints/flutter.yaml
    
    linter:
      rules:
        - avoid_print # 禁止使用 print(用 log_utils 替代)
        - prefer_const_constructors # 强制使用 const 构造函数
        - no_logic_in_create_state # 禁止在 createState 中写逻辑
        - unused_import # 禁止未使用的导入
    
    analyzer:
      exclude:
        - build/
        - .dart_tool/
  3. 提交前检查

    • 必须执行 flutter analyze(静态分析无错误);
    • 必须执行 flutter format .(自动格式化代码);

9.3 版本控制规范(强制)

  1. 分支管理
    • master:生产环境分支(仅合并 release/hotfix 分支);
    • feature:开发分支(日常开发、合并 feature 分支);
    • release/xxx:上线分支(从 master 拉出,完成后合并回 feature);
    • hotfix/xxx:紧急修复分支(从 master 拉出,修复后合并 master);
    • release/hotfix:分支发布成功后合并到 master
  2. 提交信息规范
    • 格式:类型: 描述(英文,首字母小写)
    • 类型:feat(新增功能)、fix(修复 bug)、docs(文档)、style(格式调整)、refactor(重构)、test(测试)、chore(依赖/构建调整);
    • 示例:feat: add login pagefix: resolve login button unclickable issue
  3. 提交粒度:单次提交仅包含一个功能/修复,代码量不超过 300 行(便于 Code Review)。

十、最佳实践(建议)

  1. 国际化 :使用 flutter_localizations + intl 库,字符串按语言拆分(strings_en.dart/strings_zh.dart);
  2. 主题适配 :支持深色模式,全局样式通过 Theme.of(context) 获取(如 Theme.of(context).primaryColor);
  3. 错误处理
    • 统一网络错误(如 401 未授权、500 服务器错误)拦截处理;
    • 全局异常捕获(FlutterError.onError + runZonedGuarded),收集崩溃日志;
  4. 代码复用
    • 公共逻辑提取为 mixin(如 ToastMixinLoadingMixin),避免继承冗余;
    • 表单验证逻辑封装为工具类(如 FormValidator.validatePhone());
  5. 工具推荐
    • 序列化:freezed + json_serializable(自动生成模型代码);
    • 路由:auto_route(类型安全路由,避免手动配置);
    • 日志:logger(替代 print,支持分级日志、格式化输出);
    • 代码生成:build_runner(自动化生成模板代码)。

十一、规范落地与维护

  1. 工具保障 :IDE 安装 Flutter/Dart 插件,开启自动格式化、lint 实时检查;
  2. CI/CD 集成 :提交代码时触发 CI 流程(执行 flutter analyzeflutter test),不通过则禁止合并;
  3. 定期复盘:每季度更新规范(适配 Flutter 新版本特性),团队内部分享违规案例与优化方案;
  4. 新人培训:将规范纳入新人入职培训,配套示例项目(展示规范落地效果)。
相关推荐
码农刚子10 小时前
字符串拼接用“+”还是 StringBuilder?别再凭感觉写了
后端·代码规范
程序员Ctrl喵14 小时前
异步编程:Event Loop 与 Isolate 的深层博弈
开发语言·flutter
前端不太难15 小时前
Flutter 如何设计可长期维护的模块边界?
flutter
小蜜蜂嗡嗡16 小时前
flutter列表中实现置顶动画
flutter
始持17 小时前
第十二讲 风格与主题统一
前端·flutter
始持17 小时前
第十一讲 界面导航与路由管理
flutter·vibecoding
始持17 小时前
第十三讲 异步操作与异步构建
前端·flutter
新镜17 小时前
【Flutter】 视频视频源横向、竖向问题
flutter
黄林晴18 小时前
Compose Multiplatform 1.10 发布:统一 Preview、Navigation 3、Hot Reload 三箭齐发
android·flutter
Swift社区18 小时前
Flutter 应该按功能拆,还是按技术层拆?
flutter