You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 08:02:08