1

背景

在有状态小部件的状态对象内,我有以下代码。

class _PendingJobsState extends State<PendingJobs> {
  List<String> pendingJobs = [];      <------------------- I am trying to change the state of this variable.

  void updateListWithResponseData(List jobs) {
    BackendApi.call(
        endpoint: APIEndPoints.GET_PENDING_JOBS,
        data: {"email": GlobalData.userEmail},
        onSuccess: (data) {
          setState(() {           <---------------- I call set State here
            jobs = data['job_list'];
            print(pendingJobs);        <------------ State is not changed
            print(jobs);
          });
        },
        onFailed: (error) {
          print('Failed to fetch data!');
        });
  }

  @override
  void initState() {
    updateListWithResponseData(pendingJobs);    <------- This is where the function in which I call the setState is executed.
    super.initState();
  }

关于上述代码的详细信息

List<String> pendingJobs = [];是我期望完成状态更改的变量。

在上面调用的变量正下方定义的函数updateListWithResponseData采用 type 的参数List。它还负责调用另一个名为BackendApi.call().

我正在调用udateListWithResponseData()内部initState,对于它所采用的类型参数List,我给出了pendingJobs我定义的变量。(因为我是setStateupdateListWithResponseData()函数内部调用的,所以我期望调用时的状态pendingJobs会发生变化updateListWithResponseData。)

但是,我在上述观点中所期望的状态变化并没有发生。

BackendApi.call负责从给定的 url 获取数据,它需要两个回调函数,onSuccess它们onFailure负责根据数据获取是否成功执行必要的操作。

重要说明

从 中删除List jobs参数updateListWithResponseData并直接引用pendingJobs变量对我来说不是解决方案,因为我希望将调用的函数提取updateListWithResponseData到单独的 dart 文件并从不同的小部件中调用它。所以我必须List jobs在函数中有那个参数。


我尝试调试此问题一段时间,但找不到解决方案。如果有人能指出为什么状态pendingJobs没有改变以及如何真正改变它,那将非常有帮助。(谢谢。)


**编辑**

由于下面的很多评论似乎都围绕着该BackendApi.call()函数,并且由于我没有在我的原始帖子中包含该函数的代码,因此我编辑了这篇文章以包含该函数的代码。

import 'dart:convert';
import 'package:field_app/globalData/global_data.dart';
import 'package:http/http.dart' as http;
import 'api_endpoints.dart';

typedef ValueChanged<T> = void Function(T value);

class BackendApi {
  static void call(
      {String endpoint,
      Map<String, dynamic> data,
      ValueChanged<Map<String, dynamic>> onSuccess,
      ValueChanged<String> onFailed}) async {
    try {
      var response = await http.post(APIEndPoints.API_ROOT + endpoint,
          headers: {
            "Content-Type": "application/json",
            "Authorization": 'Bearer ' + GlobalData.authToken,
          },
          body: jsonEncode(data));

      Map<String, dynamic> apiResponse = jsonDecode(response.body);
      if (apiResponse != null) {
        if (response.statusCode == 200) {
          if (onFailed != null) {
            onSuccess(apiResponse);
          }
        } else {
          print(apiResponse['message']);
          print('code: ' + response.statusCode.toString());
          if (onFailed != null) {
            onFailed(apiResponse['message']);
          }
        }
      } else {
        print('Invalid API response format');
        print('code: ' + response.statusCode.toString());
        return null;
      }
    } catch (e) {
      print("Failed to connect with backend API");
      print(e.toString());
      return null;
    }
  }
}
4

3 回答 3

1

这种行为背后的原因是 dart 参数是按值传递的。(即变量的副本与变量数据一起传递)

所以在这里你传递了一个值的副本,pendingJobs其中恰好是对列表的引用。

@override
  void initState() {
    updateListWithResponseData(pendingJobs);    <------- This is where the function in which I call the setState is executed.
    super.initState();
  }

现在updateListWithResponseData有自己的变量来保存引用jobs的副本pendingJobs

Future<void> updateListWithResponseData(List jobs) async{
   await BackendApi.call(
        endpoint: APIEndPoints.GET_PENDING_JOBS,
        data: {"email": GlobalData.userEmail},
        onSuccess: (data) {
          setState(() {           <---------------- I call set State here
            pendingJobs = data['job_list'];
            print(pendingJobs);        <------------ State is not changed
            print(jobs);
          });
        },
        onFailed: (error) {
          print('Failed to fetch data!');
        });
  }

所以这jobs = data['job_list'];所做的是分配局部变量(到updateListWithResponseDatajobs值,这个变化不会反映在pendingJobs你只更新里面的副本updateListWithResponseData

要解决此问题,您删除分配并用这种方式jobs = data['job_list'];替换它,值也会得到更新。 jobs.addAll(data['job_list']);pendingJobs

于 2021-09-23T19:38:41.447 回答
0

第一件事:如果你想做一个在第一次构建时会改变状态的异步任务,总是在 WidgetsBinding.instance.addPostFrameCallback 中做。第二件事:您不必使用状态变量作为状态方法的参数。

尝试这个:

class _PendingJobsState extends State<PendingJobs> {
  List<String> pendingJobs = [];      <------------------- I am trying to change the state of this variable.

  void updateListWithResponseData() {
    BackendApi.call(
        endpoint: APIEndPoints.GET_PENDING_JOBS,
        data: {"email": GlobalData.userEmail},
        onSuccess: (data) {
          setState(() {           <---------------- I call set State here
            pendingJobs = data['job_list'];
            print(pendingJobs);
          });
        },
        onFailed: (error) {
          print('Failed to fetch data!');
        });
  }

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) => updateListWithResponseData()) <------- This is where the function in which I call the setState is executed.
  }
于 2021-09-23T19:02:16.320 回答
0

尝试:

Future<void> updateListWithResponseData(List jobs) async{
   await BackendApi.call(
        endpoint: APIEndPoints.GET_PENDING_JOBS,
        data: {"email": GlobalData.userEmail},
        onSuccess: (data) {
          setState(() {           <---------------- I call set State here
            pendingJobs = data['job_list'];
            print(pendingJobs);        <------------ State is not changed
            print(jobs);
          });
        },
        onFailed: (error) {
          print('Failed to fetch data!');
        });
  }

并且在initState

updateListWithResponseData(pendingJobs); 

这是该工作流程的演示小部件


class ApplicantsX extends StatefulWidget {
  const ApplicantsX({Key? key}) : super(key: key);

  @override
  State<ApplicantsX> createState() => _ApplicantsXState();
}

class _ApplicantsXState extends State<ApplicantsX> {
  int a = 0;
  Future<void> up(int v) async {
    await Future.delayed(Duration(seconds: 2), () {
      setState(() {
        a = v;
      });
    });
  }

  @override
  void initState() {
    super.initState();
    up(234);
  }

  @override
  Widget build(BuildContext context) {
    return Center(child: Text("$a"));
  }
}

于 2021-09-23T18:21:50.077 回答