You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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"));
          }
        },
      ),
    );
  }
}

核心要点说明

  1. Map转列表:通过currency.rates.entries.toList()将Map的键值对转换成List<MapEntry>,这样每个条目都包含货币代码(key)和汇率(value)。
  2. ListView构建:itemCount用_rateEntries!.length,itemBuilder里直接通过索引获取MapEntry,然后分别展示key和value。
  3. 数据状态处理:用FutureBuilder处理异步数据的加载、错误、成功三种状态,保证UI的流畅性。
  4. 格式化显示:用toStringAsFixed(6)把汇率格式化为6位小数,避免显示过长的数字。

如果你的数据不是通过Future异步获取的,而是已经同步拿到了Currency对象,只需要在setState里执行_rateEntries = currency.rates.entries.toList(),然后直接构建ListView即可。

内容的提问来源于stack exchange,提问作者Ceridoglu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 11:34:12