Flutter中onTap异步逻辑await偶发失效,图片上传结果异常
问题:Flutter图片上传异步逻辑偶发失效,AddNewProductByUser.pic5为空
场景描述
使用ImagePicker实现图片选择后上传,上传结果需赋值给静态变量AddNewProductByUser.pic5并展示,同时存入SharedPreferences。异步逻辑写在InkWell的onTap回调中,约50%的场景下AddNewProductByUser.pic5会为空。
代码实现
InkWell的onTap回调
InkWell( onTap: () async { AddNewProductByUser.pic5 = await _getImageAndUpload(ImageSource.gallery); print('(success)AddNewProductByUser.pic5>>>>${AddNewProductByUser.pic5}'); localStorage ?? await SharedPreferences.getInstance(); localStorage?.setString("product_pic5", AddNewProductByUser.pic5); _selectedImageFileObj = null; setState(() {}); }, )
_getImageAndUpload方法
XFile? _selectedImageFileObj; final ImagePicker picker = ImagePicker(); SharedPreferences? localStorage; Future<String> _getImageAndUpload(ImageSource media) async { _selectedImageFileObj = await picker.pickImage(source: media); return await JJ.fileUpload(File(_selectedImageFileObj!.path)); }
JJ.fileUpload方法
static Future<String> fileUpload(File imageFile) async { try { String result = ''; var stream = http.ByteStream(imageFile.openRead()); stream.cast(); var length = await imageFile.length(); var uri = Uri.parse("${JJ.server}UploadServlet"); var request = http.MultipartRequest("POST", uri); var multipartFile = http.MultipartFile('file', stream, length, filename: basename(imageFile.path)); //contentType: new MediaType('image', 'png')); request.files.add(multipartFile); final response = await request.send(); print(response.statusCode); if (response.statusCode == 200) { response.stream.transform(utf8.decoder).listen((value) { print(value); result = value; }); return result; } jjToast('err 78'); return ''; } catch (e) { jjToast('err 81'); return ''; } }
异常现象
正常日志顺序
I/flutter (18058): (upload response code)200 (0) I/flutter (18058): (upload result)p7928552424.jpg (1) I/flutter (18058): (success)AddNewProductByUser.pic5>>>>p7928552424.jpg (2)
异常日志顺序(偶发)
I/flutter (18058): (upload response code)200 (0) I/flutter (18058): (success) AddNewProductByUser.pic5>>>> (2) I/flutter (18058): (upload result) p7928552424.jpg (1)
问题根源
核心问题出在JJ.fileUpload方法中:response.stream.transform(utf8.decoder).listen()是异步流订阅,它不会阻塞后续代码执行。当执行到return result时,listen的回调函数可能还没被触发(网络响应流尚未完全读取),此时result还是初始的空字符串,导致上层await _getImageAndUpload拿到空值,先执行了打印和赋值逻辑,之后listen回调才拿到上传结果并打印,出现日志顺序颠倒、变量为空的情况。
修复方案
将流订阅改为同步等待流完成,使用await response.stream.transform(utf8.decoder).join()来获取完整的响应内容,确保拿到结果后再返回。修改后的JJ.fileUpload方法如下:
static Future<String> fileUpload(File imageFile) async { try { var stream = http.ByteStream(imageFile.openRead()); stream.cast(); var length = await imageFile.length(); var uri = Uri.parse("${JJ.server}UploadServlet"); var request = http.MultipartRequest("POST", uri); var multipartFile = http.MultipartFile('file', stream, length, filename: basename(imageFile.path)); request.files.add(multipartFile); final response = await request.send(); print(response.statusCode); if (response.statusCode == 200) { // 同步等待流读取完成,获取完整响应内容 final result = await response.stream.transform(utf8.decoder).join(); print(result); return result; } jjToast('err 78'); return ''; } catch (e) { jjToast('err 81'); return ''; } }
额外优化建议
- 对
_selectedImageFileObj添加空值判断,避免用户取消图片选择时触发空指针异常:
Future<String> _getImageAndUpload(ImageSource media) async { _selectedImageFileObj = await picker.pickImage(source: media); if (_selectedImageFileObj == null) { // 用户取消选择,返回空或提示 return ''; } return await JJ.fileUpload(File(_selectedImageFileObj!.path)); }
localStorage ?? await SharedPreferences.getInstance()存在语法问题,正确写法应为:
localStorage ??= await SharedPreferences.getInstance();
内容的提问来源于stack exchange,提问作者MrSalesi
相关产品推荐
相关产品推荐

