Flutter调用API获取用户列表失败,控制台报文件不存在错误
问题分析与解决方案
问题描述
尝试从指定API获取用户列表,界面无数据显示;控制台输出E/LB (26008): fail to open file: No such file or directory;打印response.statusCode无输出。
提供的代码
API配置
class ApiSetting{ static const String _baseUri='http://demo-api.mr-dev.tech/api/'; static const String users= '${_baseUri}users'; }
User模型
class User { late int id; late String firstName; late String lastName; late String email; late String mobile; late String bio; late String jobTitle; late String latitude; late String longitude; late String country; late String image; late String active; late String emailVerifiedAt; late String imagesCount; User.fromJson(Map<String, dynamic> json) { id = json['id']; firstName = json['first_name']; lastName = json['last_name']; email = json['email']; mobile = json['mobile']; bio = json['bio']; jobTitle = json['job_title']; latitude = json['latitude']; longitude = json['longitude']; country = json['country']; image = json['image']; active = json['active']; emailVerifiedAt = json['email_verified_at']; imagesCount = json['images_count']; } }
API控制器
import 'package:http/http.dart' as http; import 'dart:convert'; import 'package:api_secand_project/api/api_setting.dart'; import 'package:api_secand_project/models/user.dart'; class UserApiController { Future<List<User>> getUser() async { var uri = Uri.parse(ApiSetting.users); var response = await http.get(uri); if (response.statusCode == 200) { print(response.statusCode); var jsonResponse = jsonDecode(response.body); var userJsonArray = jsonResponse['data'] as List; return userJsonArray .map((jsonObject) => User.fromJson(jsonObject)) .toList(); } return []; } }
界面代码
import 'package:api_secand_project/api/controllers/user_api_controller.dart'; import 'package:api_secand_project/models/user.dart'; import 'package:flutter/material.dart'; class UsersScreen extends StatefulWidget { const UsersScreen({Key? key}) : super(key: key); @override State<UsersScreen> createState() => _UsersScreenState(); } class _UsersScreenState extends State<UsersScreen> { List<User> _users=<User>[]; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Users'), ), body: FutureBuilder<List<User>>( future: UserApiController().getUser(), builder: (context, snapshot) { if(snapshot.connectionState == ConnectionState.waiting){ return const Center( child: CircularProgressIndicator(), ); } else if(snapshot.hasData){ _users=snapshot.data!; return ListView.builder( itemCount: _users.length, itemBuilder: (context, index) { return ListTile( leading: CircleAvatar( radius: 30, // child: NetworkImage(snapshot.data!.), ), title: Text(_users[index].firstName), subtitle: Text(_users[index].mobile), ); }, ); } else{ return Center(child: Text('No Data',style: TextStyle(fontWeight: FontWeight.bold,fontSize: 28),),); } }, )); } }
可能原因及解决方案
1. 网络请求未处理异常与非200状态
- 原因:代码仅处理
statusCode == 200的情况,若请求抛出异常(如网络断开、域名无法解析),会直接终止流程,既不会执行print,也不会返回有效数据;非200状态直接返回空列表,无法排查问题。 - 解决方案:在API控制器中添加异常捕获,并打印所有状态码和错误信息:
class UserApiController { Future<List<User>> getUser() async { try { var uri = Uri.parse(ApiSetting.users); var response = await http.get(uri); // 打印所有状态码,方便排查 print('Response status: ${response.statusCode}'); if (response.statusCode == 200) { var jsonResponse = jsonDecode(response.body); var userJsonArray = jsonResponse['data'] as List; return userJsonArray.map((jsonObject) => User.fromJson(jsonObject)).toList(); } else { // 打印非200状态的响应体 print('Response body: ${response.body}'); return []; } } catch (e) { // 捕获并打印异常信息 print('Request error: $e'); return []; } } }
2. Android明文HTTP限制
- 原因:Android 9(API级别28)及以上默认禁止明文HTTP请求,API使用
http://协议会被系统拦截,这是请求失败的核心原因。 - 解决方案:
- 方法一:在
android/app/src/main/AndroidManifest.xml的<application>标签中添加属性:<application ... android:usesCleartextTraffic="true"> ... </application> - 方法二:若API支持HTTPS,将
ApiSetting中的_baseUri改为https://demo-api.mr-dev.tech/api/。
- 方法一:在
3. FutureBuilder错误处理缺失
- 原因:当前
FutureBuilder仅判断hasData,未处理请求失败(hasError)的情况,异常时界面仍显示"No Data",无法直观看到错误。 - 解决方案:添加错误处理分支:
builder: (context, snapshot) { if(snapshot.connectionState == ConnectionState.waiting){ return const Center(child: CircularProgressIndicator()); } else if(snapshot.hasData){ _users=snapshot.data!; return _users.isEmpty ? const Center(child: Text('暂无数据')) : ListView.builder( itemCount: _users.length, itemBuilder: (context, index) { return ListTile( leading: CircleAvatar( radius: 30, backgroundImage: NetworkImage(_users[index].image), ), title: Text(_users[index].firstName), subtitle: Text(_users[index].mobile), ); }, ); } else if(snapshot.hasError){ // 显示错误信息 return Center(child: Text('请求失败: ${snapshot.error}', textAlign: TextAlign.center,)); } else{ return const Center(child: Text('No Data',style: TextStyle(fontWeight: FontWeight.bold,fontSize: 28),)); } }
4. FutureBuilder重复发起请求
- 原因:每次
build方法执行时都会创建新的UserApiController().getUser(),导致重复发起网络请求,可能引发异常或数据混乱。 - 解决方案:将Future初始化放在
initState中:
class _UsersScreenState extends State<UsersScreen> { List<User> _users=<User>[]; late Future<List<User>> _futureUsers; @override void initState() { super.initState(); // 仅在初始化时发起一次请求 _futureUsers = UserApiController().getUser(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Users')), body: FutureBuilder<List<User>>( future: _futureUsers, // 使用初始化的Future builder: (context, snapshot) { // ... 原有builder逻辑 }, )); } }
关于控制台的fail to open file日志
该日志与API请求无关,属于系统或其他模块的日志,无需处理。
内容的提问来源于stack exchange,提问作者ِAhmad Shaheen
相关产品推荐
相关产品推荐

