Flutter从API获取多卡片数据显示异常排查求助
问题分析与解决方案
核心问题总结
你的代码存在几个关键问题导致显示异常:
- 全局列表未清空,重复请求时数据堆积
- 异步方法内错误调用
setState()引发UI提前重建 - ListView依赖全局列表长度而非
FutureBuilder返回的快照数据 - 所有列表项复用同一个
ScreenshotController,导致截图逻辑混乱 - ListView未正确处理父容器约束,
shrinkWrap设置不当引发布局空白
1. 修复数据获取逻辑
修改getData()方法,使用局部列表避免全局数据污染,移除不必要的setState():
List<DataDetails> multiCardData = []; Future<List<DataDetails>> getData() async { final sp_savedId = await SharedPreferences.getInstance(); final get_savedId = sp_savedId.getString("Key_1") ?? ""; final sp_savedPhone = await SharedPreferences.getInstance(); final get_savedPhone = sp_savedPhone.getString("Key_2") ?? ""; final response = await http.get(Uri.parse('你的API地址')); if (response.statusCode == 200) { final temp = jsonDecode(response.body.toString()); // 每次请求前清空全局列表,避免数据堆积 multiCardData.clear(); for (var userData in temp['data']) { final cardData = DataDetails() ..cardName = userData['cardName'].toString() ..id = userData['$get_savedId'] ..name = userData['name'].toString() ..designation = userData['designation'].toString() ..company = userData['company'].toString() ..address = userData['address'].toString() ..email = userData['email'].toString() ..phone = userData['phone'].toString() ..profilePhotoPath = userData['profilePhotoPath'].toString() ..qrCode = 'BEGIN:VCARD\nVERSION:3.0;\nN:${cardData.name};\nORG:${cardData.company}\nTITLE:${cardData.designation}\nADR:${cardData.address};;;;;;\nTEL;CELL:${cardData.phone}\nEMAIL:${cardData.email}\nEND:VCARD'; multiCardData.add(cardData); } return multiCardData; } else { multiCardData.clear(); return multiCardData; } }
2. 修复FutureBuilder与ListView逻辑
- 使用
snapshot.data的长度作为itemCount,而非全局列表 - 每个列表项创建独立的
ScreenshotController - 用
Expanded包裹ListView,解决shrinkWrap: false时的布局空白问题:
FutureBuilder( future: _futureBuilderData, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } if (!snapshot.hasData || snapshot.data!.isEmpty) { return const Center(child: Text("暂无数据")); } return Expanded( child: ListView.builder( shrinkWrap: false, itemCount: snapshot.data!.length, itemBuilder: (context, index) { final ssController = ScreenshotController(); final itemData = snapshot.data![index]; return Screenshot( controller: ssController, child: GestureDetector( onTap: () { Navigator.push(context, MaterialPageRoute(builder: (context) => OntapView())); }, child: Container( margin: const EdgeInsets.symmetric(vertical: 8, horizontal: 16), padding: const EdgeInsets.all(10), width: double.infinity, decoration: BoxDecoration( borderRadius: BorderRadius.circular(15), color: const Color(0xffccf7ff) ), child: Column( children: [ Container( padding: const EdgeInsets.all(10), child: Row( children: [ SizedBox( width: 200, child: Text(itemData.cardName, style: const TextStyle(fontWeight: FontWeight.bold)), ), const SizedBox(width: 20), SizedBox( height: 20, width: 90, child: Image.asset('asset/image.png'), ) ], ), ), const SizedBox(height: 8), Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ CircleAvatar( radius: 45, backgroundColor: Colors.grey, backgroundImage: NetworkImage(itemData.profilePhotoPath), ), const SizedBox(width: 10), SizedBox( width: 130, height: 100, child: Column( mainAxisAlignment: MainAxisAlignment.start, children: [ Text(itemData.name, style: const TextStyle(fontSize: 22, fontWeight: FontWeight.bold)), const SizedBox(height: 5), Text(itemData.designation, style: const TextStyle(fontSize: 12)), ], ), ), const SizedBox(width: 20), Container( width: MediaQuery.of(context).size.width * 0.08, height: MediaQuery.of(context).size.height * 0.08, decoration: BoxDecoration( shape: BoxShape.circle, color: Colors.blue[600] ), child: IconButton( onPressed: () async { final list = await ssController.capture(); if (list != null) { final directory = (await getTemporaryDirectory()).path; final imgFile = File('$directory/${DateTime.now().millisecondsSinceEpoch}.png'); imgFile.writeAsBytesSync(list); await Share.shareFiles([imgFile.path], text: 'Hi this is from qr code').catchError((e) { debugPrint('Error: $e'); }); } }, icon: const Icon(Icons.share, color: Colors.white, size: 18), ), ) ], ), Container( padding: const EdgeInsets.all(10), child: Row( children: [ SizedBox( height: 60, width: 200, child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text('Mob : +91 -${itemData.phone}'), Text('Email : ${itemData.email}'), Text('Work : ${itemData.company}') ], ), ), const SizedBox(width: 20), ClipRRect( borderRadius: BorderRadius.circular(10), child: SizedBox( height: 90, width: 90, child: QrImage( data: itemData.qrCode, backgroundColor: Colors.white, ), ), ) ], ), ) ], ), ), ), ); }, ), ); }, )
3. 额外优化建议
- 确保
_futureBuilderData只初始化一次,避免重复请求:
late Future<List<DataDetails>> _futureBuilderData; @override void initState() { super.initState(); _futureBuilderData = getData(); }
- 处理API返回的空值,使用
??运算符设置默认值,避免空指针异常 - 检查
userData['$get_savedId']的字段名是否与API返回一致,确保数据正确赋值
内容的提问来源于stack exchange,提问作者Nihar
相关产品推荐
相关产品推荐

