本节目标

  • 编写 mutation 操作,登录、注册
  • graphql 操作类加入异常处理

视频

https://www.bilibili.com/video/BV1vt4y1Q7i3/

代码

https://github.com/ducafecat/flutter_learn_news/releases/tag/v1.0.16

strapi 运行环境网盘下载

  • 网盘

链接:https://pan.baidu.com/s/13Ujy2hzXp8tSqxCx_4IhVQ
密码:yu82

  • 运行

需要用 docker-compose 启动
账号 admin
密码 123456

1
2
3
4
5
# 启动
docker-compose up -d --remove-orphans

# 关闭
docker-compose down

正文

  • 调试地址

http://localhost:1337/graphql

注册 graphql

  • mutation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
mutation UserRegister($username: String!, $email: String!, $password: String!) {
register(input: { username: $username, email: $email, password: $password }) {
jwt
user {
id
username
email
role {
id
name
description
type
}
blocked
confirmed
}
}
}
  • variables
1
2
3
4
5
{
"username": "dbuser",
"email": "dbuser@ducafecat.tech",
"password": "12345678"
}

登录 graphql

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
mutation UserLogin($identifier: String!, $password: String!) {
login(input: { identifier: $identifier, password: $password }) {
jwt
user {
id
username
email
role {
id
name
description
type
}
blocked
confirmed
}
}
}
  • variables
1
2
3
4
{
"identifier": "dbuser",
"password": "12345678"
}

identifier 可以是 username、email ,都是唯一的

Graphql 请求类加入异常处理

  • lib/common/utils/graphql_client.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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
import 'package:flutter/material.dart';
import 'package:flutter_ducafecat_news/common/utils/utils.dart';
import 'package:flutter_ducafecat_news/common/values/values.dart';
import 'package:flutter_ducafecat_news/common/widgets/widgets.dart';
import 'package:flutter_ducafecat_news/global.dart';
import 'package:graphql/client.dart';

class GraphqlClientUtil {
static OptimisticCache cache = OptimisticCache(
dataIdFromObject: typenameDataIdFromObject,
);

static client() {
HttpLink _httpLink = HttpLink(
uri: '$SERVER_STRAPI_GRAPHQL_URL/graphql',
);

if (Global.profile?.jwt != null) {
final AuthLink _authLink = AuthLink(
getToken: () => 'Bearer ${Global.profile.jwt}',
);
final Link _link = _authLink.concat(_httpLink);
return GraphQLClient(
cache: cache,
link: _link,
);
} else {
return GraphQLClient(
cache: cache,
link: _httpLink,
);
}
}

/// 错误处理
static _formatException(BuildContext context, OperationException exception) {
var statusCode = '';
try {
statusCode = exception
.graphqlErrors[0]?.extensions["exception"]["output"]["statusCode"]
.toString();
if (statusCode == '') {
statusCode = exception.graphqlErrors[0]?.extensions["exception"]["code"]
.toString();
}
} catch (e) {}

switch (statusCode) {
case '400': // 重新登录
toastInfo(msg: "错误请求,提交数据错误!");
break;
case '401': // 没有认证
case '403': // 没有授权
toastInfo(msg: "账号无效、服务没有授权,请重新登录!");
return goLoginPage(context);
// break;
default:
toastInfo(msg: exception.toString());
}
throw exception;
}

// 查询
static Future query({
@required BuildContext context,
@required String schema,
Map<String, dynamic> variables,
}) async {
QueryOptions options = QueryOptions(
documentNode: gql(schema),
variables: variables,
);

QueryResult result = await client().query(options);

if (result.hasException) {
_formatException(context, result.exception);
}

return result;
}

// 操作
static Future mutate({
@required BuildContext context,
@required String schema,
Map<String, dynamic> variables,
}) async {
MutationOptions options = MutationOptions(
documentNode: gql(schema),
variables: variables,
);

QueryResult result = await client().mutate(options);

if (result.hasException) {
_formatException(context, result.exception);
}

return result;
}
}
  • 常见错误码:

400 数据提交时间
401 需要登录认证
403 功能需要授权

Entity 用户

  • lib/common/entitys/gql_user.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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
// 用户登录 - request
class GqlUserLoginRequestEntity {
GqlUserLoginRequestEntity({
this.identifier,
this.password,
});

String identifier;
String password;

factory GqlUserLoginRequestEntity.fromJson(Map<String, dynamic> json) =>
GqlUserLoginRequestEntity(
identifier: json["identifier"],
password: json["password"],
);

Map<String, dynamic> toJson() => {
"identifier": identifier,
"password": password,
};
}

