Dart/Flutter: What does copyWith() do?

Dart/Flutter: What does copyWith() do?

Although the notion of copyWith() isn't specifically related to Dart or Flutter, the pattern is used throughout quite often and I see this question pop up all the time.

I'd give this the following definition:

copyWith allows us to copy a T and pass in arguments that overwrite settable values.

Let's take a look at how it's used, as well as ways to create our own copyWith function!  

Immutable copyWith uses in Flutter

This pattern is commonly used when theming items within a Flutter application, such as taking a textTheme and overriding properties:

  "I'm using a custom title",
  style: Theme.of(context)
                color: Colors.red, 
                fontWeight: FontWeight.bold),
We're copying the headline theme and overriding some values such as color and fontWeight.

You'll find that this immutable, copy first pattern is used in other places such as withOpacity:


How do we make our own copyWith?

Let's make a very basic example of this with a Product class:

class Product {
  final String id;
  final String name;
  final Color color;

  Product({this.id, this.color, this.name});

  Product copyWith({String id, String name, Color color}) => Product(
        id: id ?? this.id,
        name: name ?? this.name,
        color: color ?? this.color,

Within our copyWith function we're taking in the potential overrides such as id, name and color. We're then returning a new Product with either the current values, or replacing those values with the ones passed in.

We can use this to display a list of our dining room tables that we have for sale. They both have the same name, but have a different id and color:

class _HomePageState extends State<HomePage> {
  List<Product> products = [];

  void initState() {

    Product sixSeaterDiningTableBrown = Product(
      id: "0",
      name: "6 Seater Dining Table",
      color: Colors.brown,

    Product sixSeaterDiningTableBlack =
        sixSeaterDiningTableBrown.copyWith(color: Colors.black, id: "1");


  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Hello, copyWith!"),
      body: ListView(
          children: products
              .map((Product product) => ListTile(
                    trailing: Container(
                      width: 14,
                      height: 14,
                      color: product.color,
                    title: Text(product.name),
                    subtitle: Text(product.id),

If we take a look at our HomePage, we can see both of our items in the ListView. Both with different id and color values, but the name remains the same.

Does anyone want to buy a table?