Dart: spread operator for constructor
Asked Answered
K

5

11

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'),
)
Krisha answered 7/3, 2019 at 10:34 Comment(0)
E
3

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.

Exmoor answered 7/3, 2019 at 10:49 Comment(2)
But this way I get errors in the borderBase variable saying Undefined name 'color', and other linter errors.Krisha
Oh, I think you meant the borderBase var should use the BorderSide() class, right? Cause that seems to work.Krisha
C
10

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)

Chiro answered 7/3, 2019 at 10:36 Comment(3)
I see. So ther's no way to re-use the same properties dynamically? Meaning I have to repeat them everytime?Krisha
Yes, you're forced to. But you can discuss about it on the issue. I think this is an interesting feature too.Kishakishinev
2022 - ZzZzzzzZzZzzCoati
C
4

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,
    );
  }
}
Conceit answered 13/12, 2019 at 18:0 Comment(0)
E
3

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.

Exmoor answered 7/3, 2019 at 10:49 Comment(2)
But this way I get errors in the borderBase variable saying Undefined name 'color', and other linter errors.Krisha
Oh, I think you meant the borderBase var should use the BorderSide() class, right? Cause that seems to work.Krisha
F
2

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'));
}
Fierce answered 14/3, 2019 at 11:49 Comment(0)
P
0

The spread operator will work on Maps e.g. Map<String, dynamic> so what worked for me was to convert the object to json which is Map<String, dynamic>, use the spread operator and then convert back to the object.

You can try something like this:

class Something {
  final String id;
  final String name;
  
  Something({required this.id, required this.name});

  factory Something.fromJson(Map<String, dynamic?> json){
    return Something(
      id: json['id'],
      name: json['name']
    );
  }

  Map<String, dynamic?> toJson() {
    return {
    'id': id,
    'name': name
    };
  }

}


final x = Something(id: 'abc', name: 'def');
final y = Something.fromJson({...x.toJson(), 'name': 'somethingElse'})

This might be very expensive to do, but if all you care about is using the spread operator, this should do the job.

Parlance answered 30/5, 2021 at 18:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.