Flutter开发代码规范

851 阅读5分钟

Flutter代码规范

插件推荐

为保证开发一致性,使用Android Studio作为开发工具

  1. Dart
  2. Flutter
  3. Flutter Intl 国际化
  4. FlutterAssetsGenerator 生成资源索引
  5. JsonToDart (JSON To Dart) 生成模型
  6. GetX
  7. TinyPNG Image Optimizer 图片压缩
  8. Android Drawable Preview 图片图标预览
  9. Translation 翻译

代码规范

参考链接

  1. 来学习了解下Flutter及Dart 编码规范?

主要注意一下几点

  1. 命名库、包、目录、dart文件都应该是小写加上下划线
library peg_parser/source_scanner;
import 'file_system.dart';
  1. 类, 枚举, 类型定义, 以及泛型,都需要使用大写开头的驼峰命名法
class HttpRequest { ... }

typedef Predicate<T> = bool Function(T value);
  1. 变量名、方法、参数名都应该是小写开头的驼峰命名法
HttpRequest httpRequest;

void align(bool clearItems) {}

const defaultTimeout = 1000;

final urlScheme = RegExp('^([a-z]+):');
  1. 内部类、方法、参数使用_开头
class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }
}

代码检查lints

  1. 使用官方flutter_lintsanalysis_options.yaml文件配置,创建项目时会自动配置
  2. 编码过程中消除警告,查看Android Studio Dart Analysis 面板消除警告后,编码基本符合Flutter及Dart的规范

git

  1. 使用Git Flow流程管理代码分支 git flow

项目规范

包管理

第三方包引用

引入第三方包,添加注释说明及库地址。防止库自动更新版本,版本号前面不使用^。

#网络请求 https://github.com/flutterchina/dio/blob/master/README-ZH.md
dio: 4.0.3

项目包引用

引用

目前一些常用的包以及自定义组件,我们使用library.dart统一引入并导出

library library;

export 'dart:async';
export 'package:flutter/material.dart';
export 'package:flutter_screenutil/flutter_screenutil.dart';
export 'package:get/get.dart';
export 'package:common_utils/common_utils.dart';
...

减少包的引用,统一使用

import 'package:povison/common/library.dart';

类名冲突

import 'package:pokalive_flutter/common/library.dart' hide FormData;
import 'package:agora_rtc_engine/rtc_local_view.dart' as rtc_local_view;

项目框架结构

common公共基础包

utils工具类
net网络请求
net
├─net
│  ├─api   				配置请求url,状态码
│  ├─exceptions 		配置网络请求异常情况异常,如token已过期
│  ├─interceptors		配置网络拦截器,如抛出异常,baseUrl替换,显示Loading等功能
│  ├─options			配置网络请求额外的业务处理,如加解密、显示Toast/Loading
│  └─http.dart			基于Dio的包装类
ui
ui
├─style					颜色/主题
├─widgets				通用的widgets
│  ├─multi_state		多状态页面封装
│  └─refresh 			刷新列表封装

generated

自动生成的文件

generated 		
├─assets		 		资源文件
└─I10n					国际化文件

models

存放Json模型

router

router		
├─route					路由表配置
└─arguments				页面传参

ui

ui
├─pages					页面/逻辑
│  └─mian 				
│     ├─main_page		页面
│     └─main_logic 		logic
├─widgets				业务的widgets

资源处理

颜色/主题

class AppColors {
  static const Color background = Color(0xFF0E0F1A);
  static const Color transparent = Colors.transparent;
  static const Color white = Colors.white;
  static const Color black = Colors.black;

  static const Color c333333 = Color(0xFF333333);
 }

项目中使用

AppColors.c333333
    
//使用透明度
AppColors.c333333.withOpacity(0.5)

图片

图片生成

命名方式以对应功能/模块作为前缀 如 user_avater_cover

使用FlutterAssetsGenerator插件自动生成Assets.dart

assets
├─image
│  ├─xx_tab_bg.dart
│  └─xx_tab_icon.dart

需要在pubspec.yaml中配置目录

assets:
  - assets/img/

项目中使用

Assets.imgPhEmpty
图片压缩

使用TinyPNG Image Optimizer插件压缩图片,减少包体积

国际化

Flutter国际化 多语言 使用Flutter intl插件实现多语言

命名方式以对应功能/模块作为前缀 如 login_title

国际化配置文件intl_en.arb

{
  "ph_empty": "Nothing is here",
  "profile_call_coin": "{d} / Min",
  "gift_tab_gift": "Gift ( [0] 1 = [1] 1 )",
}
一般使用
"ph_empty": "Nothing is here",

getString().ph_empty
有参数使用
"profile_call_coin": "{d} / Min",

getString().profile_call_coin(user.audioAmt!);
Span