// 用户登录 - request
class GqlUserRegisterRequestEntity {
GqlUserRegisterRequestEntity({
this.username,
this.email,
this.password,
});

String username;
String email;
String password;

factory GqlUserRegisterRequestEntity.fromJson(Map<String, dynamic> json) =>
GqlUserRegisterRequestEntity(
username: json["username"],
email: json["email"],
password: json["password"],
);

Map<String, dynamic> toJson() => {
"username": username,
"email": email,
"password": password,
};
}

//////////////////////////////////////////////////////////////////

// 用户登录 - response
class GqlUserLoginResponseEntity {
GqlUserLoginResponseEntity({
this.jwt,
this.user,
});

String jwt;
UserEntity user;

factory GqlUserLoginResponseEntity.fromJson(Map<String, dynamic> json) =>
GqlUserLoginResponseEntity(
jwt: json["jwt"],
user: UserEntity.fromJson(json["user"]),
);

Map<String, dynamic> toJson() => {
"jwt": jwt,
"user": user.toJson(),
};
}

// 注册新用户 - response
class GqlUserRegisterResponseEntity {
GqlUserRegisterResponseEntity({
this.jwt,
this.user,
});

String jwt;
UserEntity user;

factory GqlUserRegisterResponseEntity.fromJson(Map<String, dynamic> json) =>
GqlUserRegisterResponseEntity(
jwt: json["jwt"],
user: UserEntity.fromJson(json["user"]),
);

Map<String, dynamic> toJson() => {
"jwt": jwt,
"user": user.toJson(),
};
}

// 用户
class UserEntity {
UserEntity({
this.id,
this.username,
this.email,
this.role,
this.blocked,
this.confirmed,
});

String id;
String username;
String email;
RoleEntity role;
bool blocked;
bool confirmed;

factory UserEntity.fromJson(Map<String, dynamic> json) => UserEntity(
id: json["id"],
username: json["username"],
email: json["email"],
role: RoleEntity.fromJson(json["role"]),
blocked: json["blocked"],
confirmed: json["confirmed"],
);

Map<String, dynamic> toJson() => {
"id": id,
"username": username,
"email": email,
"role": role.toJson(),
"blocked": blocked,
"confirmed": confirmed,
};
}

// 角色
class RoleEntity {
RoleEntity({
this.id,
this.name,
this.description,
this.type,
});

String id;
String name;
String description;
String type;

factory RoleEntity.fromJson(Map<String, dynamic> json) => RoleEntity(
id: json["id"],
name: json["name"],
description: json["description"],
type: json["type"],
);

Map<String, dynamic> toJson() => {
"id": id,
"name": name,
"description": description,
"type": type,
};
}

API 用户注册、登录

  • lib/common/apis/gql_user.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
import 'package:flutter/material.dart';
import 'package:flutter_ducafecat_news/common/entitys/entitys.dart';
import 'package:flutter_ducafecat_news/common/graphql/graphql.dart';
import 'package:flutter_ducafecat_news/common/utils/utils.dart';
import 'package:graphql/client.dart';

/// 新闻
class GqlUserAPI {
/// 登录
static Future<GqlUserLoginResponseEntity> login({
@required BuildContext context,
@required GqlUserLoginRequestEntity variables,
}) async {
QueryResult response = await GraphqlClientUtil.mutate(
context: context,
schema: GQL_USER_LOGIN,
variables: variables.toJson());

return GqlUserLoginResponseEntity.fromJson(response.data["login"]);
}

/// 注册
static Future<GqlUserRegisterResponseEntity> register({
@required BuildContext context,
@required GqlUserRegisterRequestEntity variables,
}) async {
QueryResult response = await GraphqlClientUtil.mutate(
context: context,
schema: GQL_USER_REGISTER,
variables: variables.toJson());

return GqlUserRegisterResponseEntity.fromJson(response.data["register"]);
}
}

资源

设计稿蓝湖预览

https://lanhuapp.com/url/lYuz1
密码: gSKl

蓝湖现在收费了,所以查看标记还请自己上传 xd 设计稿
商业设计稿文件不好直接分享, 可以加微信联系 ducafecat


© 猫哥

https://ducafecat.tech