3

Let's say I have a data class Dog that includes a List of puppies.

@immutable
class Dog extends Equatable{
    final int id;
    final List<Puppy> listOfPuppies;

    Dog({required this.id,required  this.listOfPuppies});


    Dog copyWith({int? newId, List<Puppy>? newListOfPuppies}){
        return Dog(id: newId ?? id, listOfPuppies: newListOfPuppies ?? listOfPuppies);
}

  @override
  List<Object?> get props => [id, listOfPuppies];
}

@immutable
class Puppy extends Equatable{
  final String name;
  final int age;

  Puppy({required this.name, required this.age});
  
  Puppy copyWith({int? newAge, String? newName}){
        return Puppy(age: newAge?? age, name: newName ?? name);
  }
    
  @override
  List<Object?> get props => [name, age];
}

And later down the line I need to update one of the puppies inside the dog class without:

  1. Changing the order of the listOfPuppies variable provided using Riverpod (important)
  2. Affecting the other puppies and possibly re-creating them unnecessarily (not important)

For reference I'm using Riverpod and providing the dog anywhere in the app. This is the controller class:

class DogController extends StateNotifier<Dog>{
  DogController(super.state);
  
  void updatePuppy(Puppy newPuppy){
    //update specific puppy here inside the listOfPuppies list
    //state = state.copyWith(listOfPuppies: );
  }
}

I'm clueless on how I need to update the puppy using the constraints given above.

bqubique
  • 678
  • 6
  • 17

2 Answers2

2

You can do it this way:

void updatePuppy(Puppy newPuppy){
    
    final oldListOfPuppies = state.listOfPuppies;
    final indexNewPuppy = state.listOfPuppies.indexOf(newPuppy);
    
    oldListOfPuppies[indexNewPuppy] = newPuppy;
    
    state = state.copyWith(
    newListOfPuppies: oldListOfPuppies
    );
  }
Ruble
  • 2,589
  • 3
  • 6
  • 29
  • This definitely was the way to go! I had an even more complex situation (nested objects inside lists) and was able to overcome it using the method you suggested. I'm accepting this as the correct answer, and will be adding my situation and the solution I applied. Thanks for your insight Ruble! – bqubique Feb 28 '23 at 23:08
0

My case was a bit more tedious, but with the given instructions from Ruble I was able to update everything smoothly. In my case there was only one more model that was including the Dog class.

Owner model:

class Owner extends Equatable {
  final List<Dog> dogs;
  
  const Owner({required this.dogs});

  Owner copyWith(List<Dog>? newDogs){
    return Owner(dogs: newDogs?? dogs);
  }
  ...
}

Dog model:

class Dog extends Equatable {
  final List<Puppy> puppies;
  
  const Dog({required this.puppies});

  Dog copyWith(List<Puppy>? newPuppies){
    return Dog(puppies: newPuppies?? puppies);
  }
  ...
}

And finally the Puppy model was as following:

class Puppy extends Equatable {
  final bool isVaccinated;

  const Puppy({required this.isVaccinated});

  Puppy copyWith(bool? newIsVaccinated){
    return Puppy(isEnabled: newIsVaccinated?? isVaccinated);
  }
  ...
}

To update the isVaccinated field inside one of the Puppy models I had to follow these steps inside the controller class:

void updatePuppy(Puppy puppyToUpdate, bool newIsVaccinated){
  //1. find the Dog index that contains the Puppy passed as parameter
  final dogIndex = state.owner.dogs!.indexWhere((dog) => dog.puppies!.contains(puppyToUpdate));
  //2. save all old dogs so we can update them later
  final listOfOldDogs = state.owner.dogs!;
  //3. save the dog that contains the puppy we need (with the index we found in step 1)
  final selectedDog = state.owner.dogs![dogIndex];
  //4. save all old puppies so we can update them later
  final listOfOldPuppies = selectedDog.puppies!;
  //find the puppy index that is equal to the puppy passed as parameter
  final puppyIndex = selectedDog.puppies!.indexWhere((puppy) => puppy == puppyToUpdate);
  //update relevant puppy inside the old puppy list (saved in step 4)
  listOfOldPuppies[puppyIndex] = puppyToUpdate.copyWith(isVaccinated: newIsVaccinated);
  //create a section same as the one we selected above in [selectedSection] variable, by just updating its list of fields
  final updatedDog = selectedDog.copyWith(puppies: listOfOldPuppies);
  //update the selected dog in the listOfOldDogs list (saved in step 2)
  listOfOldDogs[dogIndex] = updatedDog;
  //copy the whole list of dogs into the owner using its copyWith method
  final newOwner = state.owner.copyWith(dogs: listOfOldDogs);
  //finally update the state with the new owner
  state = state.copyWith(owner: owner);
}

This way old puppies and dogs were not moved inside the list and only the relevant puppy's isVaccinated field was updated.

bqubique
  • 678
  • 6
  • 17