当字符出现占位时,使用SpanUtil进行处理

"gift_tab_gift": "Gift ( [0] 1 = [1] 1 )",

Text.rich(
	TextSpan(
      children: SpanUtil.formatRichText(
        source: getString().gift_tab_gift,
        spans: [
          WidgetSpan(
            child: Image.asset(Assets.userCoin, width: 10.w),
            alignment: PlaceholderAlignment.middle,
          ),
          WidgetSpan(
            child: Image.asset(Assets.closeHeart, width: 10.w),
            alignment: PlaceholderAlignment.middle,
          ),
        ],
      ),
    )
) ;

Json to Dart

使用JsonToDart (JSON To Dart)插件生成数据模型

一些组件的使用

状态管理Getx

相关资料

  1. Getx Doc

  2. Flutter GetX使用---简洁的魅力!

  3. GetX代码生成IDEA插件,超详细功能讲解(透过现象看本质)

  4. 【源码篇】Flutter GetX深度剖析 | 我们终将走出自己的路(万字图文)

目前项目主要使用GetX的状态管理和路由管理

路由管理

///路由配置
class RouteConfig {
  ///主页
  static const String main = "/main";

  static final List<GetPage> getPages = [
    GetPage(
      name: main,
      page: () => MainPage(),
    ),
  ];
}

//定义页面传参规范
class MainPageArguments {
  final String value;

  MainPageArguments(this.value);
}

页面跳转与参数传递,参数获取

var result = await Get.toNamed(RouteConfig.main,arguments: MainPageArguments("value"));
//do something


class OrderDetailLogic extends MultiStateLogic {
  var orderDetail = OrderDetail().obs;

  @override
  Future initData()  {
    if (Get.arguments is OrderDetailPageArguments) {
      OrderDetailPageArguments arguments = Get.arguments;
      //do something
    }
  }
}

状态管理

通过Getx插件生成一个文件夹,包含logic和page文件,分别处理逻辑和UI

pages
├─xx
│  ├─xx_logic.dart
│  └─xx_page.dart

页面展示

一般根据网络请求的成功与否,页面通常有Loding/Success/Empty/Error等状态,需要对页面结构封装,自动处理状态展示及点击重试

本项目使用了Getx的状态,可以实现状态页面懒加载,参考使用GetX构建更优雅的页面结构

一般页面

项目封装了MultiStateWidget作为页面状态的Widget,搭配MultiStateLogic使用

class OrderDetailPage extends StatelessWidget {
  final logic = Get.put(OrderDetailLogic());

  OrderDetailPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: const NaviAppBar(title: 'Order Detail'),
      backgroundColor: AppColors.cF9F9F9,
      body: MultiStateWidget(
        logic: logic,
        content: SingleChildScrollView(
          child: Obx(() {
            return Column(
              children: [
              ...
              ],
            );
          }),
        ),
      ),
    );
  } 
)

class OrderDetailLogic extends MultiStateLogic {
  var orderDetail = OrderDetail().obs;

  @override
  Future initData() async {
    if (Get.arguments is OrderDetailPageArguments) {
      OrderDetailPageArguments arguments = Get.arguments;
      orderDetail.value = await HttpManager.orderDetail(arguments.orderId);
    }
  }
}


列表页面

进一步封装,使用RefreshListViewRefreshListLogic

class HomePage extends StatelessWidget {
  final logic = Get.put(HomeLogic());

  HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return RefreshListView(
      logic: logic,
      itemBuilder: itemBuilder,
    );
  }
  
class HomeLogic extends RefreshListLogic<Showcase> {
  @override
  Future<List<Showcase>> load(int pageNo) async {
    return await HttpManager.getHome();
  }

  @override
  int get pageSize => 0;
}

网络请求

ApiUrl统一配置管理

class ApiUrl {
  static const baseUrl = Config.testUrl ? "https://test-api.pokalive.com/" : "https://www.povison.com/";

  ///登录
  static const login = 'front/member/login';
}

HttpManager管理相关的网络请求及Json转换

class HttpManager {
    static Future<List<Showcase>> getHome() async {
      var response = await Http().get(
        ApiUrl.home,
        options: HttpOptions.getOptions(showLoading: true),
      );
      HomeResult homeResult = HomeResult.fromMap(response.data);
      return homeResult.data;
    }
}

相关文档及博客

文档

  1. Dart 语言开发文档
  2. Flutter 开发文档
  3. Flutter实战·第二版
  4. GSY Flutter
  5. 控件介绍及使用_FlutterUnit

博客推荐

  1. 掘金Flutter
  2. 恋猫de小郭
  3. 张风捷特烈掘金小册
  4. 法的空间
  5. 岛上码农
  6. Nayuta