Search code examples
flutterdartmobile-applicationflutter-change-notifier

how to let consumer listen to multiple parameters in flutter?


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

Solution

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

    }