Flutter异步API调用后Snackbar无法显示更新状态问题
Flutter图片上传后Snackbar无法正确显示API响应状态
问题描述
实现图片上传API功能后,希望通过Snackbar展示上传成功或失败的状态。在State类中定义了text变量,计划根据API响应结果更新后在Snackbar中显示,但存在两个问题:
- 点击ElevatedButton时,API调用与Snackbar展示同步执行,导致Snackbar显示的是
text的初始值,而非API返回的更新值 - 将Snackbar移至CreateAuditAPI函数内也无法触发显示
控制台显示API已成功上传,但状态提示异常。
相关代码
CreateAuditAPI函数
Future CreateAuditAPI(String userId, String warehouseId, String base64IMG, String description) async{ final Uri uri = Uri.parse("https://firebase.google.com/");/*sample url for getting response I know it wil not work*/ CreateAudit obj = CreateAudit( userID: userId, warehouseID: warehouseId, description: [description], imagedata: [base64IMG] ); var d = jsonEncode(obj); print("printing data before passing"); print(d); try { print("printing in try"); final http.Response response = await client.post(uri, headers: headers, body: d); Map<String, dynamic> jsonResponse = jsonDecode(response.body); if (jsonResponse["status"] == 200) { text = jsonResponse["message"]; return jsonResponse["data"]; } else { text = "Something Went Wrong"; throw Exception(jsonResponse["msg"]); } } catch(e) { print(e); } }
DisplayScreen完整代码
class DisplayScreen extends StatefulWidget { String imagePath; String warehouseid ; DisplayScreen(this.imagePath, this.warehouseid); @override State<DisplayScreen> createState() => _DisplayScreenState(); } class _DisplayScreenState extends State<DisplayScreen> { String? dropdownBuldingtype = ''; String? _imagData; String? userID; String? text='Uploaded Successfully'; Future<String> getExifFromFile() async { if (File(widget.imagePath) == null) { return ""; } final fileBytes = File(widget.imagePath)!.readAsBytesSync(); final data = await readExifFromBytes(fileBytes); if (data.isEmpty) { print("No EXIF information found"); return ""; } final datetime = data['EXIF DateTimeOriginal']?.toString(); if (datetime == null) { print("datetime information not found"); return ""; } String? datetime1; datetime1 = datetime; print(widget.imagePath); File imagefile = File(widget.imagePath); //convert Path to File Uint8List imagebytes = await imagefile.readAsBytes(); //convert to bytes _imagData = base64.encode(imagebytes); //convert bytes to base64 string return datetime1; } Future CreateAuditAPI(String userId, String warehouseId, String base64IMG, String description) async{ final Uri uri = Uri.parse("https://firebase.google.com/"); print('insidewarehouse'); print(uri); CreateAudit obj = CreateAudit( userID: userId, warehouseID: warehouseId, description: [description], imagedata: [base64IMG] ); var d = jsonEncode(obj); print("printing data before passing"); print(d); try { print("printing in try"); final http.Response response = await client.post(uri, headers: headers, body: d); Map<String, dynamic> jsonResponse = jsonDecode(response.body); if (jsonResponse["status"] == 200) { text = jsonResponse["message"]; return jsonResponse["data"]; } else { text = 'Something Went Wrong'; throw Exception(jsonResponse["msg"]); } } catch(e) { print(e); } } void getuserid() async{ SharedPreferences _prefs = await SharedPreferences.getInstance(); userID = _prefs.getString("userid"); } TextEditingController description = TextEditingController(); final client = http.Client(); final headers ={ 'Content-type': 'application/json' }; @override void initState() { getuserid(); super.initState(); } @override Widget build(BuildContext context) { print("attal======>"+widget.imagePath); getExifFromFile(); getuserid(); print("atal"); print(widget.imagePath); final size = MediaQuery .of(context) .size; final String _description = description.text; String? warehouseID = widget.warehouseid; print('warehiii'); print(warehouseID); print('useriiiiiii'); print(userID); return Scaffold( appBar: AppBar( backgroundColor: Colors.black, leading: IconButton(icon: const Icon(Icons.close, color: Colors.red, size: 50,), padding: EdgeInsets.all(4.0), onPressed: () {Navigator.pop(context); },), actions: [IconButton(icon: const Icon(Icons.check, color: Colors.green, size: 50,), padding: EdgeInsets.only(right: 4.0),onPressed: () { showDialog( context: context, builder: (ctx) => AlertDialog( actions: <Widget>[ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.end, children: [ IconButton(onPressed: (){ Navigator.of(ctx).pop(); }, icon: Icon(Icons.close, color: Colors.red,) ) ], ), Padding(padding: EdgeInsets.symmetric(horizontal: size.width*0.07), child: Text('Description : ', style: TextStyle(fontWeight: FontWeight.bold),), ), SizedBox(height: 20,), Padding( padding: EdgeInsets.symmetric(horizontal: size.width*0.07), child: TextFormField( controller: description, decoration: InputDecoration( focusedBorder: OutlineInputBorder(borderSide: BorderSide(color: Colors.black, width: 2)), enabledBorder: OutlineInputBorder(borderSide: BorderSide(color: Colors.grey, width: 1 ),), ), ), ), SizedBox(height: size.height*0.05,), Padding( padding: EdgeInsets.symmetric(horizontal: size.width*0.07), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ ElevatedButton(onPressed: (){}, child: Text('More Image'),style: ElevatedButton.styleFrom( primary: Colors.orange.withOpacity(0.8), ),), ElevatedButton(onPressed: (){CreateAuditAPI(userID!,warehouseID,_imagData!,description.text); ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text("$text"),action: SnackBarAction(label: 'OK',onPressed: (){ Navigator.pop(context); },),)); Navigator.pop(context); }, child: Text('Upload'), style: ElevatedButton.styleFrom( primary: Colors.orange.withOpacity(0.8), ),) ], ), ) ], ), ], ), );},)], ), backgroundColor: Colors.black, body: SizedBox.expand(child: Image.file(File(widget.imagePath), )), ); } }
问题核心代码片段
ElevatedButton(onPressed: (){CreateAuditAPI(userID!,warehouseID,_imagData!,description.text); ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text("$text"),action: SnackBarAction(label: 'OK',onPressed: (){ Navigator.pop(context); },),)); Navigator.pop(context); }, child: Text('Upload'), style: ElevatedButton.styleFrom( primary: Colors.orange.withOpacity(0.8), ),)
解决方案
1. 等待API执行完成后再显示Snackbar
CreateAuditAPI是异步函数,点击按钮时必须用await等待它执行完毕,才能获取到更新后的text值。同时添加错误捕获,确保上传失败时也能正确提示:
ElevatedButton( onPressed: () async { try { // 等待API调用完成 await CreateAuditAPI(userID!, warehouseID, _imagData!, description.text); // 确保Widget未被销毁再操作UI if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text("$text"), action: SnackBarAction( label: 'OK', onPressed: () { Navigator.pop(context); }, ), ), ); Navigator.pop(context); } } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text("上传失败: ${e.toString()}")), ); } } }, child: Text('Upload'), style: ElevatedButton.styleFrom( primary: Colors.orange.withOpacity(0.8), ), )
2. 优化API函数的状态更新逻辑
在API函数中更新text时调用setState,确保UI能感知到状态变化;同时重新抛出异常,让调用者可以捕获处理:
Future<void> CreateAuditAPI(String userId, String warehouseId, String base64IMG, String description) async { final Uri uri = Uri.parse("https://firebase.google.com/"); CreateAudit obj = CreateAudit( userID: userId, warehouseID: warehouseId, description: [description], imagedata: [base64IMG], ); var d = jsonEncode(obj); try { final http.Response response = await client.post(uri, headers: headers, body: d); Map<String, dynamic> jsonResponse = jsonDecode(response.body); if (jsonResponse["status"] == 200) { setState(() { text = jsonResponse["message"]; }); } else { setState(() { text = 'Something Went Wrong'; }); throw Exception(jsonResponse["msg"]); } } catch (e) { print(e); setState(() { text = '上传失败: ${e.toString()}'; }); rethrow; // 重新抛出异常,让按钮回调可以捕获 } }
3. 额外优化点
- 不要在
build方法中调用getExifFromFile和getuserid,这会导致每次build都重复执行异步操作,应该将这些逻辑移到initState中:
@override void initState() { super.initState(); getuserid(); getExifFromFile(); }
- 异步操作中始终检查
mounted状态,避免Widget销毁后调用setState或使用context导致崩溃。
内容的提问来源于stack exchange,提问作者Atalgupta
相关产品推荐
相关产品推荐

