Flutter中如何从REST API获取数据并在ListView中展示
Flutter中从REST API获取数据并展示在ListView的解决方案
核心修改点
- 调整API数据解析逻辑,适配数组类型的返回结果
- 使用
FutureBuilder处理异步请求的状态(加载、错误、成功) - 解决Column嵌套ListView的布局溢出问题
修改后的完整代码
import 'dart:async'; import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; class TransactionDetails { final String avatar; final String name; final String date; final String amount; TransactionDetails({ required this.avatar, required this.name, required this.date, required this.amount, }); // 从单个JSON对象创建实例 factory TransactionDetails.fromJson(Map<String, dynamic> json) { return TransactionDetails( avatar: json['avatar'], name: json['name'], date: json['date'], amount: json['amount'], ); } // 从JSON数组转换为List<TransactionDetails> static List<TransactionDetails> fromJsonList(List<dynamic> jsonList) { return jsonList.map((json) => TransactionDetails.fromJson(json)).toList(); } } class BaseScreen extends StatelessWidget { const BaseScreen({Key? key}) : super(key: key); // 修改为返回列表类型的Future Future<List<TransactionDetails>> fetchTransactions() async { final response = await http.get( Uri.parse('https://brotherlike-navies.000webhostapp.com/people/people.php'), ); if (response.statusCode == 200) { final List<dynamic> jsonData = json.decode(response.body); return TransactionDetails.fromJsonList(jsonData); } else { throw Exception('请求失败,请检查网络或API地址'); } } @override Widget build(BuildContext context) { return Scaffold( body: SafeArea( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( width: double.infinity, height: 150, child: ListView( scrollDirection: Axis.horizontal, children: [ Container( margin: const EdgeInsets.all(15), width: 319, height: 100, color: Colors.green, alignment: Alignment.center, child: const Text( '\$5200.00', style: TextStyle( fontSize: 15, color: Colors.white, fontWeight: FontWeight.bold, ), ), ), Container( margin: const EdgeInsets.all(15), width: 319, height: 100, color: Colors.green, alignment: Alignment.center, child: const Text( '\$1200.00', style: TextStyle( fontSize: 15, color: Colors.white, fontWeight: FontWeight.bold, ), ), ), ], ), ), const Padding( padding: EdgeInsets.all(15), child: Text( "Recent Transactions", style: TextStyle( fontSize: 14, fontWeight: FontWeight.bold, color: Colors.green, ), ), ), // 用Expanded包裹ListView,解决Column布局溢出问题 Expanded( child: FutureBuilder<List<TransactionDetails>>( future: fetchTransactions(), builder: (context, snapshot) { // 加载中状态 if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } // 请求错误状态 else if (snapshot.hasError) { return Center(child: Text('错误: ${snapshot.error}')); } // 数据加载成功 else if (snapshot.hasData && snapshot.data!.isNotEmpty) { return ListView.builder( itemCount: snapshot.data!.length, itemBuilder: (context, index) { final transaction = snapshot.data![index]; return ListTile( leading: CircleAvatar( backgroundImage: NetworkImage(transaction.avatar), ), title: Text(transaction.name), subtitle: Text(transaction.date), trailing: Text( '\$${transaction.amount}', style: const TextStyle( fontWeight: FontWeight.bold, color: Colors.green, ), ), ); }, ); } // 空数据状态 else { return const Center(child: Text('暂无交易记录')); } }, ), ), ], ), ), ); } }
关键说明
- 数据解析调整:新增
fromJsonList静态方法,将API返回的JSON数组转换为List<TransactionDetails>,同时修改fetchTransactions方法返回列表类型的Future。 - FutureBuilder的使用:通过
FutureBuilder监听异步请求的状态,分别处理加载中、错误、成功、空数据四种场景,确保UI状态和数据状态同步。 - 布局问题解决:用
Expanded包裹ListView,让它在Column中占据剩余空间,避免布局溢出;同时使用ListView.builder实现懒加载,提升列表性能。
内容的提问来源于stack exchange,提问作者Mike
相关产品推荐
相关产品推荐

