39

I've been trying to get this right for some time and can't figure out what is wrong.

enum MyEnum { a, b }

class ClassA {
  final MyEnum myEnum;
  ClassA({this.myEnum = MyEnum.a});
}

class ClassB {
  final ClassA classA;
  ClassB({this.classA = ClassA()}); // ClassA expression is underlined with red
}

The IDE (Visual Studio Code) complains with:

[dart] Default values of an optional parameter must be constant.

I've tried to prefix it with const, new, and passing values to the ClassA constructor, but nothing works. Can anyone see what I am doing wrong here?

Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393
mg74
  • 651
  • 1
  • 6
  • 10

5 Answers5

31

Try

enum MyEnum { a, b }

class ClassA {
  final MyEnum myEnum;
  ClassA({this.myEnum});
}

class ClassB {
  final ClassA classA;
  ClassB({this.classA}); // ClassA expression is underlined with red
}

no need for '=' operator. It will automatically assign the value when you will pass it to the constructor.

Use the '=' operator only when you need to pass a default value to your variables hence, making them optional parameters.

Edit

enum MyEnum { a, b }

class ClassA {
  final MyEnum myEnum;
  const ClassA({this.myEnum = MyEnum.a});
}

class ClassB {
  final ClassA classA;
  ClassB({this.classA = const classA()}); // ClassA expression is underlined with red
}

This is the only way i could find to achieve what you want, the constructor should be default

This is called a canonicalized constructor.

Jaswant Singh
  • 9,900
  • 8
  • 29
  • 50
  • Won't I then need to supply value for classA when I instantiate ClassB? I.e. wont I need to do var classB = ClassB(classA: ClassA()) instead of var classB = ClassB() – mg74 Aug 18 '18 at 12:47
  • Yes you have to supply value for A whenever you instantiate B this way, I’ll try to find a workaround. By the way, it is said that no class should instantiate other class but it should get its dependencies from external sources for modularity and high cohesion. – Jaswant Singh Aug 18 '18 at 16:07
  • Yes its similar to the first answer, I think that might be the only way, even I still don't quite understand this whole "canoncialized" thing. Thank you for the help. – mg74 Aug 18 '18 at 20:18
13

For ClassA to be able to create constant values, the constructor must be marked as const.

enum MyEnum { a, b }

class ClassA {
  final MyEnum myEnum;
  const ClassA({this.myEnum = MyEnum.a});  // <- constructor is const.
}

class ClassB {
  final ClassA classA;
  ClassB({this.classA = const ClassA()}); // <- creation is const
}

You need to write const for the object creation, even if only constant values are allowed as default values. The language do not automatically insert a const in that position because the language team wants to reserve the option of allowing non-constant default values at some point.

lrn
  • 64,680
  • 7
  • 105
  • 121
  • 3
    Thank you, but isn't a const class a whole different thing; a type that has a known canonical value at compile time and is usable in switch statements, etc.? The only thing I am really trying to do here is creating an instance of the class with default values. Is that just not possible in Dart? – mg74 Aug 18 '18 at 10:55
  • 2
    There is no such thing as a "const class". Classes may have `const` constructors, which allows them to create const *values* (then the class also has to have only final fields). Constant values are canonicalized, but you can create non-constant values - it's not a property of the class, but of the way you create the individual values. Parameter default values must be const values. So, if you want a default value of type `ClassA`, then `ClassA` must have a const constructor (and only final fields, which it already does). – lrn Aug 19 '18 at 11:19
  • "Parameter default values must be const values" @Irn any idea why? – Alessio Aug 12 '20 at 07:24
12

If the Order date wasn't specified, use DateNow.. If the Order date was specified, use it.

class Order  {
  final String id;
  final double amount;
  final List cartItems;
  final DateTime? dateTime;

  Order(
      {required this.id,
      required this.amount,
      required this.cartItems,
       dateTime
       })
      :this.dateTime = ( dateTime != null ? dateTime : DateTime.now() );
}


void main() {
  
  var order_use_default_date = Order(id:"1",amount:1000,cartItems:[1,2,3]);
  var order_use_param_date = Order(id:"1",amount:1000,cartItems:[1,2,3],dateTime:DateTime.now().subtract(Duration(days: 2)) );
  
  print(order_use_default_date.dateTime);
  print(order_use_param_date.dateTime);
  
  
}
Mark Dionnie
  • 305
  • 4
  • 11
6

Have you tried?

class ClassB {
  final ClassA _classA;
  ClassB({ClassA classA}): _classA = classA ?? ClassA();
}

Suppose let's say you want to set empty list as a default value for a list member, adding 'const' to that default value, will prevent any actions like add() etc. In that case you can use

class A{
  List<int> numbers;

// will raise : Uncaught Error: Unsupported operation: add
// when you call the addEle() method
//   A({this.numbers= const []});  
    A({var passedNumbers}) : this.numbers = passedNumbers??[];
    void addEle(int n){
      numbers.add(n);
    
  }
  }

void main() {
  
  var obj = A();
  obj.addEle(5);

}
Saravanan
  • 566
  • 5
  • 13
1

Use of initializer list

Don't define the parameter in the constructor but rather in the initializer list:

enum MyEnum { a, b }

class ClassA { // a non constant class
  final MyEnum myEnum;
  ClassA({this.myEnum = MyEnum.a});
}

class ClassB {
  final ClassA classA;
  ClassB({ClassA? classA}) : classA = classA ?? ClassA(); //init in initalizer list
}

Also, make sure to check if you can make your ClassA a constant class as already described.

Paul
  • 1,349
  • 1
  • 14
  • 26