Flutter中调用null对象的data getter引发No Such Method Error求助
解决Flutter/Dart中No Such Method Error(The getter data was called on null)的问题
核心问题分析
错误源于对JSON数据结构的访问逻辑错误,以及FutureBuilder使用不当,导致尝试在null对象上调用data getter。下面是具体修复步骤:
步骤1:修复API调用中的冗余代码
response.statusCode是同步属性,无需加await,直接判断即可:
Future<Map<String, dynamic>> fetchBuildNews() async { final prefs = await SharedPreferences.getInstance(); accessToken = prefs.getString('access_Token') ?? ''; final Map<String, String> headers = { 'Content-Type': 'application/json', 'Authorization': 'Bearer $accessToken', }; final response = await http.get(Uri.parse('https://idonor.ae/api/latest-news'), headers: headers); // 移除多余的await if (response.statusCode == 200) { final items = Map<String, dynamic>.from(json.decode(response.body)); return items; } else { throw Exception('Failed to load data'); } }
步骤2:修正FutureBuilder的数据访问逻辑
JSON根对象是包含success、data、message的Map,data下的news才是新闻数组。之前错误地将根Map当作数组处理,导致索引访问错误。修改buildNews方法:
Widget buildNews() => Container( width: 300, height: 185, child: FutureBuilder<Map<String, dynamic>>( future: fetchBuildNews(), builder: (context, snapshot) { if (snapshot.hasData) { // 正确获取新闻数组 final List<dynamic> newsList = snapshot.data!['data']['news']; return ListView.builder( shrinkWrap: true, scrollDirection: Axis.horizontal, itemCount: newsList.length, // 用新闻数组长度作为itemCount itemBuilder: (context, index) { final newsItem = newsList[index]; return Container( width: 300, height: 185, child: Container( height: 185.0, width: MediaQuery.of(context).size.width - 100.0, decoration: BoxDecoration( borderRadius: BorderRadius.circular(5), color: Colors.blue, image: DecorationImage( image: NetworkImage( newsItem['news_image'] ?? '' // 匹配JSON中的下划线key ), fit: BoxFit.cover)), child: Center( child: Text( newsItem['title'] ?? 'No Title', style: TextStyle( fontFamily: 'Poppins, SemiBold', color: '#336699'.hexToColor(), fontSize: 18, fontWeight: FontWeight.w700), textAlign: TextAlign.center, ), ), )); }, ); } else if (snapshot.hasError) { print('${snapshot.error}'); return Text('${snapshot.error}'); } return CircularProgressIndicator(); }, ), );
步骤3:优化FutureBuilder避免重复请求
直接在FutureBuilder的future参数中调用API会导致每次组件重建都发起请求,建议将future实例存入State变量:
// 在State类中定义变量 late Future<Map<String, dynamic>> _newsFuture; @override void initState() { super.initState(); // 仅在初始化时调用一次API _newsFuture = fetchBuildNews(); } // 后续在buildNews中使用_newsFuture Widget buildNews() => Container( width: 300, height: 185, child: FutureBuilder<Map<String, dynamic>>( future: _newsFuture, // 其余代码保持不变 ), );
可选:使用模型类提升数据安全性
为避免手动解析Map时的key拼写错误和空指针问题,推荐用json_serializable生成模型类:
- 添加依赖到
pubspec.yaml:
dependencies: json_annotation: ^4.8.1 dev_dependencies: build_runner: ^2.4.4 json_serializable: ^6.7.0
- 创建模型类:
import 'package:json_annotation/json_annotation.dart'; part 'news_response.g.dart'; @JsonSerializable() class NewsResponse { final bool success; final NewsData data; final String message; NewsResponse({required this.success, required this.data, required this.message}); factory NewsResponse.fromJson(Map<String, dynamic> json) => _$NewsResponseFromJson(json); Map<String, dynamic> toJson() => _$NewsResponseToJson(this); } @JsonSerializable() class NewsData { final List<NewsItem> news; NewsData({required this.news}); factory NewsData.fromJson(Map<String, dynamic> json) => _$NewsDataFromJson(json); Map<String, dynamic> toJson() => _$NewsDataToJson(this); } @JsonSerializable() class NewsItem { final int id; @JsonKey(name: 'user_id') final int userId; final String title; final String content; @JsonKey(name: 'published_at') final String publishedAt; @JsonKey(name: 'created_at') final String createdAt; @JsonKey(name: 'updated_at') final String updatedAt; @JsonKey(name: 'news_image') final String newsImage; NewsItem({ required this.id, required this.userId, required this.title, required this.content, required this.publishedAt, required this.createdAt, required this.updatedAt, required this.newsImage, }); factory NewsItem.fromJson(Map<String, dynamic> json) => _$NewsItemFromJson(json); Map<String, dynamic> toJson() => _$NewsItemToJson(this); }
- 运行生成命令:
flutter pub run build_runner build
- 修改API调用返回模型类:
Future<NewsResponse> fetchBuildNews() async { final prefs = await SharedPreferences.getInstance(); accessToken = prefs.getString('access_Token') ?? ''; final Map<String, String> headers = { 'Content-Type': 'application/json', 'Authorization': 'Bearer $accessToken', }; final response = await http.get(Uri.parse('https://idonor.ae/api/latest-news'), headers: headers); if (response.statusCode == 200) { return NewsResponse.fromJson(json.decode(response.body)); } else { throw Exception('Failed to load data'); } }
- 更新
FutureBuilder逻辑:
Widget buildNews() => Container( width: 300, height: 185, child: FutureBuilder<NewsResponse>( future: _newsFuture, builder: (context, snapshot) { if (snapshot.hasData) { final newsList = snapshot.data!.data.news; return ListView.builder( shrinkWrap: true, scrollDirection: Axis.horizontal, itemCount: newsList.length, itemBuilder: (context, index) { final newsItem = newsList[index]; return Container( width: 300, height: 185, child: Container( height: 185.0, width: MediaQuery.of(context).size.width - 100.0, decoration: BoxDecoration( borderRadius: BorderRadius.circular(5), color: Colors.blue, image: DecorationImage( image: NetworkImage(newsItem.newsImage), fit: BoxFit.cover)), child: Center( child: Text( newsItem.title, style: TextStyle( fontFamily: 'Poppins, SemiBold', color: '#336699'.hexToColor(), fontSize: 18, fontWeight: FontWeight.w700), textAlign: TextAlign.center, ), ), )); }, ); } else if (snapshot.hasError) { print('${snapshot.error}'); return Text('${snapshot.error}'); } return CircularProgressIndicator(); }, ), );
内容的提问来源于stack exchange,提问作者AzeTech
相关产品推荐
相关产品推荐

