Flutter:如何在ListView.builder展示Map<String,double>类型汇率数据
解决Flutter ListView.builder展示JSON中rates键值对的问题
嘿,我来帮你搞定这个问题!你已经完成了最核心的JSON解析工作,现在只需要把rates这个Map结构转换成ListView能处理的列表形式就行——毕竟ListView.builder需要一个可索引的集合来生成每一项内容。
关键思路:把Map转换成可遍历的列表
rates是一个Map<String, double>,直接用它没法给ListView提供索引。我们可以把它的键值对转换成List<MapEntry<String, double>>,这样每个MapEntry就对应一个货币代码和汇率,完美适配ListView的需求。
完整代码示例
下面是整合后的State类代码,包含数据获取、转换和ListView展示的全流程:
import 'package:flutter/material.dart'; import 'dart:convert'; import 'package:http/http.dart' as http; // 这里放你用quicktype生成的Currency模型类 Currency currencyFromJson(String str) => Currency.fromJson(json.decode(str)); String currencyToJson(Currency data) => json.encode(data.toJson()); class Currency { bool success; int timestamp; String base; DateTime date; Map<String, double> rates; Currency({ this.success, this.timestamp, this.base, this.date, this.rates, }); factory Currency.fromJson(Map<String, dynamic> json) => Currency( success: json["success"], timestamp: json["timestamp"], base: json["base"], date: DateTime.parse(json["date"]), rates: Map.from(json["rates"]) .map((k, v) => MapEntry<String, double>(k, v.toDouble())), ); Map<String, dynamic> toJson() => { "success": success, "timestamp": timestamp, "base": base, "date": "${date.year.toString().padLeft(4, '0')}-${date.month.toString().padLeft(2, '0')}-${date.day.toString().padLeft(2, '0')}", "rates": Map.from(rates) .map((k, v) => MapEntry<String, dynamic>(k, v)), }; } class CurrencyScreen extends StatefulWidget { @override _CurrencyScreenState createState() => _CurrencyScreenState(); } class _CurrencyScreenState extends State<CurrencyScreen> { Future<Currency>? _currencyFuture; List<MapEntry<String, double>>? _rateEntries; @override void initState() { super.initState(); // 启动数据获取请求 _currencyFuture = _fetchCurrencyData(); } Future<Currency> _fetchCurrencyData() async { // 替换成你的API地址 final response = await http.get(Uri.parse("https://your-api-url.com")); if (response.statusCode == 200) { final currency = currencyFromJson(response.body); // 把Map的键值对转换成列表,方便ListView使用 setState(() { _rateEntries = currency.rates.entries.toList(); }); return currency; } else { throw Exception("Failed to load currency data"); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text("Currency Rates")), body: FutureBuilder<Currency>( future: _currencyFuture, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { // 加载中的UI return const Center(child: CircularProgressIndicator()); } else if (snapshot.hasError) { // 错误状态UI return Center(child: Text("Error: ${snapshot.error}")); } else if (snapshot.hasData && _rateEntries != null) { // 成功获取数据,展示ListView return ListView.builder( padding: const EdgeInsets.all(16), itemCount: _rateEntries!.length, itemBuilder: (context, index) { final rateItem = _rateEntries![index]; return ListTile( title: Text(rateItem.key, style: const TextStyle(fontWeight: FontWeight.bold)), trailing: Text(rateItem.value.toStringAsFixed(6)), // 可以添加点击事件等交互 onTap: () { print("Selected ${rateItem.key}: ${rateItem.value}"); }, ); }, ); } else { // 兜底的空状态 return const Center(child: Text("No data available")); } }, ), ); } }
核心要点说明
- Map转列表:通过
currency.rates.entries.toList()将Map的键值对转换成List<MapEntry>,这样每个条目都包含货币代码(key)和汇率(value)。 - ListView构建:
itemCount用_rateEntries!.length,itemBuilder里直接通过索引获取MapEntry,然后分别展示key和value。 - 数据状态处理:用
FutureBuilder处理异步数据的加载、错误、成功三种状态,保证UI的流畅性。 - 格式化显示:用
toStringAsFixed(6)把汇率格式化为6位小数,避免显示过长的数字。
如果你的数据不是通过Future异步获取的,而是已经同步拿到了Currency对象,只需要在setState里执行_rateEntries = currency.rates.entries.toList(),然后直接构建ListView即可。
内容的提问来源于stack exchange,提问作者Ceridoglu
相关产品推荐
相关产品推荐

