使用 Flutter FutureBuilder 迭代 JSON 中的对象数组

Posted

技术标签:

【中文标题】使用 Flutter FutureBuilder 迭代 JSON 中的对象数组【英文标题】:Iterating over an array of objects in JSON with Flutter FutureBuilder 【发布时间】:2021-08-30 18:02:47 【问题描述】:

我在尝试使用 Flutter 的 FutureBuilder 从远程 URL 迭代 JSON 对象数组时遇到问题。

我的目标是:

从 API 获取 JSON 数据 将数据输出到 2 列 gridview 布局中

JSON 数据是一个对象数组(或 dart 中的 Maps 列表),对象具有简单的字符串数据。

我知道我需要构建一个 future 来从 API 获取数据并解码 JSON,然后我需要创建一个 FutureBuilder 来将 List 数据输出到我的 Gridview Builder 中。这就是我在下面的代码中尝试做的事情。

import 'dart:convert';
import 'dart:async';
import 'dart:core';

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;


class HomeSection extends StatefulWidget 
  @override
  _HomeSectionState createState() => _HomeSectionState();


class _HomeSectionState extends State<HomeSection> 
  @override
  void initState() 
    super.initState();
  

  Future<List<dynamic>> fetchSectionData() async 
    String dataUrl =
        'https://www.thisisthejsonapilink.co.uk/fetch-data';
    var response = await http.get(Uri.parse(dataUrl));
    if (response.statusCode == 200) 
      return jsonDecode(response.body);
     else 
      throw Exception('Failed to get the data');
    
  

  @override
  Widget build(BuildContext context) 

    return FutureBuilder(
      future: fetchSectionData,
      builder: (context, snapshot) 
        if (snapshot.connectionState == ConnectionState.done) 
          return GridView.builder(
            itemCount: 12,
            gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
              maxCrossAxisExtent: 300,
              crossAxisSpacing: 16.0,
              mainAxisSpacing: 18.0,
              childAspectRatio: MediaQuery.of(context).size.height / 930,
            ),
            itemBuilder: (BuildContext context, int index) => GestureDetector(
              onTap: () 
                Navigator.push(
                  context,
                  MaterialPageRoute(builder: (context) => OtherScreen()),
                );
              ,
              child: Column(
                children: [
                  Container(
                    margin: const EdgeInsets.only(bottom: 12.0),
                    height: 144,
                  ),
                  Text(
                    snapshot.data[index].title,
                  ),
                  Text(
                    snapshot.data[index].duration + ' - ' + snapshot.data[index].type,
                  ),
                ],
              ),
            ),
          );
         else 
          return Center(
            child: CircularProgressIndicator(
              color: Colors.black,
            ),
          );
        
      ,
    );
  

我从 API 链接返回的 JSON 结构如下:

[
  
    "Title": "Audio 1",
    "Duration": "5 min",
    "type": "audio",
    "bodyText": "lorem ipsum blah blah audio",
    "url": "https://www.silvermansound.com/music/dirty-gertie.mp3"
  ,
  
    "Title": "Video 1",
    "Duration": "5 min",
    "type": "video",
    "bodyText": "lorem ipsum blah blah video",
    "url": "https://assets.mixkit.co/videos/preview/mixkit-woman-wearing-a-mask-while-running-40158-large.mp4"
  ,
  
    "Title": "Audio 2",
    "Duration": "5 min",
    "type": "audio",
    "bodyText": "lorem ipsum blah blah audio",
    "url": "https://www.silvermansound.com/music/dirty-gertie.mp3"
  ,
  
    "Title": "Video 2",
    "Duration": "5 min",
    "type": "video",
    "bodyText": "lorem ipsum blah blah video",
    "url": "https://assets.mixkit.co/videos/preview/mixkit-woman-wearing-a-mask-while-running-40158-large.mp4"
  ,
  
    "Title": "Audio 3",
    "Duration": "5 min",
    "type": "audio",
    "bodyText": "lorem ipsum blah blah audio",
    "url": "https://www.silvermansound.com/music/dirty-gertie.mp3"
  ,
  
    "Title": "Video 3",
    "Duration": "5 min",
    "type": "video",
    "bodyText": "lorem ipsum blah blah video",
    "url": "https://assets.mixkit.co/videos/preview/mixkit-woman-wearing-a-mask-while-running-40158-large.mp4"
  ,
]

