我需要让消费者小部件根据布尔值侦听多个变量。
这是模型类
class Lawyer{
Data? data;
double? distance = 0;
Lawyer({this.data, this.distance});
factory Lawyer.fromJson(Map<String, dynamic> json) =>
Lawyer(data: Data.fromJson(json['listing_data']));
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////
class Data{
String? title;
String? email;
String? phone;
Location? location;
List<String>? logo;
List<String>? cover;
Data({this.title, this.email, this.phone, this.logo, this.cover, this.location});
factory Data.fromJson(Map<String, dynamic> json) {
var logo = json['_job_logo'];
var cover = json['_job_cover'];
var long = json['geolocation_long'];
var lat = json['geolocation_lat'];
return Data(title: json['_job_tagline'], email: json['_job_email'],
location: Location(latitude: json['geolocation_lat'], longitude: json['geolocation_long']),
phone: json['_job_phone'], logo: List<String>.from(logo),
cover: List<String>.from(cover)
);
}
}
这是视图模型通知器
class LawyerAPIServices extends ChangeNotifier{
final url = "https://dalilvision.com/wp-json/wp/v2/job_listing";
List<Lawyer> lawyersList = [];
List<Lawyer> staticLawyersList = [];
Future<List<Lawyer>> fetchLawyers() async{
final response = await get(Uri.parse(url.toString()));
if(response.statusCode == 200){
var dynamicLawyersList = jsonDecode(response.body);
print('$dynamicLawyersList');
lawyersList = List<Lawyer>.from(dynamicLawyersList.map((x) => Lawyer.fromJson(x)));
staticLawyersList = lawyersList;
lawyersList.forEach((element) {print('all lawyers: ${element.data!.location}');});
notifyListeners();
return lawyersList;
}
else{
notifyListeners();
throw Exception(response.statusCode);
}
}
Future<List<Lawyer>> getFullListOfLawyers() async {
notifyListeners();
print('fulll list: ${staticLawyersList.length}');
return staticLawyersList;
}
}
最后是消费者小部件
Consumer<LawyerAPIServices>(
builder: (context, value, child) => FutureBuilder(
future: _list,
builder: (BuildContext context, AsyncSnapshot<List<Lawyer>> snapshot) {
if (snapshot.hasData){
return ListView.separated(
physics: const NeverScrollableScrollPhysics(),
scrollDirection: Axis.vertical,
shrinkWrap: true,
separatorBuilder: (context, index) => const Divider(color: Colors.transparent),
itemCount: value.lawyersList.length,
itemBuilder: (context, index) {
return InkWell(
child: LawyerWidget(
title: snapshot.data![index].data!.title!,
email: snapshot.data![index].data!.email!,
phone: snapshot.data![index].data!.phone!,
logo: snapshot.data![index].data!.logo![0],
cover: snapshot.data![index].data!.cover![0]
),
);
}
}
);
}
else if(snapshot.hasError){
return Center(
child: Text(snapshot.error.toString())
);
}
else {
return const CircularProgressIndicator(
strokeWidth: 2,
);
}
},
),
)
在通知器class
中有两个列表,staticLawyerList
仅在从网络调用获取列表时初始化一次,然后用作备份列表,lawyersList
是将被操纵的列表。
到目前为止,我所做的是通过网络调用获得lawyersList
的初始值,然后不知何故,staticLawyersList
的值总是等于lawyersList
,即使我做了任何更改或操纵lawyersList
,这些更改也会自动反映在staticLawyersList
上,这真的很奇怪。
现在我想要实现的是应用一个条件,根据这个条件用适当的列表更新UI。
if(setByPosition == false){
//update UI with `staticLawyersList`
}
else {
//update UI with `lawyersList`
}
更新! !
下面是我如何更新我的consumer
CheckboxListTile(
activeColor: Colors.black,
value: isChecked,
onChanged: (value) async {
saveSharedPreferences(value: value!);
if(value == true) {
Provider.of<LawyerAPIServices>(context, listen: false).sortLawyersList(
devicePosition: widget.position, lawyersList: widget.list);
}
else{
Provider.of<LawyerAPIServices>(context, listen: false).getFullListOfLawyers();// the list returned by this function don't applied to the consumer
}
setState(() {
isChecked = value;
Navigator.pop(context);
});
},
title: const Text('Filter by distance'),
),
需要考虑的几点:
-
当你这样做时&;staticLawyersList = lawyerslist &;实际上你有两个指针到同一个列表。它对列表、集合、类等都是这样工作的。只有基本类型如int, double, string被真正复制。你可以用这个代替:"staticLawyersList = List.from(lawyersList);">
-
看起来你不需要在lawyerapisservices中使用ChangeNotifier。您可以在需要的小部件中创建lawyerapisservices的实例,并调用fetchLawyers。如果不想多次重建列表,请在StatefullWidget的initState中执行此操作。在你的构建方法中,使用FutureBuilder来读取Future并决定在UI中显示什么。
class _MyWidget extends State<MyWidget> {
late final LawyerAPIServices lawyerApi;
// Create this variable to avoid calling fetchLawers many times
late final Future<List<Lawyer>> lawyersList;
@override
void initState() {
super.initState();
// Instantiate your API
lawyerApi = LawyerAPIServices();
// This will be called only once, when this Widget is created
lawyersList = lawyerApi.fetchLawyers();
}
@override
Widget build(BuildContext context) {
return FutureBuilder<List<Lawyer>>(
future: lawyersList,
builder: ((context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.done:
if (setByPosition) {
//update UI with `lawyersList`
return _listView(snapshot.data!);
} else {
//update UI with `staticLawyersList`
// Since the Future state is Complete you can be sure that
// the staticLawyersList variable in your API was already set
return _listView(lawyerApi.staticLawyersList);
}
case ConnectionState.none:
return const Text('Error');
default:
return const CircularProgressIndicator.adaptive();
}
}),
);
}
Widget _listView(List<Lawyer> lawyersList) {
return ListView.separated(
physics: const NeverScrollableScrollPhysics(),
scrollDirection: Axis.vertical,
shrinkWrap: true,
separatorBuilder: (context, index) =>
const Divider(color: Colors.transparent),
itemCount: lawyersList.length,
itemBuilder: (context, index) {
return InkWell(
child: LawyerWidget(
title: lawyersList[index].data!.title!,
email: lawyersList[index].data!.email!,
phone: lawyersList[index].data!.phone!,
logo: lawyersList[index].data!.logo![0],
cover: lawyersList[index].data!.cover![0]),
);
});
}
}
如果出于任何原因你需要在多个小部件之间共享相同的LawyerAPIServices,你可以在树的顶部实例化它,并使用Provider或作为参数发送它。
方法getfulllistayyers不需要返回一个Future,因为staticLawyersList是一个List(不是Future)。你可以使用" lawyerapisservices . staticlawyerslist "或者像这样的东西可能有意义:
Future
- getfulllistayers () async {如果(staticLawyersList.isEmpty) {等待fetchLawyers ();}print('完整列表:${staticLawyersList.length}');返回Future.value (staticLawyersList);}
正如@ saicchi - okuma所说,要复制列表的内容,您应该使用staticLawyersList = List.from(lawyersList)
,因为在dart和大多数java编译器编程语言中,当您使用staticLawyersList = lawyersList
时,这意味着您通过staticLawyersList
引用lawyersList
。然后我在staticLawyersList
lawyersList
lawyersList.clear();
lawyersList.addAll(staticLawyersList);
但是当我这样做时,消费者没有应用基于staticLawyersList
的更改,尽管logcat显示staticLawyersList
长度为10,这是我想要的(完整列表,不过滤)。
我的问题可以归纳为两点:
1-消费者只听一个列表lawyersList
,我认为它仍然存在。2- @Saichi-Okuma提到的指针问题。
这里是完整的代码更改
void getFullListOfLawyers() {
lawyersList.clear(); // to make sure that the list is clean from older operations
lawyersList.addAll(staticLawyersList);// the trick
notifyListeners();
}
Future<List<Lawyer>> fetchLawyers() async{
final response = await get(Uri.parse(url.toString()));
if(response.statusCode == 200){
var dynamicLawyersList = jsonDecode(response.body);
print('$dynamicLawyersList');
lawyersList = List<Lawyer>.from(dynamicLawyersList.map((x) => Lawyer.fromJson(x)));
staticLawyersList = List.from(lawyersList);// use this statment instead of staticLawyersList = lawyersList
lawyersList.forEach((element) {print('all lawyers: ${element.data!.location}');});
notifyListeners();
return lawyersList;
}
else{
notifyListeners();
throw Exception(response.statusCode);
}
}
无论列表的状态如何,每次调用notifyListeners时,都会重新构建Consumer Widget。
可能您没有访问正在使用的API的实例。请确保您正在使用Consumer构建器的第二个参数。
Consumer<LawyerAPIServices>(builder: (context, lawyerAPI, child) =>
FutureBuilder(
future: lawyerAPI.fetchLawyers(),
builder: ((context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.done:
if (setByPosition) {
//update UI with `lawyersList`
return _listView(snapshot.data!);
} else {
//update UI with `staticLawyersList`
// Since the Future state is Complete you can be sure that
// the staticLawyersList variable in your API was already set
return _listView(lawyerAPI.staticLawyersList);
}
case ConnectionState.none:
return const Text('Error');
default:
return const CircularProgressIndicator.adaptive();
}
}),
我认为你不需要下面的代码来满足这个特殊的需求。它会覆盖你的lawyersList并通知所有的听众,即使实际上什么都没有改变。只要直接访问你的staticLawyersList,因为它是在你调用fetchLawyers时填充的。
void getFullListOfLawyers() {
lawyersList.clear(); // to make sure that the list is clean from older operations
lawyersList.addAll(staticLawyersList);// the trick
notifyListeners();
}