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

Flutter中ListView单索引按钮状态独立控制问题求助

Flutter ListView.builder 按钮状态同步变化问题解决

问题描述

使用ListView.builder渲染列表时,点击单个「发送请求」按钮,所有按钮的状态都会同步变为「Pending...」,无法单独控制每个列表项的按钮状态。

问题根源

你当前使用了**单个布尔变量pending**控制所有按钮的状态,所有列表项共享这个全局变量。当任意按钮点击触发setState时,所有按钮都会根据这个变量统一更新状态,导致同步变化。

解决方案

改用**Map<String, bool>类型变量**存储每个列表项的请求状态,以Firestore文档的唯一ID作为Key,实现单个列表项的状态独立控制。

修改后的核心代码

class YourPageState extends State<YourPage> {
  // 替换单个pending变量,用文档ID作为key存储每个项的请求状态
  Map<String, bool> _requestStatus = {};
  String? userId;
  final String key = "你的FCM密钥";

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: mainColor,
        title: const Text("Choose Your Partner"),
      ),
      body: SafeArea(
        child: SingleChildScrollView(
          child: Column(
            children: [
              Padding(
                padding: const EdgeInsets.all(8.0),
                child: StreamBuilder(
                  stream: FirebaseFirestore.instance.collection('recentPublished').snapshots(),
                  builder: (context, snapshot) {
                    userId = FirebaseAuth.instance.currentUser?.uid;
                    if (!snapshot.hasData) {
                      return const Center(child: CupertinoActivityIndicator());
                    }

                    final docs = snapshot.data!.docs;
                    return ListView.builder(
                      shrinkWrap: true,
                      primary: false,
                      itemCount: docs.length,
                      itemBuilder: (context, index) {
                        final doc = docs[index];
                        final docId = doc.id;
                        // 获取当前项的请求状态,默认未请求(false)
                        final isPending = _requestStatus[docId] ?? false;

                        return Column(
                          children: [
                            _vehicleCard(
                              userId!,
                              doc["userId"].toString(),
                              doc["riderProfileUrl"].toString(),
                              userName,
                              doc["rideOfferBy"].toString(),
                              doc["travellingDate"].toString(),
                              doc["pickUpPlace"].toString(),
                              doc["destination"].toString(),
                              doc["vehicleNumber"].toString(),
                              doc["vehicleName&Models"].toString(),
                              doc["vehicleColor"].toString(),
                              doc["seat"].toString(),
                              doc["fairPrice"].toString(),
                              doc["fcmToken"].toString(),
                            ),
                            isPending
                                ? const TextButton(
                                    onPressed: null,
                                    child: Text("Pending..."),
                                  )
                                : ElevatedButton(
                                    onPressed: () async {
                                      // 先标记当前项为Pending
                                      setState(() {
                                        _requestStatus[docId] = true;
                                      });

                                      try {
                                        final body = {
                                          "to": doc["fcmToken"].toString(),
                                          "notification": {
                                            "title": doc["rideOfferBy"].toString(),
                                            "body": "Are you ready for this ride?",
                                            "android_channel_id": "Vision DG Tech"
                                          },
                                          'data': {
                                            'type': 'notify',
                                            'status': 'pending'
                                          }
                                        };
                                        var res = await post(
                                          Uri.parse('https://fcm.googleapis.com/fcm/send'),
                                          body: jsonEncode(body),
                                          headers: {
                                            HttpHeaders.contentTypeHeader: 'application/json; charset=UTF-8',
                                            HttpHeaders.authorizationHeader: "key=$key"
                                          },
                                        );
                                        print('Res -------- ${res.body}');
                                      } catch (_) {
                                        // 请求失败时恢复当前项状态
                                        setState(() {
                                          _requestStatus[docId] = false;
                                        });
                                      }
                                    },
                                    child: const Text("Send Request"),
                                    style: ButtonStyle(
                                      backgroundColor: MaterialStatePropertyAll(pSecondaryColor),
                                    ),
                                  )
                          ],
                        );
                      },
                    );
                  },
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  // 你的_vehicleCard方法保持原有实现即可
  Widget _vehicleCard(...) {
    // ...原有代码
  }
}

逻辑说明

  1. 状态存储:_requestStatus作为状态容器,每个Firestore文档ID对应一个布尔值,记录该列表项的请求状态。
  2. 状态判断:每个列表项根据自身文档ID,从Map中获取对应状态,默认未请求(false)。
  3. 状态更新:点击按钮时仅更新当前文档ID对应的Map值,触发setState后只有当前项的按钮状态会变化。
  4. 异常处理:请求失败时恢复当前项状态,避免一直显示Pending。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 16:15:55