-2

I'm working on a Flutter project in which the architecture forces me to use an instance of a class, say UserModel, as a value attached to the BuildContext. In this way, I can access that class in every part of the app.

Anyway, there are moments in which I want to update the UserModel, and not only by changing the value of some of the fields, but updating all of them. At this moment, I am using an horrible function, let's call that updateUser(final UserModel newInstance). It follows an example.

class UserModel {
  String name, surname;

  UserModel({required this.name, required this.surname});

  void updateUser(final UserModel newInstance) {
    name = newInstance.name;
    surname = newInstance.surname;
  }
}

The main problem with this solution is that it often happens that I add some fields to the UserModel class, but I forgot to add them to updateUser. Also, the solution I am using looks horrible.

I know that many of you could say that I should enclose the instance of UserModel in a UserModelProvider class and just replace the value of the userModel field. This would not be compatible with the architecture of the software I am developing, so please don't suggest me that.

  • Are you wanting to inspect the `newInstance` parameter for all fields, and assign every `newInstance` field to you current instance? That's the type of thing JavaScript or Python can do, but Dart is statically typed, so would need a lot of code, if it's possible at all. – John Bayko Apr 08 '23 at 17:04
  • Yeah, you got me! That's the goal of what I need. – Emilio Dalla Torre Apr 08 '23 at 17:06
  • Java supports this, but for Dart I think you need the `reflectable` package, and annotate the classes you want to inspect with `@Reflector`. But in a statically typed language, it's not simple (e.g. you can't just do an assign, you need to determine field type and have an assign for each type). – John Bayko Apr 08 '23 at 17:16
  • You could sidestep all static typing and store everything in a `Map` of `Object`s. Just copy the map and you're done. But there are a lot of reasons not to do that. – John Bayko Apr 08 '23 at 17:19

2 Answers2

0

To be honest, it's hard to understand your issue.

If I'm not wrong, you are looking for copyWith method.

class UserModel {
  String name, surname;

  UserModel({required this.name, required this.surname});

  UserModal copyWith({
     String? name,
     String? surname,
  }) => UserModel(
          name: name?? this.name,
          surname: surname ?? this.surname);
}

With this method, you will be able to update one or both variable.

final  foo = UserModel("foo","foo2");

final boo = foo.copyWith(); // boo = UserModel("foo","foo2");

final yoo = foo.copyWith(name:"yoo"); // yoo = UserModel("yoo","foo2");

// update foo
 foo = foo.copyWith(surname:"update foo"); // foo = UserModel("foo","update foo");

Updated

I don't know why you force to update it, the concept is the same.

class UserModel {
...
 UserModel updateWITHMODELASYOUWANT( UserModel data){
  return UserModel(
    name: data.name, 
    surname: data.surname,
 }
}

As I said in the comments, you update the instance, not the object model.

foo is an instance of UserModel

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
pmatatias
  • 3,491
  • 3
  • 10
  • 30
  • Hi, nope: what I need to do is update an entire instance of a class with a method inside that same class. I mean, roughly speaking, assigning "this". – Emilio Dalla Torre Apr 08 '23 at 15:40
  • boo is new instance. foo = foo.copyWith() is also new updated instance – pmatatias Apr 08 '23 at 15:42
  • if you want update the instance, you need to do is update the `foo`, not the `UserModel`. – pmatatias Apr 08 '23 at 15:47
  • No, the point is that I need to assign an instance of a field to a newer one, inside a method of the same instance. Just look at my code. If I could be able to loop through the fields of UserModel, I would have the solution. – Emilio Dalla Torre Apr 08 '23 at 15:48
  • its same. just change the argument – pmatatias Apr 08 '23 at 15:53
  • The concept is not the same, you are, with your update, returning a NEW instance of UserModel. I need to update all the fields of the current instance - that is btw a ChangeNotifier, and that's why I need the instance to stay the same - without reassigning it. So, as I already mentioned, I want to do something similar to looping on all the new instance fields and assigning them to the instance that I am in. – Emilio Dalla Torre Apr 08 '23 at 16:01
  • i didnt see any `ChangeNotifier` there. – pmatatias Apr 08 '23 at 16:03
  • `foo` is an instance. i repeat foo is an instance. thats why the method iside the class is return thats model – pmatatias Apr 08 '23 at 16:04
  • Please see my own answer that explains the solution and lets you understand whaat I was looking for. – Emilio Dalla Torre Apr 08 '23 at 17:25
-2

I got it sorted out. The main point to understand is to use reflection, the ability of a program to examine and modify its own structure, behavior, and metadata at runtime.

To do so, I edit my updateUser function to be able to loop through the class variables and set them in the new instance. Here is the result.

import 'dart:mirrors';

class UserModel {
  String name, surname;

  UserModel({required this.name, required this.surname});

  void updateUser(final UserModel newInstance) {
    final InstanceMirror thisInstanceMirror = reflect(this);
    final InstanceMirror newInstanceMirror = reflect(newInstance);

    final ClassMirror classMirror = thisInstanceMirror.type;

    for (final DeclarationMirror otherField in classMirror.declarations.values) {
      // We exclude fields that are not variables, like functions
      if (otherField is VariableMirror) {
        // We exclude constant, final and static variables
        if (!otherField.isConst && !otherField.isFinal && !otherField.isStatic) {
          // Finally we update the field with the new value
          thisInstanceMirror.setField(otherField.simpleName,
            newInstanceMirror.getField(otherField.simpleName).reflectee);
        }
      }
    }
  }

  @override
  String toString() {
    return "Name: $name, Surname: $surname";
  }
}

So now I can use an instance of the class to update itself, for instance by fetching new values from Firestore. It follows an example of the functioning.

void main() {
  final UserModel user = UserModel(name: "John", surname: "Doe");
  final UserModel user2 = UserModel(name: "Jane", surname: "Doe");

  print(user); // Name: John, Surname: Doe
  print(user2); // Name: Jane, Surname: Doe

  user.updateUser(user2);

  print(user); // Name: Jane, Surname: Doe
}

And this is crucial if my UserModel is being served by Provider, and therefore attached to the BuildContext.