如何通过Dio调用API并结合Provider展示旅游地点数据?
解决Dio+Provider获取并展示旅游地点数据的问题
现有代码的核心问题
- 类型不匹配:Provider中
_wisata定义为List<ListWisata>,但实际API返回的datawisata是List<Datawisata>,类型完全错误 - 不安全的空值访问:直接用
c!.datawisata!会导致空指针异常,未处理API请求失败或数据为空的情况 - 错误处理缺失:API请求的catch块仅执行
(e.toString()),无实际错误日志输出 - UI数据访问错误:
wisatalist[index]实际是Datawisata对象,需对应访问其属性
步骤1:修正API请求代码
import 'package:dio/dio.dart'; class WisataApi { // 复用Dio实例,避免重复创建 static final Dio _dio = Dio(); static Future<ListWisata?> getWisata() async { try { final result = await _dio.get('https://windramayu-api-default-rtdb.asia-southeast1.firebasedatabase.app/'); print('getWisata: ${result.data}'); return ListWisata.fromJson(result.data); } catch (e) { // 打印错误日志,方便调试 print('API请求失败: ${e.toString()}'); return null; } } }
步骤2:修正Provider状态管理类
import 'package:flutter/foundation.dart'; import 'your_model_file_path.dart'; // 替换为你的ListWisata/Datawisata模型文件路径 class HomeViewModel extends ChangeNotifier { // 正确定义为Datawisata列表 List<Datawisata> _wisata = []; // 返回不可变列表,防止外部直接修改 List<Datawisata> get wisatalist => List.unmodifiable(_wisata); Future<void> getWisatas() async { final response = await WisataApi.getWisata(); if (response?.datawisata != null) { _wisata = response!.datawisata!; notifyListeners(); // 通知UI更新 } else { // 数据为空时清空列表,避免旧数据残留 _wisata = []; notifyListeners(); } } }
步骤3:修正UI展示代码
import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'your_viewmodel_file_path.dart'; // 替换为HomeViewModel文件路径 class WisataListScreen extends StatefulWidget { const WisataListScreen({super.key}); @override State<WisataListScreen> createState() => _WisataListScreenState(); } class _WisataListScreenState extends State<WisataListScreen> { @override void initState() { super.initState(); // 页面初始化时调用API获取数据 Provider.of<HomeViewModel>(context, listen: false).getWisatas(); } @override Widget build(BuildContext context) { return Consumer<HomeViewModel>( builder: (context, modelView, child) { return ListView.builder( itemCount: modelView.wisatalist.length, scrollDirection: Axis.horizontal, itemBuilder: (context, index) { final wisata = modelView.wisatalist[index]; return Card( child: Padding( padding: const EdgeInsets.all(8.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 确保Datawisata类中有nama字段 Text(wisata.nama ?? '无名称'), Text(wisata.alamat ?? '无地址'), Text(wisata.about ?? '无介绍'), ], ), ), ); }, ); }, ); } }
步骤4:在main.dart中注册Provider
import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'your_viewmodel_file_path.dart'; import 'your_ui_screen_path.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return ChangeNotifierProvider( create: (context) => HomeViewModel(), child: MaterialApp( title: '旅游地点列表', home: const WisataListScreen(), ), ); } }
额外注意事项
- 确保
Datawisata类中包含nama字段(根据UI代码中的wisatalist[index].nama),如果quicktype生成时遗漏,需手动添加 - 若API返回的
datawisata是Map而非数组,需调整模型类和解析逻辑(但根据你提供的JSON结构是数组,当前模型是正确的) - 可添加加载状态(比如在ViewModel中增加
isLoading字段),提升UI体验
内容的提问来源于stack exchange,提问作者StarsHat
相关产品推荐
相关产品推荐

