1.2 逻辑、页面、数据 分离

1.2 逻辑、页面、数据 分离

MVVM 架构

Model-View-ViewModel Architecture

职责:

  • View 视图显示
  • Controller 控制器,大管家,各种生命周期、数据装备、路由…
  • DataModel 数据模型
  • ViewModel 负责数据发生变化时去更新 View 视图

getx + dio + mvvm

img

说明:

  • View 视图中加载控制器 Controller
  • 控制器 Controller 生命周期载入数据
  • View 视图触发控制器 Controller 方法改变数据
  • 数据 ViewModel 触发更新视图
  • Model 数据模型拉取远程数据或者本地数据

分层代码生成与理解


第 1 步: 创建 login 模块

通过右键 “Getx: Full Page” 创建一个 login 的模块

我们创建了一个 login 的模块,文件清单如下

1
2
3
4
5
6
7
8
9
├── login
│ ├── bindings.dart
│ ├── controller.dart
│ ├── index.dart
│ ├── state.dart
│ ├── view.dart
│ └── widgets
│ ├── hello.dart
│ └── widgets.dart

文件说明

名称 说明
index.dart 导包
controller.dart 控制器 业务逻辑
state.dart 数据
view.dart 视图
bindings.dart 路由懒加载
widgets 子组件

img

可以发现通过这样的方式,将代码的层级关系进行了拆分,需要维护哪里就哪里,不会混乱写的到处都是,适合团队开发。

第 2 步: 阅读理解代码

state.dart

1
2
3
4
5
6
7
8
import 'package:get/get.dart';

class LoginState {
// title
final _title = "".obs;
set title(value) => _title.value = value;
get title => _title.value;
}

controller.dart

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import 'package:get/get.dart';

import 'index.dart';

class LoginController extends GetxController {
LoginController();

final state = LoginState();

// tap
void handleTap(int index) {
Get.snackbar(
"标题",
"消息",
);
}

/// 在 widget 内存中分配后立即调用。
@override
void onInit() {
super.onInit();
}

/// 在 onInit() 之后调用 1 帧。这是进入的理想场所
@override
void onReady() {
super.onReady();
}

/// 在 [onDelete] 方法之前调用。
@override
void onClose() {
super.onClose();
}

/// dispose 释放内存
@override
void dispose() {
super.dispose();
}
}

view.dart

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import 'package:flutter/material.dart';
import 'package:get/get.dart';

import 'index.dart';
import 'widgets/widgets.dart';

class LoginPage extends GetView<LoginController> {
const LoginPage({Key? key}) : super(key: key);

// 内容页
Widget _buildView() {
return const HelloWidget();
}

@override
Widget build(BuildContext context) {
return GetBuilder<LoginController>(
builder: (_) {
return Scaffold(
appBar: AppBar(title: const Text("login")),
body: SafeArea(
child: _buildView(),
),
);
},
);
}
}

bindings.dart

1
2
3
4
5
6
7
8
9
10
import 'package:get/get.dart';

import 'controller.dart';

class LoginBinding implements Bindings {
@override
void dependencies() {
Get.lazyPut<LoginController>(() => LoginController());
}
}

widgets/hello.dart

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import 'package:flutter/material.dart';
import 'package:get/get.dart';

import '../index.dart';

/// hello
class HelloWidget extends GetView<LoginController> {
const HelloWidget({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
return Center(
child: Obx(() => Text(controller.state.title)),
);
}
}

在绑定路由时, binding 中加入你的模块依赖

1
2
3
4
5
GetPage(
name: RouteNames.login,
page: () => const LoginPage(),
binding: LoginBinding(),
),

创建路由

lib/common/routers/pages.dart

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import 'package:flutter_woo_commerce_getx_learn/pages/system/login/index.dart';
import 'package:get/get.dart';

// 路由 Pages
class RoutePages {
// 列表
static List<GetPage> list = [
GetPage(
name: "/",
page: () => const LoginPage(),
binding: LoginBinding(),
),
];
}

lib/main.dart

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);

// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return GetMaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),

// 路由
initialRoute: "/",
getPages: RoutePages.list,

// home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}

修改 login

lib/pages/system/login/controller.dart

1
2
3
4
5
6
7
8
9
class LoginController extends GetxController {
LoginController();

final state = LoginState();

// tap
void onTap(int index) {
state.title = '点击了第$index个按钮';
}

lib/pages/system/login/view.dart

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 主视图
Widget _buildView() {
return Center(
child: Column(
children: [
const HelloWidget(),
ElevatedButton(
onPressed: () {
controller.onTap(DateTime.now().microsecondsSinceEpoch);
},
child: const Text("点击"),
),
],
),
);
}
image-20220705154042331

提交代码到 git

1
2
git add .
git commit -m "逻辑、数据、视图 分离"