Dart: spread operator for constructor

6,014

Solution 1

You can do something like this:

const BorderSide borderBase = BorderSide(
  color: Colors.red,
  width: 2,
  style: BorderStyle.solid,
);

Container(
  decoration: BoxDecoration(
    border: Border.all(
      color: borderBase.color,
      width: borderBase.width,
      style: borderBase.style,
    ),
  ),
  child: Text('Container 1'),
)

Container(
  decoration: BoxDecoration(
    border: Border(
      top: borderBase,
    ),
  ),
  child: Text('Container 2'),
)

Not the best but still some reuse.

Solution 2

There is no such thing.

A spread operator is in development, but it is only for lists, not classes (https://github.com/dart-lang/language/issues/47)

Solution 3

Now that dart has both the spread operator and class extensions, you could abuse both to add ...spread support to static methods. While I doubt this is a good idea, I made a working dartpad (gist) to demonstrate. The end result looks something like:

final Border spreadBorder = Border.fromSides([
  ...Border(
    left: BorderSide(color: Colors.pink),
    right: BorderSide(color: Colors.pinkAccent),
  ).sides,
  ...Border(
    top: BorderSide(color: Colors.blue),
    bottom: BorderSide(color: Colors.blueAccent),
  ).sides,
]).scale(5);

enum _Side { top, right, bottom, left }

extension SpreadBorder on Border {
  Iterable<MapEntry<_Side, BorderSide>> get sides {
    return () sync* {
      if (top != BorderSide.none) {
        yield MapEntry(_Side.top, top);
      }
      // ...other yields
    }();
  }

  static Border fromSides(Iterable<MapEntry<_Side, BorderSide>> parts) {
    BorderSide top, right, bottom, left;

    for (final borderPart in parts) {
      switch (borderPart.key) {
        case _Side.top:
          top = borderPart.value;
          break;
        // ... other cases
      }
    }
    return Border(
      top: top,
      right: right,
      bottom: bottom,
      left: left,
    );
  }
}

Solution 4

Here is a dummy sample of my poor man's spread operator pattern:

I make a copy constructor for classes often re-created with slight modifications. It is extra work during definition, but it pays off at many locations when using these classes. The same pattern can be used on standard classes by subclassing -- just to connect to the specific question.

class Address {
    final String street;
    final String city;
    final String state;

    Address({this.street, this.city, this.state});

    Address.copy(Address copy, {
        String street,
        String city,
        String state,
    }) : this (
        street: street ?? copy.street,
        city: city ?? copy.city,
        state: state ?? copy.state,
    );
}

class User {
    final String firstName;
    final String lastName;
    final Address address;
    final String email;

    User({this.firstName, this.lastName, this.address, this.email});

    User.copy(User copy, {
        String firstName,
        String lastName,
        Address address,
        String email,
    }) : this (
        firstName: firstName ?? copy.firstName,
        lastName: lastName ?? copy.lastName,
        address: address ?? copy.address,
        email: email ?? copy.email,
    );
}

void workWithUsers(User user) {
    final userChangedEmail = User.copy(user, email: '[email protected]');
    final userMovedToAnotherStreet = User.copy(user, address: Address.copy(user.address, street: 'Newstreet'));
}
Share:
6,014
Giraldi
Author by

Giraldi

Updated on December 09, 2022

Comments

  • Giraldi
    Giraldi over 1 year

    In my flutter app, I have widgets like below:

    Container(
      decoration: BoxDecoration(
        border: Border.all(
          color: Colors.red,
          width: 2,
          style: BorderStyle.solid,
        ),
      ),
      child: Text('Container 1'),
    )
    
    Container(
      decoration: BoxDecoration(
        border: Border(
          top: BorderSide(
            color: Colors.red,
            width: 2,
            style: BorderStyle.solid,
          ),
        ),
      ),
      child: Text('Container 2'),
    )
    

    Both use the same properties for their borders. So I was wondering if there's a spread-operator-like way of inserting the same properties for both widgets? Maybe like:

    const borderBase = (
      color: Colors.red,
      width: 2,
      style: BorderStyle.solid,
    )
    
    Container(
      decoration: BoxDecoration(
        border: Border.all(
          ...borderBase,
        ),
      ),
      child: Text('Container 1'),
    )
    
    Container(
      decoration: BoxDecoration(
        border: Border(
          top: BorderSide(
            ...borderBase,
          ),
        ),
      ),
      child: Text('Container 2'),
    )
    
  • Giraldi
    Giraldi about 5 years
    I see. So ther's no way to re-use the same properties dynamically? Meaning I have to repeat them everytime?
  • Rémi Rousselet
    Rémi Rousselet about 5 years
    Yes, you're forced to. But you can discuss about it on the issue. I think this is an interesting feature too.
  • Giraldi
    Giraldi about 5 years
    But this way I get errors in the borderBase variable saying Undefined name 'color', and other linter errors.
  • Giraldi
    Giraldi about 5 years
    Oh, I think you meant the borderBase var should use the BorderSide() class, right? Cause that seems to work.