开关/复选框未在 setState() 上更新

Posted

技术标签:

【中文标题】开关/复选框未在 setState() 上更新【英文标题】:Switch/Checkbox not updating on setState() 【发布时间】:2021-10-31 07:40:51 【问题描述】:

我已经这样做了 4 天,阅读了一半的 Google 内容并观看了 3/4 的 Youtube。我有一个对象列表,我正在从 API 获取数据。当这些对象生成卡片时,Checkbox 的 Switch 不起作用,setState 根本不会影响它们。这是原始代码,在我开始尝试并弄乱代码之前(感谢 Git)。

有人有什么想法吗?

import 'dart:convert';
import 'dart:developer';

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:mojmegatel/API/get_settings_api.dart';
import 'package:mojmegatel/api/set_settings_api.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';

import '../constants.dart';
import 'dart:io';

class SettingsArray extends StatefulWidget 
  final ValueChanged onChanged;
  final bool isSwitched;

  const SettingsArray(
    required this.onChanged,
    required this.isSwitched,
    Key? key,
  ) : super(key: key);

  @override
  _SwitchWidget createState() => _SwitchWidget();


class _SwitchWidget extends State<SettingsArray> 
  Future? dataFuture;
  bool isSwitched = false;

  @override
  void initState() 
    isSwitched = false;
    dataFuture = _getData();

    super.initState();
  

  _getData() async 
    await GetSettings();

    SharedPreferences sharedPrefs = await SharedPreferences.getInstance();

    String sharedPrefsSettings = sharedPrefs.getString('settings').toString();
    String jsonSettings = await DefaultAssetBundle.of(context).loadString("assets/json/settings_data.json");

    String mergedData = await MergeData(jsonSettings, sharedPrefsSettings);
    return mergedData;
  

  Future<String> MergeData(String jsonSettings, String sharedPrefsSettings) async 
    var decodedJsonSettings = json.decode(jsonSettings);
    var decodedSharedPrefsSettings = json.decode(sharedPrefsSettings);

    String mergedSettings = '[';

    bool found_id = false;
    bool is_last = false;

    String service_id;
    String name;
    String description;
    String servicerequestvalue;

    for (int i = 0; i < decodedJsonSettings.length; i++) 
      for (int j = 0; j < decodedSharedPrefsSettings.length; j++) 
        if (decodedJsonSettings[i]['service_id'] == decodedSharedPrefsSettings[j]['service_id']) 
          found_id = true;

          service_id = decodedJsonSettings[i]['service_id'];
          name = decodedJsonSettings[i]['title'];
          description = decodedJsonSettings[i]['description'];
          servicerequestvalue = decodedSharedPrefsSettings[j]['servicerequestvalue'];

          mergedSettings += '"service_id": "$service_id",';
          mergedSettings += '"name": "$name",';
          mergedSettings += '"description": "$description",';
          mergedSettings += '"servicerequestvalue": "$servicerequestvalue"';

          if (i == decodedJsonSettings.length - 1 && j == decodedSharedPrefsSettings.length - 1) 
            // Nothing, don't complicate
           else 
            mergedSettings += ',';
          
        
      

      if (found_id == false) 
        service_id = decodedJsonSettings[i]['service_id'];
        name = decodedJsonSettings[i]['title'];
        description = decodedJsonSettings[i]['description'];

        mergedSettings += '"service_id": "$service_id",';
        mergedSettings += '"name": "$name",';
        mergedSettings += '"description": "$description",';
        mergedSettings += '"servicerequestvalue": "0"';

        if (i == decodedJsonSettings.length - 1) 
          // Nothing, don't complicate
         else 
          mergedSettings += ',';
        
       else 
        found_id = false;
      
    

    mergedSettings += ']';

    return mergedSettings;
  

  Widget build(BuildContext context) 
    List<Widget> widgetList = <Widget>[];

    return ListView.builder(
      shrinkWrap: true,
      physics: ScrollPhysics(),
      itemCount: 1,
      itemBuilder: (context, index) 
        return FutureBuilder(
          future: dataFuture,
          builder: (context, snapshot) 
            if (snapshot.data != null) 
              String service_id;
              String name;
              String description;

              var settingsData = json.decode(snapshot.data.toString());

              List<dynamic> list = settingsData;
              widgetList = <Widget>[];

              for (int i = 0; i < list.length; i++) 
                String serviceRequestValue = settingsData[i]['servicerequestvalue'].toString();
                bool serviceRequestValueBool = true;

                service_id = settingsData[i]['service_id'].toString();
                name = settingsData[i]['name'].toString();
                description = settingsData[i]['description'].toString();

                log(service_id);

                if (serviceRequestValue == "1" || serviceRequestValue == "1,1" || serviceRequestValue == "14") 
                  serviceRequestValueBool = true;
                 else 
                  serviceRequestValueBool = false;
                

                widgetList.add(settingsContainer(service_id, service_id, name, description, serviceRequestValueBool));
              

              return Column(children: widgetList.toList());
            

            return Column(
              children: [
                Container(
                  width: 60.0,
                  height: 80.0,
                  padding: EdgeInsets.fromLTRB(0.0, 20.0, 0.0, 0.0),
                  child: SpinKitFoldingCube(color: MojMegaTelTheme.megaTelBlue), // CircularProgressIndicator(),
                ),
              ],
            );
          ,
        );
      ,
    );
  

  Widget settingsContainer(String key, String serviceId, String title, String description, bool isSwitched) 
    return Padding(
      key: ValueKey(key),
      padding: const EdgeInsets.only(left: 24, right: 24, top: 16, bottom: 18),
      child: Container(
        decoration: BoxDecoration(
          color: MojMegaTelTheme.white,
          borderRadius: BorderRadius.only(topLeft: Radius.circular(8.0), bottomLeft: Radius.circular(8.0), bottomRight: Radius.circular(8.0), topRight: Radius.circular(8.0)),
          boxShadow: <BoxShadow>[
            BoxShadow(color: MojMegaTelTheme.grey.withOpacity(0.2), offset: Offset(1.1, 1.1), blurRadius: 10.0),
          ],
        ),
        child: Column(
          children: <Widget>[
            Padding(
              padding: const EdgeInsets.only(top: 16, left: 16, right: 24),
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                crossAxisAlignment: CrossAxisAlignment.start,
                children: <Widget>[
                  Row(
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    crossAxisAlignment: CrossAxisAlignment.center,
                    children: <Widget>[
                      Row(
                        mainAxisAlignment: MainAxisAlignment.spaceBetween,
                        children: <Widget>[
                          Padding(
                            padding: const EdgeInsets.only(left: 4, bottom: 3),
                            child: Row(
                              children: [
                                Text(
                                  title,
                                  overflow: TextOverflow.fade,
                                  maxLines: 1,
                                  softWrap: false,
                                  textAlign: TextAlign.center,
                                  style: TextStyle(
                                    fontFamily: MojMegaTelTheme.fontName,
                                    fontWeight: FontWeight.w600,
                                    fontSize: 14,
                                    color: MojMegaTelTheme.darkerText,
                                  ),
                                ),
                              ],
                            ),
                          ),
                          Padding(
                            padding: const EdgeInsets.only(left: 8),
                            child: Row(
                              children: [
                                Checkbox(
                                  value: isSwitched,
                                  onChanged: (value) 
                                    setState(
                                      () 
                                        isSwitched = value!;
                                        log("Swich: " + isSwitched.toString());
                                      ,
                                    );
                                  ,
                                ),
                              ],
                            ),
                          ),
                        ],
                      ),
                    ],
                  )
                ],
              ),
            ),
            Padding(
              padding: const EdgeInsets.only(left: 24, right: 24, top: 8, bottom: 8),
              child: Container(
                height: 2,
                decoration: BoxDecoration(
                  color: MojMegaTelTheme.background,
                  borderRadius: BorderRadius.all(Radius.circular(4.0)),
                ),
              ),
            ),
            Padding(
              padding: const EdgeInsets.only(left: 24, right: 24, top: 8, bottom: 16),
              child: Row(
                children: <Widget>[
                  Expanded(
                    child: Column(
                      mainAxisAlignment: MainAxisAlignment.center,
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: <Widget>[
                        Padding(
                          padding: const EdgeInsets.only(top: 6),
                          child: Text(
                            description,
                            textAlign: TextAlign.left,
                            style: TextStyle(
                              fontFamily: MojMegaTelTheme.fontName,
                              fontWeight: FontWeight.w600,
                              fontSize: 12,
                              color: MojMegaTelTheme.grey.withOpacity(0.5),
                            ),
                          ),
                        ),
                      ],
                    ),
                  ),
                ],
              ),
            )
          ],
        ),
      ),
    );
  

【问题讨论】:

我认为问题出在 isSwitched 上。为什么你在两个班级都设置它?似乎每次重建此小部件时,它都会将 isSwitched 重置为 false ,因此什么也没有发生。删除 isSwitched = false;从初始化状态看看会发生什么 【参考方案1】:

问题出在这里:

if (serviceRequestValue == "1" || serviceRequestValue == "1,1" || serviceRequestValue == "14") 
  serviceRequestValueBool = true;
 else 
  serviceRequestValueBool = false;

您的复选框状态已经改变,但它会重建您的整个有状态小部件,因此上述代码会将其恢复到最后一个状态。您可以为 Switch 创建一个单独的有状态小部件,并让它自行重建以解决此问题。

【讨论】:

【参考方案2】:

你应该用 StatefulBuilder 小部件包装你的小部件。

StatefulBuilder(
    builder: (context, _setState) => // Your Widget );

【讨论】:

以上是关于开关/复选框未在 setState() 上更新的主要内容,如果未能解决你的问题,请参考以下文章

选中的复选框未在 UI 中更新

复选框值在 setState 中反转

反应复选框不更新

新特性速递新增复选框开关样式

React:受控表单上的复选框和文件

KnockoutJS Observable 未在模板中更新