Skip to content

Commit 4566c52

Browse files
committed
feat:support Multiple file selection and upload function (#12)
1 parent 0425de6 commit 4566c52

File tree

9 files changed

+473
-54
lines changed

9 files changed

+473
-54
lines changed
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
import 'dart:io';
2+
3+
import 'package:cached_network_image/cached_network_image.dart';
4+
import 'package:extended_image/extended_image.dart';
5+
import 'package:flutter/cupertino.dart';
6+
import 'package:flutter/material.dart';
7+
import 'package:flutter_picgo/components/upload_item/upload_item_presenter.dart';
8+
9+
enum UploadState {
10+
/// 上传中
11+
Uploading,
12+
13+
/// 保存中
14+
Saving,
15+
16+
/// 已完成
17+
Complete,
18+
19+
/// 上传失败
20+
UploadFail,
21+
22+
/// 保存失败
23+
SaveFail
24+
}
25+
26+
class UploadItem extends StatefulWidget {
27+
final File file;
28+
final String rename;
29+
30+
UploadItem(this.file, this.rename);
31+
32+
@override
33+
_UploadItemState createState() => _UploadItemState();
34+
}
35+
36+
class _UploadItemState extends State<UploadItem> implements UploadItemContract {
37+
UploadState _state;
38+
UploadItemPresenter _presenter;
39+
_UploadItemState() {
40+
_state = UploadState.Uploading;
41+
_presenter = new UploadItemPresenter(this);
42+
}
43+
44+
@override
45+
void initState() {
46+
super.initState();
47+
_startUpload();
48+
}
49+
50+
@override
51+
Widget build(BuildContext context) {
52+
return ListTile(
53+
leading: SizedBox(
54+
height: 50,
55+
width: 50,
56+
child: Card(
57+
clipBehavior: Clip.antiAlias,
58+
shape: RoundedRectangleBorder(
59+
borderRadius: BorderRadiusDirectional.circular(2)),
60+
child: ExtendedImage.file(
61+
File(widget.file.path),
62+
width: 50,
63+
height: 50,
64+
fit: BoxFit.cover,
65+
border: Border.all(color: Colors.grey, width: 1.0),
66+
borderRadius: BorderRadius.all(Radius.circular(8)),
67+
loadStateChanged: (state) => defaultLoadStateChanged(state),
68+
),
69+
),
70+
),
71+
title: Text(
72+
widget.rename,
73+
maxLines: 1,
74+
overflow: TextOverflow.ellipsis,
75+
textWidthBasis: TextWidthBasis.parent,
76+
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w400),
77+
),
78+
subtitle: Text(
79+
'上传状态:${_parseStateText()}',
80+
maxLines: 1,
81+
style: TextStyle(color: Colors.grey),
82+
),
83+
trailing: buildStateTip(),
84+
onTap: () {},
85+
);
86+
}
87+
88+
Widget buildStateTip() {
89+
switch (_state) {
90+
case UploadState.Uploading:
91+
case UploadState.Saving:
92+
return SizedBox(
93+
width: 16,
94+
height: 16,
95+
child: CupertinoActivityIndicator(),
96+
);
97+
case UploadState.Complete:
98+
return Icon(
99+
Icons.done,
100+
size: 16,
101+
);
102+
case UploadState.UploadFail:
103+
case UploadState.SaveFail:
104+
default:
105+
return IconButton(
106+
iconSize: 16,
107+
icon: Icon(
108+
Icons.error,
109+
color: Colors.red,
110+
),
111+
onPressed: () {
112+
_startUpload();
113+
});
114+
}
115+
}
116+
117+
/// 状态转文字
118+
String _parseStateText() {
119+
switch (_state) {
120+
case UploadState.Uploading:
121+
return '上传中';
122+
case UploadState.Saving:
123+
return '保存中';
124+
case UploadState.Complete:
125+
return '已完成';
126+
case UploadState.UploadFail:
127+
return '上传失败';
128+
case UploadState.SaveFail:
129+
return '保存失败';
130+
default:
131+
return '未知';
132+
}
133+
}
134+
135+
Widget defaultLoadStateChanged(ExtendedImageState state) {
136+
switch (state.extendedImageLoadState) {
137+
case LoadState.loading:
138+
return Center(
139+
child: SizedBox(
140+
width: 16,
141+
height: 16,
142+
child: CupertinoActivityIndicator(),
143+
),
144+
);
145+
break;
146+
case LoadState.failed:
147+
return GestureDetector(
148+
child: Stack(
149+
fit: StackFit.expand,
150+
alignment: AlignmentDirectional.center,
151+
children: <Widget>[
152+
Icon(
153+
Icons.error,
154+
size: 16,
155+
color: Colors.grey[600],
156+
)
157+
],
158+
),
159+
onTap: () {
160+
state.reLoadImage();
161+
},
162+
);
163+
break;
164+
default:
165+
return null;
166+
}
167+
}
168+
169+
/// 开始上传
170+
_startUpload() {
171+
setState(() {
172+
_state = UploadState.Uploading;
173+
});
174+
_presenter.doUploadImage(widget.file, widget.rename);
175+
}
176+
177+
@override
178+
uploadFaild(String errorMsg) {
179+
setState(() {
180+
_state = UploadState.UploadFail;
181+
});
182+
}
183+
184+
@override
185+
uploadSuccess(String url) {
186+
setState(() {
187+
_state = UploadState.Complete;
188+
});
189+
}
190+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import 'dart:io';
2+
import 'package:dio/dio.dart';
3+
import 'package:flutter/cupertino.dart';
4+
import 'package:flutter_picgo/utils/image_upload.dart';
5+
import 'package:flutter_picgo/utils/strategy/upload_strategy_factory.dart';
6+
7+
abstract class UploadItemContract {
8+
uploadSuccess(String url);
9+
uploadFaild(String errorMsg);
10+
}
11+
12+
class UploadItemPresenter {
13+
UploadItemContract _view;
14+
UploadItemPresenter(this._view);
15+
16+
/// 根据配置上传图片
17+
doUploadImage(File file, String renameImage) async {
18+
// 读取配置
19+
try {
20+
String pbType = await ImageUploadUtils.getDefaultPB();
21+
var uploader =
22+
ImageUploadUtils(UploadStrategyFactory.getUploadStrategy(pbType));
23+
var uploadedItem = await uploader.upload(file, renameImage);
24+
if (uploadedItem != null) {
25+
_view.uploadSuccess(uploadedItem.path);
26+
} else {
27+
_view.uploadFaild('上传失败!请重试');
28+
}
29+
} on DioError catch (e) {
30+
debugPrint(e.toString());
31+
_view.uploadFaild('${e.message}');
32+
} catch (e) {
33+
debugPrint(e.toString());
34+
_view.uploadFaild('$e');
35+
}
36+
}
37+
}

lib/components/uploaded_item.dart

Lines changed: 0 additions & 17 deletions
This file was deleted.

lib/routers/router_handler.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,13 @@ import 'package:flutter_picgo/views/pb_setting_page/smms_page/smms_page.dart';
1818
import 'package:flutter_picgo/views/pb_setting_page/tcyun_page/tcyun_page.dart';
1919
import 'package:flutter_picgo/views/pb_setting_page/upyun_page/upyun_page.dart';
2020
import 'package:flutter_picgo/views/picgo_setting_page/theme_setting_page.dart';
21+
import 'package:flutter_picgo/views/upload_page/handle_upload_page.dart';
2122
import 'package:flutter_picgo/views/upload_page/upload_page.dart';
2223
import 'package:flutter_picgo/views/pb_setting_page/github_page/github_page.dart';
2324
import 'package:flutter_picgo/views/picgo_setting_page/picgo_setting_page.dart';
2425
import 'package:flutter_picgo/views/setting_page/setting_page.dart';
2526
import 'package:flutter_picgo/views/404.dart';
27+
import 'package:wechat_assets_picker/wechat_assets_picker.dart';
2628

2729
var appHandler = new Handler(
2830
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
@@ -59,6 +61,14 @@ var uploadHandler = new Handler(
5961
UploadPage(),
6062
);
6163

64+
var preUploadHandler = new Handler(
65+
handlerFunc: (context, parameters) {
66+
final assets = context.settings.arguments as List<AssetEntity>;
67+
68+
return HandleUploadPage(assets);
69+
},
70+
);
71+
6272
// 图床设置页面
6373
var pbsettingHandler = new Handler(
6474
handlerFunc: (BuildContext context, Map<String, List<String>> params) =>

lib/routers/routers.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ class Routes {
88
static const String notfound = '/404';
99
static const String setting = '/setting';
1010
static const String upload = '/upload';
11+
static const String handleUpload = '/handle/upload';
1112
static const String settingPb = '/setting/pb';
1213
static const String settingPicgo = '/setting/picgo';
1314
static const String settingPicgoTheme = '/setting/picgo/theme';
@@ -50,6 +51,7 @@ class Routes {
5051
router.define(notfound, handler: notfoundHandler);
5152
router.define(album, handler: albumHandler);
5253
router.define(upload, handler: uploadHandler);
54+
router.define(handleUpload, handler: preUploadHandler);
5355
router.define(setting, handler: settingHandler);
5456
router.define(settingPb, handler: pbsettingHandler);
5557
router.define(settingPbGithub, handler: pbsettingGithubHandler);

lib/views/album_page/album_page.dart

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import 'package:permission_handler/permission_handler.dart';
1111
import 'package:toast/toast.dart';
1212
import 'package:flutter/services.dart';
1313
import 'package:pull_to_refresh/pull_to_refresh.dart';
14+
import 'package:wechat_assets_picker/wechat_assets_picker.dart';
1415

1516
class AlbumPage extends StatefulWidget {
1617
@override
@@ -45,13 +46,8 @@ class _AlbumPageState extends State<AlbumPage> implements AlbumPageContract {
4546
floatingActionButton: FloatingActionButton(
4647
child: Icon(IconData(0xe639, fontFamily: 'iconfont')),
4748
onPressed: () async {
48-
var status = await PermissionUtils.requestPhotos();
49-
if (status == PermissionStatus.denied) {
50-
PermissionUtils.showPermissionDialog(context);
51-
return;
52-
}
53-
Application.router.navigateTo(context, Routes.upload,
54-
transition: TransitionType.cupertino);
49+
/// assets select
50+
handleSelect();
5551
},
5652
),
5753
body: SmartRefresher(
@@ -226,6 +222,23 @@ class _AlbumPageState extends State<AlbumPage> implements AlbumPageContract {
226222
Toast.show('已复制到剪切板', context);
227223
}
228224

225+
/// 处理选择图片
226+
handleSelect() async {
227+
var status = await PermissionUtils.requestPhotos();
228+
if (status == PermissionStatus.denied) {
229+
PermissionUtils.showPermissionDialog(context);
230+
return;
231+
}
232+
final List<AssetEntity> assets = await AssetPicker.pickAssets(context);
233+
if (assets != null && assets.length > 0) {
234+
Application.router.navigateTo(context, Routes.handleUpload,
235+
transition: TransitionType.cupertino,
236+
routeSettings: RouteSettings(arguments: assets));
237+
} else {
238+
Toast.show('您已取消选择', context);
239+
}
240+
}
241+
229242
@override
230243
void loadUploadedImages(List<Uploaded> uploadeds) {
231244
setState(() {

0 commit comments

Comments
 (0)