What is copyWith and how can I use it in Flutter and what are some of it's use cases?
Asked Answered
F

3

42
//File:  email_sign_in_model.dart

class EmailSignInModel {
  EmailSignInModel({
    this.email='',
    this.formType=EmailSignInFormType.signIn,
    this.isLoading=false,
    this.password='',
    this.submitted=false,
  });

  final String email;
  final String password;
  final EmailSignInFormType formType;
  final bool isLoading;
  final bool submitted;

  EmailSignInModel copyWith({
    String email,
    String password,
    EmailSignInFormType formType,
    bool isLoading,
    bool submitted,

  }) {
    return EmailSignInModel(
    email: email ?? this.email,
    password: password?? this.password,
    formType: formType?? this.formType,
    isLoading: isLoading?? this.isLoading,
    submitted: submitted?? this.submitted
   
    );
  }
}



//File: email_sign_in_bloc.dart

import 'dart:async';
import 'package:timetrackerapp/app/sign_in/email_sign_in_model.dart';

class EmailSignInBloc {
 final StreamController<EmailSignInModel> _modelController = StreamController<EmailSignInModel>();
 Stream<EmailSignInModel> get modelStream => _modelController.stream;
 EmailSignInModel _model = EmailSignInModel();

 void dispose() {
   _modelController.close();
 }

void updateWith({
  String email,
  String password,
  EmailSignInFormType formType,
  bool isLoading,
  bool submitted

}) {
  //update model
  _model = _model.copyWith(
    email:email,
    password: password,
    formType: formType,
    isLoading: isLoading,
    submitted: submitted


  );
  //add updated model _tomodelController
  _modelController.add(_model);
}

}

Hi, I am new to Flutter and dart and trying to learn bloc in Flutter, I am trying to use BLOC and the also created a model class. My question is What is that copyWith({}) and what it is doing for the email_sign_in_model and for that email_sign_in_bloc? and what is that updateWith doing in the code? Thank you so much!

Flanch answered 14/6, 2020 at 12:33 Comment(1)
Does this answer your question? What does copyWith() function do?Rama
S
46

Let's say you have an object in which you want to change some properties. One way to do is by changing each property at a time like object.prop1 = x object.prop2 = y and so on. This will go cumbersome if you have more than few properties to change. Then copyWith method comes handy. This method takes all the properties(which need to change) and their corresponding values and returns new object with your desired properties.

updateWith method is doing same thing by again calling copyWith method and at the end it is pushing returned object to stream.

Spellbinder answered 14/6, 2020 at 14:22 Comment(1)
At the end of return EmailSignInModel(email: email??this.email,.............)........which value does 'this.email' refers to? Does it refer to the first created class, its default values of the constructor at the top?Flanch
K
31

Lets say you have a class like it:

class PostSuccess {
  final List<Post> posts;
  final bool hasReachedMax;

  const PostSuccess({this.posts, this.hasReachedMax});

functionOne(){
///Body here

} 
}

Lets say you want to change some properties of the class on the go, What you can do you can declare the copyWith method like that:

PostSuccess copyWith({
    List<Post>? posts,
    bool? hasReachedMax,
  }) {
    return PostSuccess(
      posts: posts ?? this.posts,
      hasReachedMax: hasReachedMax ?? this.hasReachedMax,
    );
  }
  

As you see, in the return section, you can change the value of the properties as required by your situation and return the object.

Kandicekandinsky answered 14/9, 2020 at 2:40 Comment(0)
G
22

copyWith as answered previously is a simple method which can help mutate an object. Although we can also use simple setters to do so. I would elaborate the answer as it contains bloc tag in it.

Bloc & models
Immutable models are recommended while using bloc pattern as bloc pattern expects the user to use classes or models which support value comparison. And to support value comparison it is recommended that the class should be immutable. (Immutable objects are needed for efficient builds you can learn more about it here).

And if the class is immutable you cannot use setters to change the property of object. Rather you need to use copyWith method to create a new instance of the object using the old values.

Example
model.dart

class Model {
  String name;
  String value;

  Model({required this.name, required this.value});
}

here the object does not support value comparison. Hence one can use:

void main(){
  final model = Model(name: 'my model', value: '1.9');
  model.value = '2.0';
}

But when it comes to models that are immutable.

@immutable
class Model {
  final String name;
  final String value;

  const Model({required this.name, required this.value});

  Model copyWith({
    String? name,
    String? value,
  }) {
    return Model(
      name: name ?? this.name,
      value: value ?? this.value,
    );
  }

  @override
  bool operator ==(Object other) {
    if (identical(this, other)) return true;
  
    return other is Model &&
      other.name == name &&
      other.value == value;
  }

  @override
  int get hashCode => name.hashCode ^ value.hashCode;
}

Now you won't be able to use setters as they are final fields. So you have to use copyWith method.

void main(){
  final model = Model(name: 'my model', value: '1.9');
  model.copyWith(value: '2.0');
}

Now in the above object's copyWith you cannot make a value nullable. To do that we use functional params in copyWith method.

@immutable
class Model {
  final String name;
  final String? value;

  const Model({required this.name, required this.value});

  @override
  bool operator ==(Object other) {
    if (identical(this, other)) return true;

    return other is Model && other.name == name && other.value == value;
  }

  @override
  int get hashCode => name.hashCode ^ value.hashCode;

  Model copyWith({
    String? name,
    String? Function()? value,
  }) {
    return Model(
      name: name ?? this.name,
      value: value != null ? value() : this.value,
    );
  }
}

Now you will be able to pass null values as well.

void main(){
  final model = Model(name: 'my model', value: '1.9');
  model.copyWith(value: () => null);
}

I hope this will help you use bloc better.

Gerhardine answered 21/6, 2022 at 14:33 Comment(1)
Precisely what I was looking for. Thank youCaul

© 2022 - 2024 — McMap. All rights reserved.