flutter项目工程化探索
一、目录结构
│ main.dart 入口文件,全局配置(主题、响应式、标题等)
│ theme.dart 主题文件
│
└─app 项目主目录
├─api api层,配置ajax请求地址、方式
├─components 通用组件
├─data 本地数据存储
├─models model层,数据格式描述文件
├─modules view层,页面文件
│ ├─tabs 页面文件
│ │ ├─bindings controller注册文件。结合route building使用,路由进入时注册,跳出时注销
│ │ ├─controllers 页面逻辑。数据、生命周期、方法等
│ │ └─views 页面视图
│
├─request 网络层,拦截器、请求头、请求状态等统一封装
│
├─routes 路由层
│ app_pages.dart 配置路由信息,加载binding
│ app_routes.dart 注册路由
│
├─services services层,对api层请求的数据进行处理
│
├─controllers 全局数据存放,处理
│
└─utils 工具类
二、业务开发基本流程
- 创建视图层文件,注册路由
- 根据接口数据生成 model 层数据描述文件
- 在 api 层请求接口
- 使用 services 层定义的通用方法或特殊方法对 api 请求的数据进行处理
- 视图层 controller 引入数据进行业务处理
- 视图层 view 进行样式渲染
2.1、创建视图层文件,注册路由
使用 get cli 创建页面文件并注册路由,生成的文件存放在 modules/province 下,为 bindings、controllers、views
get create page:province
- province_view
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../controllers/province_controller.dart'; //引入controller,Widget中使用Obx可直接使用
class ProvinceView extends GetView<ProvinceController> {
const ProvinceView({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('ProvinceView'),
centerTitle: true,
),
body: const Center(
child: Text(
'ProvinceView is working',
style: TextStyle(fontSize: 20),
),
),
);
}
}
- province_controller
import 'package:get/get.dart';
class ProvinceController extends GetxController {
//TODO: Implement ProvinceController
final count = 0.obs; // 使用 .obs定义的数据可在view中使用
@override
void onInit() { // 页面初始化
super.onInit();
}
@override
void onReady() { // 页面渲染完成
super.onReady();
}
@override
void onClose() { // 页面关闭
super.onClose();
}
void increment() => count.value++; // 定义方法
}
2.2、生成 model 层数据描述文件
- 生成
#https://province.com/api/province =》
{"code": 1,
"msg": "查询成功",
"data": [
{
"provinceCode": 110000,
"name": "北京市",
"id": 1
},
]
}
# 生成model文件
get generate model on models from https://province.com/api/province --skipProvider //根据接口数据生成json描述文件
无法自动设置model名称,你想用什么名称? [province] // 使用接口名称定义文件名
会在 models 文件夹下生成 province_model.dart 文件,内容如下:
class Province {
int? code;
String? msg;
List<Data>? data;
Province({this.code, this.msg, this.data});
Province.fromJson(Map<String, dynamic> json) {
code = json['code'];
msg = json['msg'];
if (json['data'] != null) {
data = <Data>[];
json['data'].forEach((v) {
data?.add(Data.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final data = <String, dynamic>{};
data['code'] = code;
data['msg'] = msg;
data['data'] = data;
return data;
}
}
class Data {
int? provinceCode;
String? name;
int? id;
Data({this.provinceCode, this.name, this.id});
Data.fromJson(Map<String, dynamic> json) {
provinceCode = json['provinceCode'];
name = json['name'];
id = json['id'];
}
Map<String, dynamic> toJson() {
final data = <String, dynamic>{};
data['provinceCode'] = provinceCode;
data['name'] = name;
data['id'] = id;
return data;
}
}
- 文件内搜索[Province] 替换为 [ProvinceModel] // 加上 Model 关键字防止接口名称与默认 class 重名
- 文件内搜索[Result] 替换为 [ProvinceModelItem] // Result 为接口返回数据的最外层键名
2.3 api 层请求接口
- 创建文件,名称为模块名.dart
import 'package:wlxy/app/request/request.dart'; // ajax请求工具
import 'package:wlxy/app/models/province_model.dart'; // model层数据描述
import 'package:wlxy/app/services/default.dart'; // service层 数据基础处理,状态码判断、脱壳等,特殊处理需自定义
class ProvinceApi {
static Future getProvince() async {
final response = await HttpUtils.get(path: "/api/province"); //请求ajax
final ProvinceModel result = ProvinceModel.fromJson(response); // 进行数据描述
return DefaultService.getResult(result); // 进行基础数据处理
}
}
2.4 services 层定义数据处理方法
# 基础脱壳
class DefaultService {
static Future getResult(response) async {
if (response.code == 1) {
return response.data;
} else {
print(response.msg);
}
}
}
2.5 视图层 controller 引入数据进行业务处理
import 'package:get/get.dart';
import 'package:wlxy/app/models/get_province_model.dart'; // 引入数据描述文件
import 'package:wlxy/app/api/province.dart'; //引入api数据
class ProvinceController extends GetxController {
RxList<GetProvinceItemModel> provinceList = <GetProvinceItemModel>[].obs; //定义页面内接受数据变量
@override
void onInit() {
super.onInit();
getProvinceData();
}
getProvinceData() async {
provinceList.value = await ProvinceApi.getProvince();
}
}
2.6 视图层 view 进行数据渲染
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../controllers/province_controller.dart';
class ProvinceView extends GetView<ProvinceController> {
const ProvinceView({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('JSON序列化Demo '),
centerTitle: true,
),
// provinceList2view
body: Obx(
() => ListView.builder(
itemCount: controller.provinceList.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(controller.provinceList[index].name!),
subtitle:
Text(controller.provinceList[index].provinceCode.toString()),
);
},
),
),
);
}
}
三、全局状态管理
- 生成全局状态文件
get create controller:counter
import 'package:get/get.dart';
class CounterController extends GetxController {
/// 定义了该变量为响应式变量,当该变量数值变化时,页面的刷新方法将自动刷新
var count = 100.obs;
/// 自增方法
void increase() => ++count;
}
- 页面内使用
import 'package:wlxy/app/controllers/counter_controller.dart'; // 引入全局状态文件
...
Widget build(BuildContext context) {
final counterGlobal = Get.put(CounterController()); // 注册到当前页面中
...
Obx(()=>Text(
'全局num:${counterGlobal.count}', // 渲染数据
style: const TextStyle(fontSize: 20),
)
),
...
ElevatedButton(
onPressed: () => counterGlobal.increment(), // 使用全局状态中定义的方法更改状态
child: const Text('全局num+1'),
),
四、本地数据
- 本地数据相关操作在data目录下,根据数据内容定义文件名,例如:user_data.dart
// 使用shared_preferences 存储用户信息
import 'dart:convert';
import 'package:wlxy/app/utils/j_sp_util.dart'; // 本地数据处理通用方法库
class UserData {
String? name;
String? token;
UserData({this.name, this.token});
// json转换为UserData
UserData.fromJson(Map<String, dynamic> json) {
name = json['name'];
token = json['token'];
}
// UserData转换为json
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['name'] = name;
data['token'] = token;
return data;
}
// 获取Map类型
static Future<UserData?> getUserData() async {
String? user = await JSpUtil.getLocalStorage('user');
if (user != null) {
return UserData.fromJson(json.decode(user));
}
return null;
}
// 设置Map类型
static setUserData(UserData user) async {
// 设置Map类型
JSpUtil.setLocalStorage('user', user.toJson());
}
}
- 视图层引入使用
import 'package:get/get.dart';
import 'package:wlxy/app/data/user_data.dart'; // 本地数据
class MineController extends GetxController {
//TODO: Implement MineController
RxString name = '33'.obs;
@override
void onReady() {
super.onReady();
getUserData();
}
void getUserData() {
UserData.getUserData().then((value) {
if (value != null) {
print(value.name);
name.value = value.name!;
update();
} else {
print('没有数据');
}
});
}
void setUserData() {
UserData.setUserData(UserData(name: '用户名', token: '4123124124'));
}
}