Flutter - 如何更新用于构建 ListView 的 Future/List 的状态(或值?)(通过 FutureBuilder)

2024-04-03

我在下面粘贴了相关代码,但您也许可以根据我的伪解释来回答。

我使用 FutureBuilder 来构建列表视图。

  • 我首先使用 init() 异步 HTTP 调用 API,并将其解析为映射以表示 json 结果的对象列表(位置)。
  • 然后,该位置列表将返回到Future<List<Location>> _listFuture变量(这是 FutureBuilder 的未来)。
  • 一旦 future“返回”或“完成”,FutureBuilder 就会启动并使用 ListView.builder/Container/ListTile 循环并构建列表。
  • 在某些时候,我需要一个 onTap() 处理程序(在 ListTile 中)来更改所选列表项的背景颜色。
  • 为了支持这一点,我在 Location 类(保存 JSON 响应)中有一个 backgroundColor 成员,我对所有项目默认为“#fc7303”(假设所有内容最初总是未选中的)。然后我想将 onTap() 中选择的任何内容的背景更改为“#34bdeb”。
  • 我假设我可以调用 setState() ,这将触发刷新,并且新的背景颜色将在重绘时被注意到/使用。

问题是 ListView/Contrainer/ListTile 是由

Future<List<Location>>

。我可以将“tapped”索引传递给我的 ontap 处理程序,但我不相信我可以让我的 _changeBackground() 只需更新所选索引的 backgroundColor 值并调用 setState() 因为您无法直接访问/更新这样的未来(我收到错误ERROR: The operator '[]' isn't defined for the class 'Future<List<Location>>'.)

我不确定我采取了正确的方法。在这种情况下,我想理论上我总是可以将“背景”颜色跟踪分离到一个新的单独列表中(在未来之外),并使用 onTap() 中的对齐索引以这种方式跟踪/引用它。

但是,我不确定这总是有效。将来,我可能需要实际更改将来返回的值/状态。例如,考虑一下我是否希望能够单击列表项并更新“companyName”。在这种情况下,我将直接更改未来存储的值。我想我可以从技术上将新名称发送到服务器并以这种方式完全刷新列表,但这似乎效率低下(如果他们决定“取消”而不保存更改怎么办?)。

任何帮助表示赞赏。谢谢!

这个类实际上保存了列表的相关数据

// Location
class Location {

  // members
  String locationID;
  String locationName;
  String companyName;
  String backgroundColor = 'fc7303';

  // constructor?
  Location({this.locationID, this.locationName, this.companyName});

  // factory?
  factory Location.fromJson(Map<String, dynamic> json) {
    return Location(
      locationID: json['locationID'],
      locationName: json['locationName'],
      companyName: json['companyName'],
    );

  }

}

此类是具有“结果”(成功/错误)消息的父级 json 响应。它将上面的类实例化为列表来跟踪实际的公司/位置记录

//jsonResponse
class jsonResponse{

  String result;
  String resultMsg;
  List<Location> locations;

  jsonResponse({this.result, this.resultMsg, this.locations});

  factory jsonResponse.fromJson(Map<String, dynamic> parsedJson){

    var list = parsedJson['resultSet'] as List;
    List<Location> locationList = list.map((i) => Location.fromJson(i)).toList();
    return jsonResponse(
        result: parsedJson['result'],
        resultMsg: parsedJson['resultMsg'],
        locations: locationList
    );
  }

} // jsonResponse

这是使用上面的类来解析 API 数据并创建 ListView 的状态和有状态小部件

class locationsApiState extends State<locationsApiWidget> {

  // list to track AJAX results
  Future<List<Location>> _listFuture;

  // init - set initial values
  @override
  void initState() {
    super.initState();
    // initial load
    _listFuture = updateAndGetList();
  }

  Future<List<Location>> updateAndGetList() async {

    var response = await http.get("http://XXX.XXX.XXX.XXX/api/listCompanies.php");
    if (response.statusCode == 200) {
      var r1 = json.decode(response.body);
      jsonResponse r = new jsonResponse.fromJson(r1);
      return r.locations;
    } else {
      throw Exception('Failed to load internet');
    }

  }

  _changeBackground(int index){
    print("in changebackground(): ${index}");       // this works!
    _listFuture[index].backgroundColor = '34bdeb';   // ERROR: The operator '[]' isn't defined for the class 'Future<List<Location>>'.
  }

  // build() method
  @override
  Widget build(BuildContext context) {

    return new FutureBuilder<List<Location>>(
        future: _listFuture,
        builder: (context, snapshot){

          if (snapshot.connectionState == ConnectionState.waiting) {
            return new Center(
              child: new CircularProgressIndicator(),
            );
          } else if (snapshot.hasError) {
            return new Text('Error: ${snapshot.error}');
          } else {
            final items = snapshot.data;
            return new Scrollbar(
              child: new RefreshIndicator(
                  child: ListView.builder(
                    physics: const AlwaysScrollableScrollPhysics(),
                    //Even if zero elements to update scroll
                    itemCount: items.length,
                    itemBuilder: (context, index) {
                      return
                        Container(
                            color: HexColor(items[index].backgroundColor),
                            child:
                            ListTile(
                              title: Text(items[index].companyName),
                              onTap: () {
                                print("Item at $index is ${items[index].companyName}");
                                _changeBackground(index);
                              }  // onTap
                            )
                        );
                    },
                  ),
                  onRefresh: () {
                    // implement later
                    return;
                  } // refreshList,
              ),
            );
          }// else
        } // builder
    ); // FutureBuilder
  } // build
} // locationsApiState class


class locationsApiWidget extends StatefulWidget {
  @override
  locationsApiState createState() => locationsApiState();
}

用于将十六进制转换为整数颜色的辅助类(取自 stackoverflow 上的某处)

class HexColor extends Color {
  static int _getColorFromHex(String hexColor) {
    hexColor = hexColor.toUpperCase().replaceAll("#", "");
    if (hexColor.length == 6) {
      hexColor = "FF" + hexColor;
    }
    return int.parse(hexColor, radix: 16);
  }

  HexColor(final String hexColor) : super(_getColorFromHex(hexColor));
}

Thanks!


我建议从您的位置类别中删除背景颜色,并将选择的位置存储在您的州中。这样,选择项目时您的位置列表就不需要更改。我还将为您的位置项创建一个 StatelessWidget,它将设置背景颜色,具体取决于是否选择它。所以:

// for the LocationItem widget callback
typedef void tapLocation(int index);

class locationsApiState extends State<locationsApiWidget> {

  // list to track AJAX results
  Future<List<Location>> _listFuture;
  final var selectedLocationIndices = Set<int>();

  // init - set initial values
  @override
  void initState() {
    super.initState();
    // initial load
    _listFuture = updateAndGetList();
  }

  Future<List<Location>> updateAndGetList() async {

    var response = await http.get("http://XXX.XXX.XXX.XXX/api/listCompanies.php");
    if (response.statusCode == 200) {
      var r1 = json.decode(response.body);
      jsonResponse r = new jsonResponse.fromJson(r1);
      return r.locations;
    } else {
      throw Exception('Failed to load internet');
    }
  }

  void _toggleLocation(int index) {
    if (selectedLocationIndices.contains(index))
      selectedLocationIndices.remove(index);
    else
      selectedLocationIndices.add(index);
  }

  // build() method
  @override
  Widget build(BuildContext context) {

    return new FutureBuilder<List<Location>>(
        future: _listFuture,
        builder: (context, snapshot){

          if (snapshot.connectionState == ConnectionState.waiting) {
            return new Center(
              child: new CircularProgressIndicator(),
            );
          } else if (snapshot.hasError) {
            return new Text('Error: ${snapshot.error}');
          } else {
            final items = snapshot.data;
            return new Scrollbar(
              child: new RefreshIndicator(
                  child: ListView.builder(
                    physics: const AlwaysScrollableScrollPhysics(),
                    //Even if zero elements to update scroll
                    itemCount: items.length,
                    itemBuilder: (context, index) {
                      return LocationItem(
                        isSelected: selectedLocationIndices.contains(index),
                        onTap: () => setState({
                          _toggleLocation(index);
                        })
                      );
                    },
                  ),
                  onRefresh: () {
                    // implement later
                    return;
                  } // refreshList,
              ),
            );
          }// else
        } // builder
    ); // FutureBuilder
  } // build
} // locationsApiState class


class locationsApiWidget extends StatefulWidget {
  @override
  locationsApiState createState() => locationsApiState();
}

以及项目列表条目:

class LocationItem extends StatelessWidget {

  final bool isSelected;
  final Function tapLocation;

  const LocationItem({@required this.isSelected, @required this.tapLocation, Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      color: isSelected ? HexColor('34bdeb') : HexColor('fc7303'),
      child: ListTile(
        title: Text(items[index].companyName),
        onTap: () => tapLocation() // onTap
      )
    );
  }
}

请原谅,我无法编译它,所以我希望它是正确的。但我认为您明白了:让 Stateful 小部件单独跟踪选定的位置,并让该位置决定在重建时如何呈现自身。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Flutter - 如何更新用于构建 ListView 的 Future/List 的状态(或值?)(通过 FutureBuilder) 的相关文章

随机推荐

  • 在 Angular 中动态设置样式

    我有以下标记 tr style background color none tr 正如它所说 如果activity status字段待定 然后将背景颜色设置为红色 否则设置为绿色 但这不起作用 检查后我发现它呈现如下 tr style ba
  • C# - 编写 COM 服务器 - 映射到方法的属性

    我们正在尝试替换最初为 VB6 应用程序编写的 COM 服务器 我们无法访问源代码 由于某种原因 VB6 应用程序可以调用我们的构造函数 但随后它会得到 系统错误 H80004002 没有这样的 接口支持 我假设当它尝试使用 QueryIn
  • Magento - 致命错误:类名必须是有效的对象或字符串

    我在安装 Magento 时遇到问题 希望有人能帮助我解决 当我访问该网站时 我突然开始收到以下错误消息 Fatal error Class name must be a valid object or a string in app co
  • 科学记数法中的小“e”/Matlab中的Double是什么

    当我计算一个非常小的数字时 matlab给出 1 12345e 15这是什么 我可以将其解释为 1 12345 10 15 或其 1 12345 e 15 我很着急 抱歉问了这个愚蠢的问题 e 代表指数 它的科学计数法 http en wi
  • Crontab 格式化 - 每 15 分钟一次

    我试图让一个简单的 crontab 作业每 15 分钟运行一次 但在决定如何格式化计时时遇到困难 我一直在写的内容如下 15 我很确定这只运行每小时的前 15 分钟 我认为 crontab 允许用户指定确切的运行时间 即 0 15 30 4
  • 从主题读取后立即异步提交消息

    我正在尝试在阅读主题后立即提交一条消息 我已点击此链接 https www confluence io blog apache kafka spring boot application https www confluent io blo
  • 如何在drawString Java中更改字体大小

    如何让字体变大g drawString Hello World 10 10 g setFont new Font TimesRoman Font PLAIN fontSize 其中 fontSize 是一个 int 这绘制字符串的API h
  • 异步运行一些 Python 代码的正确方法是什么?

    我需要从我的普通 Flask 应用程序发送邮件 所以我认为最简单的方法是使用 smtplib 发送邮件 但我必须异步执行 你不能只在请求中插入 3 秒的延迟 对吧 因此 我将电子邮件添加到队列 psql 表 中 并从读取该表并使用 smpt
  • 如何在 R 中创建具有特定 MA(或 q)项的 ARIMA 模型

    我无法确定使用 arima stats 创建 ARMA 模型的特定方法 该模型具有特定的 MA 项 而这些项不仅仅由最大数指定 我的意思是 我需要指定一个 AR 1 MA 1 4 模型 该模型应该产生截距 AR1 项 MA1 项和 MA4
  • 将视频保存到 CameraRoll React-Native

    可以使用cameraRoll在React Native上保存视频 对于使用saveImageWithTag 的图像 这很容易 但我找不到视频文档 将视频保存到相机胶卷非常容易 我使用以下代码行完成了它 saveVideoToCameraRo
  • 对数据框中的列中的数据进行分类

    我的数据框中有一列数字 我想将这些数字分类为例如高 低 排除 我该如何做到这一点 我一无所知 我尝试查看剪切函数和类别数据类型 一个简短的例子pd cut 让我们从一些数据框开始 df pd DataFrame A 0 8 2 5 9 15
  • 点击 wkwebview 链接时不会打开

    我正在开发一个应用程序 其中一些代码继承自其他开发人员 并具有加载 html 文件的 Web 视图 html 文件中包含电话号码和网页链接 如果长按电话号码将会打开 但 html 链接不会打开 我希望它们通过短按打开 但短按什么也没有发生
  • 从其他进程向 WPF 单例应用程序发送数据

    我有一个 WPF 单例应用程序 其中任何时候只有一个实例在运行 如果用户尝试启动另一个实例 我们检查它是否已经在运行 然后我们终止这个新进程并将现有进程置于前面 现在 我们需要从另一个进程 可以是 xls word 或另一个独立应用程序 打
  • 如何直接从protobuf创建GRPC客户端而不将其编译成java代码

    使用 gRPC 时 我们需要通过协议缓冲区编译器 protoc 或使用 Gradle 或 Maven protoc 构建插件从 proto 服务定义生成 gRPC 客户端和服务器接口 Flow now protobuf file gt ja
  • 安装过程中,Vista 上的各种文件类型在哪里

    在 Windows XP 上安装我的软件总是很容易 所有内容都在 c Program Files myprogram 目录中 这包括 可执行程序本身和任何 dll Readme txt 和 License txt 帮助文件 hlp 或 ch
  • Angular 4 中的 ID 路由

    我在 Angular 4 中的路由方面需要帮助 我想像这样显示 URL 本地主机 4200 user 1 如果我单击第一个用户的查看详细信息 我对如何做到这一点有点困惑 我已经在下面的代码中尽力了 但它仍然不起作用 应用程序路由 modul
  • 什么时候使用inout参数?

    将类或基本类型传递给函数时 函数中对参数所做的任何更改都将反映在类外部 这基本上是同一件事inout参数应该做的 inout 参数的良好用例是什么 inout意思是修改局部变量也会修改传入的参数 如果没有它 传入的参数将保持相同的值 使用时
  • 有一个不错的 OpenCL 入门套件吗? [关闭]

    Closed 这个问题是无关 help closed questions 目前不接受答案 我有一些经验OpenGL它是可编程管道 我愿意给OpenCL不过 尝试一下 有人可以提出一个很好的集成套件来使用OpenCL 我只知道石英作曲家看起来
  • Mysql 将几列转换为一行

    我有这样的事情 id month col col2 101 Jan A B 102 feb C A 102 feb D Q 我需要这样的东西 id month col col2 101 Jan A B 102 feb C D A Q 为了获
  • Flutter - 如何更新用于构建 ListView 的 Future/List 的状态(或值?)(通过 FutureBuilder)

    我在下面粘贴了相关代码 但您也许可以根据我的伪解释来回答 我使用 FutureBuilder 来构建列表视图 我首先使用 init 异步 HTTP 调用 API 并将其解析为映射以表示 json 结果的对象列表 位置 然后 该位置列表将返回