这是我在 VS 代码调试控制台中遇到的错误:

The argument type 'Future<List<dynamic>> Function()' can't be assigned to the parameter type 'Future<Object?>?'.

红线出现在我试图在 FutureBuilder 中定义我的未来的地方fetchSectionData

return FutureBuilder(
      future: fetchSectionData,
      builder: (context, snapshot) 

我不确定这个错误是什么意思。有人可以解释一下吗? Future 肯定会返回一个 ,但是如何将此列表放入 futurebuilder 中,以便我可以对其进行迭代并将数据输出到 gridview 中?

我是 Flutter 的新手,来自 javascript 网络背景,通常在 javascript 中,您可以循环遍历数组并以这种方式输出。我很想在这里这样做,但我知道那是不对的。

当我查看有关如何从互联网获取数据的 Flutter 文档时,它提到我必须将响应转换为自定义 dart 对象,但我尝试的任何方法似乎都不起作用。

感谢您的帮助!

【问题讨论】:

【参考方案1】:

试试这个!

return FutureBuilder<List<dynamic>>(
      future: fetchSectionData,
      builder: (context, snapshot) 
        if (snapshot.connectionState == ConnectionState.done) 
          return GridView.builder(
            itemCount: 12,
            gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
              maxCrossAxisExtent: 300,
              crossAxisSpacing: 16.0,
              mainAxisSpacing: 18.0,
              childAspectRatio: MediaQuery.of(context).size.height / 930,
            ),
            itemBuilder: (BuildContext context, int index) => GestureDetector(
              onTap: () 
                Navigator.push(
                  context,
                  MaterialPageRoute(builder: (context) => OtherScreen()),
                );
              ,
              child: Column(
                children: [
                  Container(
                    margin: const EdgeInsets.only(bottom: 12.0),
                    height: 144,
                  ),
                  Text(
                    '$snapshot.data[index]['Title']',
                  ),
                  Text(
                    '$snapshot.data[index]['Duration'] + ' - ' + snapshot.data[index]['type']',
                  ),
                ],
              ),
            ),
          );
         else 
          return Center(
            child: CircularProgressIndicator(
              color: Colors.black,
            ),
          );
        
      ,
    );

【讨论】:

我必须对其添加空检查并将底部更改为:$snapshot.data[index]['Duration'] + ' - ' + snapshot.data[index]['type'] 但它有效。我还必须记住属性名称区分大小写。谢谢!【参考方案2】:

你必须像这样传递函数

      future: fetchSectionData(),
最好在您的 GridView 中使用
...
itemCount: snapshot.data.length
...
List 等于 List it self
Future<List> fetchSectionData() async
...
最好确定 FutureBuilder 的类型
FutureBuilder<List>(
...

【讨论】:

itemCount: snapshot.data.length 我已经添加了这个,我在调试控制台中收到了这个错误:The property 'length' can't be unconditionally accessed because the receiver can be 'null'. Try making the access conditional (using '?.') or adding a null check to the target ('!'). snapshot.data[index].title, 行下我也收到错误The method '[]' can't be unconditionally invoked because the receiver can be 'null'. Try making the call conditional (using '?.') or adding a null check to the target ('!'). 我认为这是一个空安全的事情 @danny471 这是 dart null 安全功能的 bc,添加!给他们,例如。 snapshot.data![index] 快照.data!.length 我试过了,但我现在在调试控制台中收到此错误:════════ Exception caught by widgets library ═══════════════════════════════════ Class '_InternalLinkedHashMap&lt;String, dynamic&gt;' has no instance getter 'title'. Receiver: _LinkedHashMap len:5 Tried calling: title itemCount: snapshot.data.length 似乎确实有效,因为它在 json 中获得了正确数量的项目。所以JSON不回来不是问题,它似乎无法获取属性。

以上是关于使用 Flutter FutureBuilder 迭代 JSON 中的对象数组的主要内容,如果未能解决你的问题,请参考以下文章

Flutter StreamBuilder 与 FutureBuilder

使用 Mockito 的 FutureBuilder 快照数据为空 - Flutter

Flutter 使用 FutureBuilder 检索列表中的数据

FutureBuilder 能够获取数据,但使用 Flutter 多次获取数据

使用 FutureBuilder 和 Provider 在 Flutter 中出现错误状态没有元素

Flutter FutureBuilder 返回空数据