flu-cli-core 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +60 -0
- package/dist/chunk-FOMWV2YP.js +378 -0
- package/dist/chunk-SW6YDKXI.js +112 -0
- package/dist/factory-6DDXZYQP.js +6 -0
- package/dist/index.cjs +4668 -0
- package/dist/index.d.cts +644 -0
- package/dist/index.d.ts +644 -0
- package/dist/index.js +4037 -0
- package/dist/upgrade_snippets-XFR7Q444.js +8 -0
- package/locales/en-US.json +59 -0
- package/locales/zh-CN.json +59 -0
- package/package.json +52 -0
- package/templates/README.md +129 -0
- package/templates/core_files/base/base_list_page.dart.template +225 -0
- package/templates/core_files/base/base_list_viewmodel.dart.template +164 -0
- package/templates/core_files/base/base_page.dart.template +252 -0
- package/templates/core_files/base/base_viewmodel.dart.template +68 -0
- package/templates/core_files/base/index.dart.template +5 -0
- package/templates/core_files/config/app_config.dart.template +142 -0
- package/templates/core_files/config/app_initializer.dart.template +74 -0
- package/templates/core_files/config/index.dart.template +3 -0
- package/templates/core_files/index.dart.template +8 -0
- package/templates/core_files/network/README.md +378 -0
- package/templates/core_files/network/app_error_code.dart.template +49 -0
- package/templates/core_files/network/app_http.dart.template +306 -0
- package/templates/core_files/network/app_response.dart.template +81 -0
- package/templates/core_files/network/index.dart.template +12 -0
- package/templates/core_files/network/interceptors/app_response_interceptor.dart.template +44 -0
- package/templates/core_files/network/interceptors/auth_interceptor.dart.template +30 -0
- package/templates/core_files/network/interceptors/error_interceptor.dart.template +48 -0
- package/templates/core_files/network/interceptors/index.dart.template +6 -0
- package/templates/core_files/network/interceptors/log_interceptor.dart.template +97 -0
- package/templates/core_files/network/interceptors/network_error_interceptor.dart.template +58 -0
- package/templates/core_files/network/interceptors/retry_interceptor.dart.template +69 -0
- package/templates/core_files/network/response_adapter.dart.template +69 -0
- package/templates/core_files/router/app_routes.dart.template +32 -0
- package/templates/core_files/router/index.dart.template +3 -0
- package/templates/core_files/router/navigator_util_getx.dart.template +131 -0
- package/templates/core_files/router/navigator_util_material.dart.template +191 -0
- package/templates/core_files/storage/index.dart.template +3 -0
- package/templates/core_files/storage/storage_keys.dart.template +34 -0
- package/templates/core_files/storage/storage_util.dart.template +102 -0
- package/templates/core_files/theme/app_theme.dart.template +37 -0
- package/templates/core_files/theme/index.dart.template +3 -0
- package/templates/core_files/theme/status_views_theme.dart.template +40 -0
- package/templates/core_files/utils/index.dart.template +2 -0
- package/templates/core_files/utils/loading_util.dart.template +55 -0
- package/templates/core_files/utils/toast_util.dart.template +128 -0
- package/templates/examples/eg_list_page.dart.template +340 -0
- package/templates/examples/eg_list_viewmodel.dart.template +31 -0
- package/templates/examples/eg_service.dart.template +78 -0
- package/templates/examples/mock_data.dart.template +50388 -0
- package/templates/examples/tu_chong_model.dart.template +633 -0
- package/templates/request_helper.dart.template +59 -0
- package/templates/snippets/flu-cli.code-snippets +268 -0
- package/templates/snippets/flu-cli.code-snippets.backup +268 -0
|
@@ -0,0 +1,633 @@
|
|
|
1
|
+
import 'dart:convert' show json, jsonEncode;
|
|
2
|
+
import 'dart:math';
|
|
3
|
+
import 'package:flutter/rendering.dart';
|
|
4
|
+
|
|
5
|
+
void tryCatch(Function f) {
|
|
6
|
+
try {
|
|
7
|
+
f.call();
|
|
8
|
+
} catch (e, stack) {
|
|
9
|
+
debugPrint('$e');
|
|
10
|
+
debugPrint('$stack');
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
T? asT<T>(dynamic value) {
|
|
15
|
+
if (value is T) {
|
|
16
|
+
return value;
|
|
17
|
+
}
|
|
18
|
+
if (value != null) {
|
|
19
|
+
final String valueS = value.toString();
|
|
20
|
+
if (0 is T) {
|
|
21
|
+
return int.tryParse(valueS) as T?;
|
|
22
|
+
} else if (0.0 is T) {
|
|
23
|
+
return double.tryParse(valueS) as T?;
|
|
24
|
+
} else if ('' is T) {
|
|
25
|
+
return valueS as T;
|
|
26
|
+
} else if (false is T) {
|
|
27
|
+
if (valueS == '0' || valueS == '1') {
|
|
28
|
+
return (valueS == '1') as T;
|
|
29
|
+
}
|
|
30
|
+
return (valueS == 'true') as T;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
class TuChongSource {
|
|
37
|
+
TuChongSource({
|
|
38
|
+
this.counts,
|
|
39
|
+
this.feedList,
|
|
40
|
+
this.isHistory,
|
|
41
|
+
this.message,
|
|
42
|
+
this.more,
|
|
43
|
+
this.result,
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
factory TuChongSource.fromJson(Map<String, dynamic> jsonRes) {
|
|
47
|
+
final List<TuChongItem>? feedList =
|
|
48
|
+
jsonRes['feedList'] is List ? <TuChongItem>[] : null;
|
|
49
|
+
if (feedList != null) {
|
|
50
|
+
for (final dynamic item in jsonRes['feedList']) {
|
|
51
|
+
if (item != null) {
|
|
52
|
+
tryCatch(() {
|
|
53
|
+
feedList
|
|
54
|
+
.add(TuChongItem.fromJson(asT<Map<String, dynamic>>(item)!));
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return TuChongSource(
|
|
61
|
+
counts: asT<int>(jsonRes['counts']),
|
|
62
|
+
feedList: feedList,
|
|
63
|
+
isHistory: asT<bool>(jsonRes['is_history']),
|
|
64
|
+
message: asT<String>(jsonRes['message']),
|
|
65
|
+
more: asT<bool>(jsonRes['more']),
|
|
66
|
+
result: asT<String>(jsonRes['result']),
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
final int? counts;
|
|
71
|
+
final List<TuChongItem>? feedList;
|
|
72
|
+
final bool? isHistory;
|
|
73
|
+
final String? message;
|
|
74
|
+
final bool? more;
|
|
75
|
+
final String? result;
|
|
76
|
+
|
|
77
|
+
Map<String, dynamic> toJson() => <String, dynamic>{
|
|
78
|
+
'counts': counts,
|
|
79
|
+
'feedList': feedList,
|
|
80
|
+
'is_history': isHistory,
|
|
81
|
+
'message': message,
|
|
82
|
+
'more': more,
|
|
83
|
+
'result': result,
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
@override
|
|
87
|
+
String toString() {
|
|
88
|
+
return json.encode(this);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
class TuChongItem {
|
|
93
|
+
TuChongItem({
|
|
94
|
+
this.authorId,
|
|
95
|
+
this.collected,
|
|
96
|
+
this.commentListPrefix,
|
|
97
|
+
this.comments,
|
|
98
|
+
this.content,
|
|
99
|
+
this.createdAt,
|
|
100
|
+
this.dataType,
|
|
101
|
+
this.delete,
|
|
102
|
+
this.eventTags,
|
|
103
|
+
this.excerpt,
|
|
104
|
+
this.favoriteListPrefix,
|
|
105
|
+
this.favorites,
|
|
106
|
+
this.imageCount,
|
|
107
|
+
this.images,
|
|
108
|
+
this.isFavorite,
|
|
109
|
+
this.parentComments,
|
|
110
|
+
this.passedTime,
|
|
111
|
+
this.postId,
|
|
112
|
+
this.publishedAt,
|
|
113
|
+
this.recommend,
|
|
114
|
+
this.recomType,
|
|
115
|
+
this.rewardable,
|
|
116
|
+
this.rewardListPrefix,
|
|
117
|
+
this.rewards,
|
|
118
|
+
this.rqtId,
|
|
119
|
+
this.shares,
|
|
120
|
+
this.site,
|
|
121
|
+
this.siteId,
|
|
122
|
+
this.sites,
|
|
123
|
+
this.tags,
|
|
124
|
+
this.title,
|
|
125
|
+
this.titleImage,
|
|
126
|
+
this.type,
|
|
127
|
+
this.update,
|
|
128
|
+
this.url,
|
|
129
|
+
this.views,
|
|
130
|
+
this.tagColors,
|
|
131
|
+
this.index,
|
|
132
|
+
this.crossAxisCount,
|
|
133
|
+
this.textimgurls,
|
|
134
|
+
this.realImages,
|
|
135
|
+
this.netData,
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
factory TuChongItem.fromJson(Map<String, dynamic> jsonRes) {
|
|
139
|
+
final List<Object?>? commentListPrefix =
|
|
140
|
+
jsonRes['comment_list_prefix'] is List ? <Object?>[] : null;
|
|
141
|
+
if (commentListPrefix != null) {
|
|
142
|
+
for (final dynamic item in jsonRes['comment_list_prefix']) {
|
|
143
|
+
if (item != null) {
|
|
144
|
+
tryCatch(() {
|
|
145
|
+
commentListPrefix.add(asT<Object>(item));
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
final List<String?>? eventTags =
|
|
152
|
+
jsonRes['event_tags'] is List ? <String?>[] : null;
|
|
153
|
+
if (eventTags != null) {
|
|
154
|
+
for (final dynamic item in jsonRes['event_tags']) {
|
|
155
|
+
if (item != null) {
|
|
156
|
+
tryCatch(() {
|
|
157
|
+
eventTags.add(asT<String>(item));
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
final List<Object?>? favoriteListPrefix =
|
|
164
|
+
jsonRes['favorite_list_prefix'] is List ? <Object?>[] : null;
|
|
165
|
+
if (favoriteListPrefix != null) {
|
|
166
|
+
for (final dynamic item in jsonRes['favorite_list_prefix']) {
|
|
167
|
+
if (item != null) {
|
|
168
|
+
tryCatch(() {
|
|
169
|
+
favoriteListPrefix.add(asT<Object>(item));
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
final List<ImageItem>? images =
|
|
176
|
+
jsonRes['images'] is List ? <ImageItem>[] : null;
|
|
177
|
+
int cACount = 1;
|
|
178
|
+
List<String> realList = [];
|
|
179
|
+
if (images != null) {
|
|
180
|
+
for (final dynamic item in jsonRes['images']) {
|
|
181
|
+
if (item != null) {
|
|
182
|
+
tryCatch(() {
|
|
183
|
+
final ImageItem imageItem =
|
|
184
|
+
ImageItem.fromJson(asT<Map<String, dynamic>>(item)!);
|
|
185
|
+
images.add(imageItem);
|
|
186
|
+
realList.add(imageItem.imageUrl);
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
if (images.length == 1) {
|
|
191
|
+
cACount = 1;
|
|
192
|
+
} else if (images.length == 4 || images.length == 2) {
|
|
193
|
+
cACount = 2;
|
|
194
|
+
} else {
|
|
195
|
+
cACount = 3;
|
|
196
|
+
}
|
|
197
|
+
} else {
|
|
198
|
+
if (jsonRes['text_img_urls'] != null) {
|
|
199
|
+
realList = jsonRes['text_img_urls'];
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
final List<Object?>? rewardListPrefix =
|
|
204
|
+
jsonRes['reward_list_prefix'] is List ? <Object?>[] : null;
|
|
205
|
+
if (rewardListPrefix != null) {
|
|
206
|
+
for (final dynamic item in jsonRes['reward_list_prefix']) {
|
|
207
|
+
if (item != null) {
|
|
208
|
+
tryCatch(() {
|
|
209
|
+
rewardListPrefix.add(asT<Object>(item));
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
final List<Object?>? sites = jsonRes['sites'] is List ? <Object?>[] : null;
|
|
216
|
+
if (sites != null) {
|
|
217
|
+
for (final dynamic item in jsonRes['sites']) {
|
|
218
|
+
if (item != null) {
|
|
219
|
+
tryCatch(() {
|
|
220
|
+
sites.add(asT<Object>(item));
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
final List<Color> tagColors = <Color>[];
|
|
226
|
+
final List<String?>? tags = jsonRes['tags'] is List ? <String?>[] : null;
|
|
227
|
+
if (tags != null) {
|
|
228
|
+
const int maxNum = 6;
|
|
229
|
+
for (final dynamic item in jsonRes['tags']) {
|
|
230
|
+
if (item != null) {
|
|
231
|
+
tryCatch(() {
|
|
232
|
+
tags.add(asT<String>(item));
|
|
233
|
+
tagColors.add(
|
|
234
|
+
Color.fromARGB(
|
|
235
|
+
255,
|
|
236
|
+
Random.secure().nextInt(255),
|
|
237
|
+
Random.secure().nextInt(255),
|
|
238
|
+
Random.secure().nextInt(255),
|
|
239
|
+
),
|
|
240
|
+
);
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
if (tags.length == maxNum) {
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return TuChongItem(
|
|
250
|
+
authorId: asT<String>(jsonRes['author_id']),
|
|
251
|
+
collected: asT<bool>(jsonRes['collected']),
|
|
252
|
+
commentListPrefix: commentListPrefix,
|
|
253
|
+
comments: asT<int>(jsonRes['comments']),
|
|
254
|
+
content: asT<String>(jsonRes['content']),
|
|
255
|
+
createdAt: asT<String>(jsonRes['created_at']),
|
|
256
|
+
dataType: asT<String>(jsonRes['data_type']),
|
|
257
|
+
delete: asT<bool>(jsonRes['delete']),
|
|
258
|
+
eventTags: eventTags,
|
|
259
|
+
excerpt: asT<String>(jsonRes['excerpt']),
|
|
260
|
+
favoriteListPrefix: favoriteListPrefix,
|
|
261
|
+
favorites: asT<int>(jsonRes['favorites']),
|
|
262
|
+
imageCount: asT<int>(jsonRes['image_count']),
|
|
263
|
+
images: images,
|
|
264
|
+
isFavorite: asT<bool>(jsonRes['is_favorite']),
|
|
265
|
+
parentComments: asT<String>(jsonRes['parent_comments']),
|
|
266
|
+
passedTime: asT<String>(jsonRes['passed_time']),
|
|
267
|
+
postId: asT<int>(jsonRes['post_id']),
|
|
268
|
+
publishedAt: asT<String>(jsonRes['published_at']),
|
|
269
|
+
recommend: asT<bool>(jsonRes['recommend']),
|
|
270
|
+
recomType: asT<String>(jsonRes['recom_type']),
|
|
271
|
+
rewardable: asT<bool>(jsonRes['rewardable']),
|
|
272
|
+
rewardListPrefix: rewardListPrefix,
|
|
273
|
+
rewards: asT<String>(jsonRes['rewards']),
|
|
274
|
+
rqtId: asT<String>(jsonRes['rqt_id']),
|
|
275
|
+
shares: asT<int>(jsonRes['shares']),
|
|
276
|
+
site: Site.fromJson(asT<Map<String, dynamic>>(jsonRes['site'])!),
|
|
277
|
+
siteId: asT<String>(jsonRes['site_id']),
|
|
278
|
+
sites: sites,
|
|
279
|
+
tags: tags,
|
|
280
|
+
tagColors: tagColors,
|
|
281
|
+
title: asT<String>(jsonRes['title']),
|
|
282
|
+
titleImage: jsonRes['title_image'] == null
|
|
283
|
+
? null
|
|
284
|
+
: TitleImage.fromJson(
|
|
285
|
+
asT<Map<String, dynamic>>(jsonRes['title_image'])!,
|
|
286
|
+
),
|
|
287
|
+
type: asT<String>(jsonRes['type']),
|
|
288
|
+
update: asT<bool>(jsonRes['update']),
|
|
289
|
+
url: asT<String>(jsonRes['url']),
|
|
290
|
+
views: asT<int>(jsonRes['views']),
|
|
291
|
+
crossAxisCount: cACount,
|
|
292
|
+
textimgurls: jsonRes['text_img_urls'],
|
|
293
|
+
realImages: realList,
|
|
294
|
+
netData: jsonRes,
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
final String? authorId;
|
|
299
|
+
final bool? collected;
|
|
300
|
+
final List<Object?>? commentListPrefix;
|
|
301
|
+
final int? comments;
|
|
302
|
+
final String? content;
|
|
303
|
+
final String? createdAt;
|
|
304
|
+
final String? dataType;
|
|
305
|
+
final bool? delete;
|
|
306
|
+
final List<String?>? eventTags;
|
|
307
|
+
final String? excerpt;
|
|
308
|
+
final List<Object?>? favoriteListPrefix;
|
|
309
|
+
int? favorites;
|
|
310
|
+
final int? imageCount;
|
|
311
|
+
final List<ImageItem>? images;
|
|
312
|
+
bool? isFavorite;
|
|
313
|
+
final String? parentComments;
|
|
314
|
+
final String? passedTime;
|
|
315
|
+
final int? postId;
|
|
316
|
+
final String? publishedAt;
|
|
317
|
+
final bool? recommend;
|
|
318
|
+
final String? recomType;
|
|
319
|
+
final bool? rewardable;
|
|
320
|
+
final List<Object?>? rewardListPrefix;
|
|
321
|
+
final String? rewards;
|
|
322
|
+
final String? rqtId;
|
|
323
|
+
final int? shares;
|
|
324
|
+
final Site? site;
|
|
325
|
+
final String? siteId;
|
|
326
|
+
final List<Object?>? sites;
|
|
327
|
+
final List<String?>? tags;
|
|
328
|
+
final String? title;
|
|
329
|
+
final TitleImage? titleImage;
|
|
330
|
+
final String? type;
|
|
331
|
+
final bool? update;
|
|
332
|
+
final String? url;
|
|
333
|
+
final int? views;
|
|
334
|
+
final List<Color>? tagColors;
|
|
335
|
+
final List? textimgurls;
|
|
336
|
+
|
|
337
|
+
final List<String>? realImages;
|
|
338
|
+
|
|
339
|
+
final Map<String, dynamic>? netData;
|
|
340
|
+
int? crossAxisCount = 1;
|
|
341
|
+
|
|
342
|
+
int? index = 0;
|
|
343
|
+
|
|
344
|
+
bool get hasImage {
|
|
345
|
+
return images != null && images!.isNotEmpty;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
Size? imageRawSize;
|
|
349
|
+
|
|
350
|
+
Size get imageSize {
|
|
351
|
+
if (!hasImage) {
|
|
352
|
+
return const Size(0, 0);
|
|
353
|
+
}
|
|
354
|
+
return Size(images![0].width!.toDouble(), images![0].height!.toDouble());
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
String get imageUrl {
|
|
358
|
+
if (!hasImage) {
|
|
359
|
+
return '';
|
|
360
|
+
}
|
|
361
|
+
return 'https://photo.tuchong.com/${images![0].userId}/f/${images![0].imgId}.jpg';
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
String? get avatarUrl => site!.icon;
|
|
365
|
+
|
|
366
|
+
String? get imageTitle {
|
|
367
|
+
if (!hasImage) {
|
|
368
|
+
return title;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
return images![0].title;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
String? get imageDescription {
|
|
375
|
+
if (!hasImage) {
|
|
376
|
+
return content;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
return images![0].description;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
Map<String, dynamic> toJson() => <String, dynamic>{
|
|
383
|
+
'author_id': authorId,
|
|
384
|
+
'collected': collected,
|
|
385
|
+
'comment_list_prefix': commentListPrefix,
|
|
386
|
+
'comments': comments,
|
|
387
|
+
'content': content,
|
|
388
|
+
'created_at': createdAt,
|
|
389
|
+
'data_type': dataType,
|
|
390
|
+
'delete': delete,
|
|
391
|
+
'event_tags': eventTags,
|
|
392
|
+
'excerpt': excerpt,
|
|
393
|
+
'favorite_list_prefix': favoriteListPrefix,
|
|
394
|
+
'favorites': favorites,
|
|
395
|
+
'image_count': imageCount,
|
|
396
|
+
'images': images,
|
|
397
|
+
'is_favorite': isFavorite,
|
|
398
|
+
'parent_comments': parentComments,
|
|
399
|
+
'passed_time': passedTime,
|
|
400
|
+
'post_id': postId,
|
|
401
|
+
'published_at': publishedAt,
|
|
402
|
+
'recommend': recommend,
|
|
403
|
+
'recom_type': recomType,
|
|
404
|
+
'rewardable': rewardable,
|
|
405
|
+
'reward_list_prefix': rewardListPrefix,
|
|
406
|
+
'rewards': rewards,
|
|
407
|
+
'rqt_id': rqtId,
|
|
408
|
+
'shares': shares,
|
|
409
|
+
'site': site,
|
|
410
|
+
'site_id': siteId,
|
|
411
|
+
'sites': sites,
|
|
412
|
+
'tags': tags,
|
|
413
|
+
'title': title,
|
|
414
|
+
'title_image': titleImage?.toJson(),
|
|
415
|
+
'type': type,
|
|
416
|
+
'update': update,
|
|
417
|
+
'url': url,
|
|
418
|
+
'views': views,
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
@override
|
|
422
|
+
String toString() {
|
|
423
|
+
return json.encode(this);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
class ImageItem {
|
|
428
|
+
ImageItem({
|
|
429
|
+
this.description,
|
|
430
|
+
this.excerpt,
|
|
431
|
+
this.height,
|
|
432
|
+
this.imgId,
|
|
433
|
+
this.imgIdStr,
|
|
434
|
+
this.title,
|
|
435
|
+
this.userId,
|
|
436
|
+
this.width,
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
factory ImageItem.fromJson(Map<String, dynamic> jsonRes) => ImageItem(
|
|
440
|
+
description: asT<String>(jsonRes['description']),
|
|
441
|
+
excerpt: asT<String>(jsonRes['excerpt']),
|
|
442
|
+
height: asT<int>(jsonRes['height']),
|
|
443
|
+
imgId: asT<int>(jsonRes['img_id']),
|
|
444
|
+
imgIdStr: asT<String>(jsonRes['img_id_str']),
|
|
445
|
+
title: asT<String>(jsonRes['title']),
|
|
446
|
+
userId: asT<int>(jsonRes['user_id']),
|
|
447
|
+
width: asT<int>(jsonRes['width']),
|
|
448
|
+
);
|
|
449
|
+
|
|
450
|
+
final String? description;
|
|
451
|
+
final String? excerpt;
|
|
452
|
+
final int? height;
|
|
453
|
+
final int? imgId;
|
|
454
|
+
final String? imgIdStr;
|
|
455
|
+
final String? title;
|
|
456
|
+
final int? userId;
|
|
457
|
+
final int? width;
|
|
458
|
+
String get imageUrl {
|
|
459
|
+
return 'https://photo.tuchong.com/$userId/f/$imgId.jpg';
|
|
460
|
+
}
|
|
461
|
+
// ImageProvider createNetworkImage() {
|
|
462
|
+
// return ExtendedNetworkImageProvider(imageUrl);
|
|
463
|
+
// }
|
|
464
|
+
|
|
465
|
+
// ImageProvider createResizeImage() {
|
|
466
|
+
// return ResizeImage(ExtendedNetworkImageProvider(imageUrl),
|
|
467
|
+
// width: width ~/ 5, height: height ~/ 5);
|
|
468
|
+
// }
|
|
469
|
+
|
|
470
|
+
// void clearCache() {
|
|
471
|
+
// createNetworkImage().evict();
|
|
472
|
+
// createResizeImage().evict();
|
|
473
|
+
// }
|
|
474
|
+
Map<String, dynamic> toJson() => <String, dynamic>{
|
|
475
|
+
'description': description,
|
|
476
|
+
'excerpt': excerpt,
|
|
477
|
+
'height': height,
|
|
478
|
+
'img_id': imgId,
|
|
479
|
+
'img_id_str': imgIdStr,
|
|
480
|
+
'title': title,
|
|
481
|
+
'user_id': userId,
|
|
482
|
+
'width': width,
|
|
483
|
+
};
|
|
484
|
+
|
|
485
|
+
@override
|
|
486
|
+
String toString() {
|
|
487
|
+
return json.encode(this);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
class Site {
|
|
492
|
+
Site({
|
|
493
|
+
this.description,
|
|
494
|
+
this.domain,
|
|
495
|
+
this.followers,
|
|
496
|
+
this.hasEverphotoNote,
|
|
497
|
+
this.icon,
|
|
498
|
+
this.isBindEverphoto,
|
|
499
|
+
this.isFollowing,
|
|
500
|
+
this.name,
|
|
501
|
+
this.siteId,
|
|
502
|
+
this.type,
|
|
503
|
+
this.url,
|
|
504
|
+
this.verificationList,
|
|
505
|
+
this.verifications,
|
|
506
|
+
this.verified,
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
factory Site.fromJson(Map<String, dynamic> jsonRes) {
|
|
510
|
+
final List<VerificationList>? verificationList =
|
|
511
|
+
jsonRes['verification_list'] is List ? <VerificationList>[] : null;
|
|
512
|
+
if (verificationList != null) {
|
|
513
|
+
for (final dynamic item in jsonRes['verification_list']) {
|
|
514
|
+
if (item != null) {
|
|
515
|
+
tryCatch(() {
|
|
516
|
+
verificationList.add(
|
|
517
|
+
VerificationList.fromJson(asT<Map<String, dynamic>>(item)!),
|
|
518
|
+
);
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
return Site(
|
|
525
|
+
description: asT<String>(jsonRes['description']),
|
|
526
|
+
domain: asT<String>(jsonRes['domain']),
|
|
527
|
+
followers: asT<int>(jsonRes['followers']),
|
|
528
|
+
hasEverphotoNote: asT<bool>(jsonRes['has_everphoto_note']),
|
|
529
|
+
icon: asT<String>(jsonRes['icon']),
|
|
530
|
+
isBindEverphoto: asT<bool>(jsonRes['is_bind_everphoto']),
|
|
531
|
+
isFollowing: asT<bool>(jsonRes['is_following']),
|
|
532
|
+
name: asT<String>(jsonRes['name']),
|
|
533
|
+
siteId: asT<String>(jsonRes['site_id']),
|
|
534
|
+
type: asT<String>(jsonRes['type']),
|
|
535
|
+
url: asT<String>(jsonRes['url']),
|
|
536
|
+
verificationList: verificationList,
|
|
537
|
+
verifications: asT<int>(jsonRes['verifications']),
|
|
538
|
+
verified: asT<bool>(jsonRes['verified']),
|
|
539
|
+
);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
final String? description;
|
|
543
|
+
final String? domain;
|
|
544
|
+
int? followers;
|
|
545
|
+
final bool? hasEverphotoNote;
|
|
546
|
+
final String? icon;
|
|
547
|
+
final bool? isBindEverphoto;
|
|
548
|
+
bool? isFollowing;
|
|
549
|
+
final String? name;
|
|
550
|
+
final String? siteId;
|
|
551
|
+
final String? type;
|
|
552
|
+
final String? url;
|
|
553
|
+
final List<VerificationList>? verificationList;
|
|
554
|
+
final int? verifications;
|
|
555
|
+
final bool? verified;
|
|
556
|
+
|
|
557
|
+
Map<String, dynamic> toJson() => <String, dynamic>{
|
|
558
|
+
'description': description,
|
|
559
|
+
'domain': domain,
|
|
560
|
+
'followers': followers,
|
|
561
|
+
'has_everphoto_note': hasEverphotoNote,
|
|
562
|
+
'icon': icon,
|
|
563
|
+
'is_bind_everphoto': isBindEverphoto,
|
|
564
|
+
'is_following': isFollowing,
|
|
565
|
+
'name': name,
|
|
566
|
+
'site_id': siteId,
|
|
567
|
+
'type': type,
|
|
568
|
+
'url': url,
|
|
569
|
+
'verification_list': verificationList,
|
|
570
|
+
'verifications': verifications,
|
|
571
|
+
'verified': verified,
|
|
572
|
+
};
|
|
573
|
+
|
|
574
|
+
@override
|
|
575
|
+
String toString() {
|
|
576
|
+
return json.encode(this);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
class VerificationList {
|
|
581
|
+
VerificationList({
|
|
582
|
+
this.verificationReason,
|
|
583
|
+
this.verificationType,
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
factory VerificationList.fromJson(Map<String, dynamic> jsonRes) =>
|
|
587
|
+
VerificationList(
|
|
588
|
+
verificationReason: asT<String>(jsonRes['verification_reason']),
|
|
589
|
+
verificationType: asT<int>(jsonRes['verification_type']),
|
|
590
|
+
);
|
|
591
|
+
|
|
592
|
+
final String? verificationReason;
|
|
593
|
+
final int? verificationType;
|
|
594
|
+
|
|
595
|
+
Map<String, dynamic> toJson() => <String, dynamic>{
|
|
596
|
+
'verification_reason': verificationReason,
|
|
597
|
+
'verification_type': verificationType,
|
|
598
|
+
};
|
|
599
|
+
|
|
600
|
+
@override
|
|
601
|
+
String toString() {
|
|
602
|
+
return json.encode(this);
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
class TitleImage {
|
|
607
|
+
const TitleImage({
|
|
608
|
+
this.height,
|
|
609
|
+
this.url,
|
|
610
|
+
this.width,
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
factory TitleImage.fromJson(Map<String, dynamic> jsonRes) => TitleImage(
|
|
614
|
+
height: asT<int?>(jsonRes['height']),
|
|
615
|
+
url: asT<String?>(jsonRes['url']),
|
|
616
|
+
width: asT<int?>(jsonRes['width']),
|
|
617
|
+
);
|
|
618
|
+
|
|
619
|
+
final int? height;
|
|
620
|
+
final String? url;
|
|
621
|
+
final int? width;
|
|
622
|
+
|
|
623
|
+
@override
|
|
624
|
+
String toString() {
|
|
625
|
+
return jsonEncode(this);
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
Map<String, dynamic> toJson() => <String, dynamic>{
|
|
629
|
+
'height': height,
|
|
630
|
+
'url': url,
|
|
631
|
+
'width': width,
|
|
632
|
+
};
|
|
633
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import 'package:flutter/material.dart';
|
|
2
|
+
import '../network/http_util.dart';
|
|
3
|
+
import 'loading_util.dart';
|
|
4
|
+
import 'toast_util.dart';
|
|
5
|
+
|
|
6
|
+
/// 网络请求辅助工具
|
|
7
|
+
///
|
|
8
|
+
/// 职责:
|
|
9
|
+
/// 1. 封装通用的网络请求交互逻辑(Loading、错误提示)。
|
|
10
|
+
/// 2. 解耦业务层与 UI 工具层。
|
|
11
|
+
class RequestHelper {
|
|
12
|
+
/// 带 Loading 和自动错误提示的请求包装
|
|
13
|
+
///
|
|
14
|
+
/// [context] 必传,用于展示弹窗和 Toast
|
|
15
|
+
/// [request] 具体的网络请求闭包
|
|
16
|
+
/// [loadingMessage] 自定义加载提示语
|
|
17
|
+
/// [showError] 是否在失败时自动展示 Toast
|
|
18
|
+
/// [showSuccess] 是否在成功时展示 Toast
|
|
19
|
+
/// [successMessage] 成功提示语
|
|
20
|
+
static Future<T> withLoading<T>({
|
|
21
|
+
required BuildContext context,
|
|
22
|
+
required Future<T> Function() request,
|
|
23
|
+
String? loadingMessage,
|
|
24
|
+
bool showError = true,
|
|
25
|
+
bool showSuccess = false,
|
|
26
|
+
String? successMessage,
|
|
27
|
+
}) async {
|
|
28
|
+
LoadingUtil.show(context, message: loadingMessage ?? '加载中...');
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
final result = await request();
|
|
32
|
+
LoadingUtil.dismiss();
|
|
33
|
+
|
|
34
|
+
// 在 await 之后使用 context 前检查 mounted
|
|
35
|
+
if (showSuccess && context.mounted) {
|
|
36
|
+
ToastUtil.show(context, successMessage ?? '操作成功');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return result;
|
|
40
|
+
} catch (e) {
|
|
41
|
+
LoadingUtil.dismiss();
|
|
42
|
+
|
|
43
|
+
// 在 await 之后使用 context 前检查 mounted
|
|
44
|
+
if (showError && context.mounted) {
|
|
45
|
+
ToastUtil.show(context, _getErrorMessage(e));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
rethrow;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/// 集中解析错误信息
|
|
53
|
+
static String _getErrorMessage(dynamic error) {
|
|
54
|
+
if (error is ServerException) return error.message;
|
|
55
|
+
if (error is NetworkException) return error.message;
|
|
56
|
+
if (error is Exception) return error.toString();
|
|
57
|
+
return '请求遇到了点问题,请重试';
|
|
58
|
+
}
|
|
59
|
+
}
|