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

Flutter中如何用Map<String,dynamic>类型JSON访问snippet字段?

解决JSON结构变更后的Flutter数据访问问题

我来帮你一步步解决这个问题,先理清楚当前的问题点,再给出具体的修复方案和替代思路。

一、当前代码的问题分析与修复

你把list改成Map<String,dynamic>后报错,核心原因是数据访问路径写错了,还有字符串插值的语法错误,具体问题和修复如下:

1. 数据访问路径错误

原来的JSON是纯数组结构,直接用list[i]就能取到第i个元素;现在JSON是外层为对象、内部包含items数组的结构,需要先通过list['items']拿到内部数组,再用索引访问元素,也就是list['items'][i],而不是list[i]。

2. 字符串插值语法错误

在拼接YouTube嵌入链接时,你写的"https://youtube.com/embed/$list[i]['contentDetails']['videoId']"是错误的——复杂表达式的插值必须用${}包裹,正确写法是"https://youtube.com/embed/${list['items'][i]['contentDetails']['videoId']}"。

3. Null Safety 优化(可选但推荐)

现在Flutter默认启用空安全,建议给list参数加上required修饰,避免null异常,同时可以提前提取items数组,减少重复代码。

修复后的完整代码

class ListVideo extends StatelessWidget {
  final Map<String, dynamic> list;

  // 加上required确保参数必传,避免空安全警告
  const ListVideo({super.key, required this.list});

  @override
  Widget build(BuildContext context) {
    // 提前提取items数组,为空时返回空数组避免报错
    final items = list['items'] as List? ?? [];
    
    return ListView.builder(
      itemCount: items.length,
      itemBuilder: (context, i) {
        final videoItem = items[i];
        return Container(
          padding: const EdgeInsets.all(10.0),
          child: Column(
            children: <Widget>[
              GestureDetector(
                onTap: () => Navigator.of(context).push(
                  MaterialPageRoute(
                    builder: (BuildContext context) => VideoPlay(
                      url: "https://youtube.com/embed/${videoItem['contentDetails']['videoId']}",
                    ),
                  ),
                ),
                child: Container(
                  height: 210.0,
                  decoration: BoxDecoration(
                    image: DecorationImage(
                      image: NetworkImage(
                        videoItem['snippet']['thumbnails']['high']['url'],
                      ),
                      fit: BoxFit.cover,
                    ),
                  ),
                ),
              ),
              const Padding(padding: EdgeInsets.all(10.0)),
              Text(
                videoItem['snippet']['title'],
                style: const TextStyle(fontSize: 15.0),
              ),
              const Padding(padding: EdgeInsets.all(10.0)),
              const Divider(),
            ],
          ),
        );
      },
    );
  }
}

二、是否可以不用改用Map类型?

当然可以!你完全可以保持原来的List类型,只需要在获取JSON数据后,先提取出内部的items数组再传给ListVideo组件。

举个例子,假设你通过网络请求获取JSON数据:

// 假设拿到的响应数据是Map类型
Map<String, dynamic> responseMap = await fetchVideoData();
// 提取出items数组,转为List类型
List<dynamic> videoList = responseMap['items'] as List;
// 传给ListVideo组件,此时组件里的list还是List类型
Navigator.of(context).push(MaterialPageRoute(
  builder: (context) => ListVideo(list: videoList),
));

这样你的ListVideo组件几乎不用修改,还是用原来的List类型:

class ListVideo extends StatelessWidget {
  final List<dynamic> list;

  const ListVideo({super.key, required this.list});

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: list.length,
      itemBuilder: (context, i) {
        final videoItem = list[i];
        return Container(
          padding: const EdgeInsets.all(10.0),
          child: Column(
            children: <Widget>[
              GestureDetector(
                onTap: () => Navigator.of(context).push(
                  MaterialPageRoute(
                    builder: (BuildContext context) => VideoPlay(
                      url: "https://youtube.com/embed/${videoItem['contentDetails']['videoId']}",
                    ),
                  ),
                ),
                child: Container(
                  height: 210.0,
                  decoration: BoxDecoration(
                    image: DecorationImage(
                      image: NetworkImage(
                        videoItem['snippet']['thumbnails']['high']['url'],
                      ),
                      fit: BoxFit.cover,
                    ),
                  ),
                ),
              ),
              const Padding(padding: EdgeInsets.all(10.0)),
              Text(
                videoItem['snippet']['title'],
                style: const TextStyle(fontSize: 15.0),
              ),
              const Padding(padding: EdgeInsets.all(10.0)),
              const Divider(),
            ],
          ),
        );
      },
    );
  }
}

这种方式更贴合你原来的代码结构,不需要大范围修改组件内部逻辑,只需要在数据传入前做一次转换即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 07:11:37