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. 新人培训:将规范纳入新人入职培训,配套示例项目(展示规范落地效果)。
相关推荐
西西学代码8 小时前
Flutter---CustomPaint
学习·flutter
火柴就是我9 小时前
flutter 实现文本贴图
flutter
Swift社区11 小时前
Flutter / RN / iOS 的 UI 渲染机制,本质差异在哪里?
flutter·ui·ios
小蜜蜂嗡嗡12 小时前
flutter可吸边且隐藏一半的拖拽悬浮按钮
flutter
AiFlutter13 小时前
三、内容展示(04):条形码
flutter·低代码平台·aiflutter·aiflutter低代码·flutter低代码开发
会煮咖啡的猫14 小时前
Cursor AI Skills 实战:自动生成 Flutter 页面、代码与文档
flutter·ai编程·cursor
ITKEY_14 小时前
flutter应用名称rename
flutter
恋猫de小郭15 小时前
Dart 官方再解释为什么放弃了宏编程,并转向优化 build_runner ? 和 Kotlin 的区别又是什么?
android·前端·flutter
ITKEY_15 小时前
flutter 如何设置app的图标?
flutter