Flutter无法获取团队数据,报错Failed to fetch team data(附代码)
Flutter获取团队数据失败问题修复
问题现象
从后端接口获取团队数据时抛出异常:
ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: Exception: Failed to fetch team data
现有代码
TeamPage
import 'dart:convert'; import 'package:crm/models/base_url.dart'; import 'package:crm/models/team.dart'; import 'package:crm/profile/profil.dart'; import 'package:crm/screens/team/widgets/team_widget.dart'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import '../home/home_widgets/navigation_drawer_widget.dart'; class TeamPage extends StatefulWidget { @override _TeamPageState createState() => _TeamPageState(); } class _TeamPageState extends State<TeamPage> { GlobalKey<ScaffoldState> _scaffoldKEY = GlobalKey<ScaffoldState>(); late Future<List<Team>> _teamData; @override void initState() { super.initState(); _teamData = fetchTeams(); } Future<List<Team>> fetchTeams() async { final response = await http.get(Uri.parse('${BaseUrl.BASE_URL}/flutter-api/database')); if (response.statusCode == 200) { final List<dynamic> data = jsonDecode(response.body); return data.map((json) => Team.fromJson(json)).toList(); } else { throw Exception('Failed to fetch team data'); } } @override Widget build(BuildContext context) { return Scaffold( key: _scaffoldKEY, drawer: NavigationDrawerWidget(), body: Container( decoration: BoxDecoration( image: DecorationImage( image: AssetImage("assets/images/bg.png"), fit: BoxFit.cover, ), ), child: SingleChildScrollView( child: Column( children: [ Padding( padding: EdgeInsets.all(10.0), child: Row( children: [ SizedBox( height: 90, ), IconButton( icon: Icon( Icons.menu_outlined, color: Colors.white, ), onPressed: () => _scaffoldKEY.currentState?.openDrawer(), ), Text( "Team", style: TextStyle(color: Colors.white, fontSize: 20), ), Spacer(), Icon( Icons.add_alert_outlined, color: Colors.transparent, ), SizedBox( width: MediaQuery.of(context).size.width * 0.05, ), InkWell( onTap: () { Navigator.of(context).push( MaterialPageRoute( builder: (context) => PorfilePage(), ), ); }, child: Icon( Icons.person, color: Colors.white, ), ), SizedBox( width: MediaQuery.of(context).size.width * 0.05, ), ], ), ), SizedBox( height: MediaQuery.of(context).size.height * 0.04, ), Container( height: MediaQuery.of(context).size.height * 0.045, width: MediaQuery.of(context).size.width * 0.87, child: Row( children: [ SizedBox( width: MediaQuery.of(context).size.width * 0.04, ), Icon(Icons.search_outlined, color: Colors.white), ], ), decoration: BoxDecoration( color: Colors.teal.shade200, borderRadius: BorderRadius.circular(15), boxShadow: [ BoxShadow( blurRadius: 10, spreadRadius: 5, offset: Offset(1, 1), color: Colors.grey.withOpacity(0.1), ), ], ), ), SizedBox( height: MediaQuery.of(context).size.height * 0.04, ), FutureBuilder<List<Team>>( future: fetchTeams(), builder: (context, snapshot) { if (snapshot.hasData) { final List<Team> teams = snapshot.data!; return TeamWidget(team: teams.first); } else if (snapshot.hasError) { return Text('Error: ${snapshot.error}'); } return CircularProgressIndicator(); }, ), ] ) ) ) ); } }
TeamWidget
import 'package:crm/models/team.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:percent_indicator/linear_percent_indicator.dart'; import '../../../../route/route_helper.dart'; class TeamWidget extends StatelessWidget { final Team team; TeamWidget({required this.team}); @override Widget build(BuildContext context) { return SingleChildScrollView( child: Container( height: MediaQuery.of(context).size.height*0.8, width: MediaQuery.of(context).size.width, child: ListView.builder( itemCount:9, itemBuilder: (BuildContext context, int index){ return Padding( padding: EdgeInsets.all(6.0), child: ListTile( title: Container( height:MediaQuery.of(context).size.height*0.15, width:MediaQuery.of(context).size.width*0.8 , child: Row( children: [ Column( children: [ SizedBox(height:MediaQuery.of(context).size.height*0.06,), Row( children: [ SizedBox( width:MediaQuery.of(context).size.width*0.02 ,), CircleAvatar( radius: 25, backgroundImage: AssetImage('assets/images/1.png'), ), ], ), ], ), SizedBox( width:MediaQuery.of(context).size.width*0.03 ,), VerticalDivider( color: Colors.black26, width: 1, thickness: 1, indent: 8, endIndent: 8, ), SizedBox( width:MediaQuery.of(context).size.width*0.03 ,), Column( children: [ SizedBox(width:MediaQuery.of(context).size.width*0.03 ,), SizedBox(height:MediaQuery.of(context).size.height*0.05,), Text('Name: ${team.name}',style: TextStyle(fontSize: 18),), SizedBox( width:MediaQuery.of(context).size.width*0.03 ,), Text('Email: ${team.email}',style: TextStyle(fontSize: 12,color: Colors.grey.shade400,),), SizedBox( width:MediaQuery.of(context).size.width*0.03 ,), Text('Phone : ${team.phone }',style: TextStyle(color:Colors.black54,fontSize: 13 ),) ], ), Spacer(), Column( children: [ SizedBox(height:MediaQuery.of(context).size.height*0.055,), Icon(Icons.remove_circle,color: Colors.red,) ], ), SizedBox( width:MediaQuery.of(context).size.width*0.03 ,), ], ), decoration: BoxDecoration( color:Colors.white, borderRadius: BorderRadius.circular(15), boxShadow: [ BoxShadow( blurRadius: 10, spreadRadius: 5, offset: Offset(1, 1), color:Colors.grey.withOpacity(0.1) ) ] ), ), ), ); } ), decoration: BoxDecoration( color:Colors.grey.shade100, borderRadius: BorderRadius.only(topRight: Radius.circular(20),topLeft: Radius.circular(20)), ), ), ); } }
Team类
class Team { late final int id; late final String name; late final String email; late final double phone; Team({ required this.id, required this.name, required this.email, required this.phone, }); factory Team.fromJson(Map<String, dynamic> json) { return Team( id: json['id'], name: json['name'], email: json['email'], phone: json['phone'], ); } }
BaseUrl
// ignore_for_file: non_constant_identifier_names class BaseUrl { static String BASE_URL = "http://mermez.site/flutter-api"; }
修复步骤
1. 修正API请求地址
BaseUrl已包含/flutter-api,原代码重复拼接路径导致请求地址错误,修改fetchTeams方法:
Future<List<Team>> fetchTeams() async { final response = await http.get(Uri.parse('${BaseUrl.BASE_URL}/database')); if (response.statusCode == 200) { final List<dynamic> data = jsonDecode(response.body); return data.map((json) => Team.fromJson(json)).toList(); } else { // 补充状态码信息,便于排查 throw Exception('Failed to fetch team data. Status code: ${response.statusCode}'); } }
2. 优化FutureBuilder的Future参数
避免每次build重复发起请求,使用initState中初始化的_teamData:
FutureBuilder<List<Team>>( future: _teamData, builder: (context, snapshot) { if (snapshot.hasData) { final List<Team> teams = snapshot.data!; return TeamWidget(teams: teams); } else if (snapshot.hasError) { return Text('Error: ${snapshot.error}'); } return CircularProgressIndicator(); }, ),
3. 修正Team类的phone字段类型
手机号通常为字符串(可能包含特殊前缀),将double改为String:
class Team { late final int id; late final String name; late final String email; late final String phone; Team({ required this.id, required this.name, required this.email, required this.phone, }); factory Team.fromJson(Map<String, dynamic> json) { return Team( id: json['id'], name: json['name'], email: json['email'], phone: json['phone'].toString(), ); } }
4. 修复TeamWidget的列表展示逻辑
修改TeamWidget接收团队列表,根据实际数据长度渲染:
class TeamWidget extends StatelessWidget { final List<Team> teams; TeamWidget({required this.teams}); @override Widget build(BuildContext context) { return Container( height: MediaQuery.of(context).size.height*0.8, width: MediaQuery.of(context).size.width, child: ListView.builder( itemCount: teams.length, itemBuilder: (BuildContext context, int index){ final team = teams[index]; return Padding( padding: EdgeInsets.all(6.0), child: ListTile( title: Container( height:MediaQuery.of(context).size.height*0.15, width:MediaQuery.of(context).size.width*0.8 , child: Row( children: [ Column( children: [ SizedBox(height:MediaQuery.of(context).size.height*0.06,), Row( children: [ SizedBox( width:MediaQuery.of(context).size.width*0.02 ,), CircleAvatar( radius: 25, backgroundImage: AssetImage('assets/images/1.png'), ), ], ), ], ), SizedBox( width:MediaQuery.of(context).size.width*0.03 ,), VerticalDivider( color: Colors.black26, width: 1, thickness: 1, indent: 8, endIndent: 8, ), SizedBox( width:MediaQuery.of(context).size.width*0.03 ,), Column( children: [ SizedBox(width:MediaQuery.of(context).size.width*0.03 ,), SizedBox(height:MediaQuery.of(context).size.height*0.05,), Text('Name: ${team.name}',style: TextStyle(fontSize: 18),), SizedBox( width:MediaQuery.of(context).size.width*0.03 ,), Text('Email: ${team.email}',style: TextStyle(fontSize: 12,color: Colors.grey.shade400,),), SizedBox( width:MediaQuery.of(context).size.width*0.03 ,), Text('Phone : ${team.phone }',style: TextStyle(color:Colors.black54,fontSize: 13 ),) ], ), Spacer(), Column( children: [ SizedBox(height:MediaQuery.of(context).size.height*0.055,), Icon(Icons.remove_circle,color: Colors.red,) ], ), SizedBox( width:MediaQuery.of(context).size.width*0.03 ,), ], ), decoration: BoxDecoration( color:Colors.white, borderRadius: BorderRadius.circular(15), boxShadow: [ BoxShadow( blurRadius: 10, spreadRadius: 5, offset: Offset(1, 1), color:Colors.grey.withOpacity(0.1) ) ] ), ), ), ); } ), decoration: BoxDecoration( color:Colors.grey.shade100, borderRadius: BorderRadius.only(topRight: Radius.circular(20),topLeft: Radius.circular(20)), ), ); } }
内容的提问来源于stack exchange,提问作者Iman Alabid
相关产品推荐
相关产品推荐

