当调用 setState 时,我的有状态小部件不会更新它的状态
My stateful widget does not update it's state when setState is called
背景
在我的有状态小部件的状态对象中,我有以下代码。
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
的变量正下方定义的函数采用 List
类型的参数。它还负责调用另一个名为 BackendApi.call()
.
的实用函数
我在 initState
中调用 udateListWithResponseData()
并且对于它需要的 List
类型的参数,我给出了我定义的 pendingJobs
变量。 (因为我是从 updateListWithResponseData()
函数中调用 setState
,所以我希望 pendingJobs
的状态在调用 updateListWithResponseData
时发生变化。)
然而,我在上面所期待的状态变化并没有发生。
BackendApi.call
负责从给定的 url 中获取数据,它需要两个回调函数 onSuccess
和 onFailure
负责执行必要的操作,具体取决于取数据成功与否
重要提示
从updateListWithResponseData
中去掉List jobs
参数直接引用pendingJobs
变量是不是 对我来说是一个解决方案,因为我希望将名为 updateListWithResponseData
的函数提取到一个单独的 dart 文件中,并从不同的小部件调用它。 所以我必须在函数中包含 List jobs
参数。
我尝试调试这个问题有一段时间了,但找不到解决方案。如果有人能指出为什么 pendingJobs
的状态没有改变以及如何真正改变它,那将非常有帮助。 (谢谢。)
**编辑**
由于下面的很多评论似乎都围绕 BackendApi.call()
函数,并且由于我没有在我的原始 post 中包含该函数的代码,所以我编辑了这个 post包括代码。
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;
}
}
}
尝试:
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"));
}
}
第一件事:如果你想做一个在第一次构建时恢复时会改变状态的异步任务,总是在 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.
}
此行为背后的原因是 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'];
所做的是分配局部变量(给 updateListWithResponseData
) jobs
值,这个变化不会反映在 pendingJobs
上,因为你只是在 updateListWithResponseData
.
内更新副本
要解决此问题,请删除分配 jobs = data['job_list'];
并将其替换为 jobs.addAll(data['job_list']);
这样 pendingJobs
值也会更新。
背景
在我的有状态小部件的状态对象中,我有以下代码。
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
的变量正下方定义的函数采用 List
类型的参数。它还负责调用另一个名为 BackendApi.call()
.
我在 initState
中调用 udateListWithResponseData()
并且对于它需要的 List
类型的参数,我给出了我定义的 pendingJobs
变量。 (因为我是从 updateListWithResponseData()
函数中调用 setState
,所以我希望 pendingJobs
的状态在调用 updateListWithResponseData
时发生变化。)
然而,我在上面所期待的状态变化并没有发生。
BackendApi.call
负责从给定的 url 中获取数据,它需要两个回调函数 onSuccess
和 onFailure
负责执行必要的操作,具体取决于取数据成功与否
重要提示
从updateListWithResponseData
中去掉List jobs
参数直接引用pendingJobs
变量是不是 对我来说是一个解决方案,因为我希望将名为 updateListWithResponseData
的函数提取到一个单独的 dart 文件中,并从不同的小部件调用它。 所以我必须在函数中包含 List jobs
参数。
我尝试调试这个问题有一段时间了,但找不到解决方案。如果有人能指出为什么 pendingJobs
的状态没有改变以及如何真正改变它,那将非常有帮助。 (谢谢。)
**编辑**
由于下面的很多评论似乎都围绕 BackendApi.call()
函数,并且由于我没有在我的原始 post 中包含该函数的代码,所以我编辑了这个 post包括代码。
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;
}
}
}
尝试:
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"));
}
}
第一件事:如果你想做一个在第一次构建时恢复时会改变状态的异步任务,总是在 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.
}
此行为背后的原因是 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'];
所做的是分配局部变量(给 updateListWithResponseData
) jobs
值,这个变化不会反映在 pendingJobs
上,因为你只是在 updateListWithResponseData
.
要解决此问题,请删除分配 jobs = data['job_list'];
并将其替换为 jobs.addAll(data['job_list']);
这样 pendingJobs
值也会更新。