如何让消费者收听颤动中的多个参数?

发布于 2025-02-10 01:50:16 字数 6088 浏览 3 评论 0原文

我需要让消费者小部件根据布尔值聆听多个变量。

this is the model class

    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,
                              );
                            }
                          },
                        ),
                      )

Notifier class中的消费者小部件,有两个列表,staticlawyerlist从网络调用获得列表然后用作备份列表时仅初始化一次,而LawyersList是将要操纵的列表。

到目前为止,我所做的是通过网络调用获得LawyerSlist的初始值,然后以某种方式staticlawyerslist值始终等于lawyerslist ,即使我进行了任何更改或操纵LawyerSlist这些更改也会自动反映staticlawyerslist,这确实很奇怪。

现在,我要确切地实现的目的是应用条件,根据此条件,使用适当的列表更新UI。

if(setByPosition == false){
//update UI with `staticLawyersList`
}
else {
//update UI with `lawyersList` 
}

更新!!!!!!!!

这是我更新消费者的方式

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'),
            ),

I need to let the consumer widget listen to multiple variables depending on a boolean value.

this is the model class

    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)
    );
  }
}

and this is the view model notifier

    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;
      }
    }

and finally this is the consumer widget

    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,
                              );
                            }
                          },
                        ),
                      )

In the notifier class there are two lists, the staticLawyerList is initialized only once when getting the list from a network call and then used as a backup list, and the lawyersList is the one that will be manipulated.

what I have done until now is to get the initial value of lawyersList by a network call, then somehow the staticLawyersList values are always equal to lawyersList, even if I made any change or manipulate the lawyersList these changes will automatically reflect on the staticLawyersList which is really weird.

now what I want to achieve exactly is to apply a condition to update the UI with the appropriate list depending on this condition.

if(setByPosition == false){
//update UI with `staticLawyersList`
}
else {
//update UI with `lawyersList` 
}

update!!!!!!!!

here's how I update my 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'),
            ),

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

挥剑断情 2025-02-17 01:50:16

需要考虑的几件事:

  1. 当您执行此“ staticlawyerslist = LawyerSlist”时,您实际上有两个“指针”到同一列表中。它适用于列表,集合,类等的方式。只有基本类型为int,double,字符串才真正复制。
    您可以使用此方法:“ staticlawyerslist = list.from(LawyerSlist);”

  2. 似乎您不需要律师服务中的changeNotifier。您可以在需要它的小部件中创建一个律师事务所的实例,并致电Fetchlawyers。如果您不希望多次重建列表,请在状态Fullwidget的初始化中进行。在您的构建方法中,使用FutureBuilder阅读未来并决定在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]),
              );
            });
      }
    }
  1. 如果出于任何原因,您需要在多个小部件上共享相同的Lawyerapiservices,则可以将其实例化在树的顶部,并使用提供商或作为参数将其发送给。

  2. getfulllistoflawyers的方法不需要返回未来,因为staticlawyerslist是列表(不是未来)。您可以使用“ Lawyerapiservices.staticlawyerslist”直接获得此列表,或者可能是这样有意义的:

    未来&lt; list&gt; getfulllistoflawyers()async {
    如果(staticlawyerslist.isempty){
    等待fetchlawyers();
    }
    print('Fulll列表:$ {staticlawyerslist.length}');
    返回Future.Value(staticlawyerslist);
    }

A few things to consider:

  1. When you do this "staticLawyersList = lawyersList" you actually have two "pointers" to the same list. It works that way for lists, sets, classes, etc.. only basic types as int, double, string are really copied.
    You can use this instead: "staticLawyersList = List.from(lawyersList);"

  2. It doesn't seem you need the ChangeNotifier in your LawyerAPIServices. You could create an instance of LawyerAPIServices in the widget you need it and call fetchLawyers. Do it in the initState of a StatefullWidget if you don't want the list to be rebuilt multiple times. In your build method use a FutureBuilder to read the Future and decide what to show in the 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]),
              );
            });
      }
    }
  1. If for any reason you need to share the same LawyerAPIServices across multiple widgets, you could instantiate it on the top of your tree and send it down using Provider or as a parameter.

  2. The method getFullListOfLawyers doesn't need to return a Future, since staticLawyersList is a List (not a Future). You could get this list directly using "LawyerAPIServices.staticLawyersList" or maybe something like this could make sense:

    Future<List> getFullListOfLawyers() async {
    if(staticLawyersList.isEmpty) {
    await fetchLawyers();
    }
    print('fulll list: ${staticLawyersList.length}');
    return Future.value(staticLawyersList);
    }

够运 2025-02-17 01:50:16

正如 @saichi-okuma所说,要复制列表的内容,您应该使用staticlawyerslist = list.from(lawyerSlist),因为在DART和大多数Java编译器编程语言中, = lawyerslist 这意味着您是通过staticlawyerslist引用LawyerSlist
然后,我可以在lawyerslist上操纵staticlawyerslist

lawyersList.clear();
lawyersList.addAll(staticLawyersList);

但是当我这样做时,消费者没有根据staticlawyerslist 尽管logcat显示staticlawyerslist长度是我想要的(无过滤的完整列表)。

我的问题的结论可以用两点列出:

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);
}

}

as @Saichi-Okuma said that to copy the content of a list you should use staticLawyersList = List.from(lawyersList) because in dart and most of the java compiler programming languages when you use staticLawyersList = lawyersList this means that you are referring to the lawyersList by the staticLawyersList.
then I manipulate the lawyersList as I want with help of staticLawyersList

lawyersList.clear();
lawyersList.addAll(staticLawyersList);

But when I did so, the consumer didn't apply the changes based on the staticLawyersList although the logcat shows that the staticLawyersList length is 10 which is what I want (full list without filtration).

the conclusion of my problem can be listed in two points:

1- the consumer is listening to only one list lawyersList and I think it still exists.
2- the pointer problem as @Saichi-Okuma mentioned.

here are the full code changes

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);
}

}

神妖 2025-02-17 01:50:16

每当您拨打NotifyListeners时,消费者小部件都会重建,无论列表的状态如何。

也许您无法访问所消耗的API实例。确保您使用的是消费者构建器的第二个参数。

    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();
            }
          }),

我认为您不需要下面的代码来满足这种特殊需求。它覆盖了您的律师列表,即使没有真正改变,也会通知所有听众。只需直接访问您的staticlawyerslist,因为当您打电话给fetchlawyers时,它就会填充。

void getFullListOfLawyers() {
lawyersList.clear(); // to make sure that the list is clean from older operations
lawyersList.addAll(staticLawyersList);// the trick
notifyListeners();
}

The Consumer Widget gets rebuild every time you call notify notifyListeners, regardless the state of any lists.

Maybe you are not accessing the Instance of the API being consumed. Make sure you are using the 2nd parameter of the Consumer builder.

    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();
            }
          }),

I don't think you need the code below for this particular need. It'd override your lawyersList and notify to all listeners even though nothing really changed. Just access your staticLawyersList directly, since it was populated when you called fetchLawyers.

void getFullListOfLawyers() {
lawyersList.clear(); // to make sure that the list is clean from older operations
lawyersList.addAll(staticLawyersList);// the trick
notifyListeners();
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文