I want to use named parameters in Dart for clarity. How should I handle them?

34,225

Solution 1

The meta package provides a @required annotation that is supported by the DartAnalyzer.

Flutter uses this a lot and provides @required directly from import 'package:flutter/foundation.dart'

foo({@required String name}) {...}

foo(); // results in static warning

@required doesn't check if the passed value is null or not, only that a value was actually passed on the call site. To check for null you can also use assert() to check for passed values

class Ability {
  Ability(this.name, this.effectDuration, this.recast) : assert(name != null), assert(effectDuration != null), assert(recast != null);
  final name;
  final effectDuration;
  final recast;            // wait time until next use
  // ...
}    

Solution 2

[Update] New as-of Dart 2.0

In dart 2.0 the required keyword has been added to the language as part of the null-safety update. This means that you get a compiler-enforced non-null value rather than one checked by the analyzer; this makes the null check completely redundant.

This means that this code does effectively the same as the old code below, except that you never have to worry about the assertion throwing as the values for name, effectDuration, and recast cannot be null.

class Ability {
  final String name;
  final Duration effectDuration;
  final bool recast;
  final String? description;

  Ability({
    required this.name,
    this.effectDuration = Duration(seconds: 1),
    this.recast = false,
    this.description,
  });
}

Before Dart 2.0

Yes, there is!

Here's an example:

class Ability {
  final String name;
  final Duration effectDuration;
  final bool recast;
  final String description;

  Ability({
    @required this.name,
    this.effectDuration = new Duration(seconds: 1),
    this.recast = false,
    this.description,
  }): 
    assert(name != null),
    assert(effectDuration != null);
}

You don't have to assert that name is not equal to null, but it might be useful for you.

Solution 3

Although you could use the flutter foundation package as described in the accepted answer, when I am working with model classes that don't need to know about Flutter, I prefer to use the meta package directly. That way it doesn't create an unnecessary dependency on the framework. This allows you to share the Dart code even outside of Flutter.

Add meta to pubspec.yaml:

dependencies:
  meta: ^1.1.7

Import it in your class file:

import 'package:meta/meta.dart';

Use the @required annotation in your code:

class Person {
  String name;
  int age;

  Person({@required this.name, this.age,});
}

So name is a required parameter, but age isn't.

final person = Person(name: 'Bob');

Update:

In an upcoming version of Dart, the required keyword should be added by default, so no imports will be necessary at all.

Solution 4

As of 2.12 with null safety you can use required keyword (not @required). Also no need to import any additional packages.

In this example named parameter name is optional while effectDuration and recast are required.

class Ability {
  final name;
  final effectDuration;
  final recast;

  Ability({this.name, required this.effectDuration, required this.recast});
}

Update pubspec.yaml, for example:

environment:
  sdk: ">=2.12.0-0  <3.0.0"

References:

  1. Sound null safety
  2. How does @required compare to the new required keyword?
Share:
34,225

Related videos on Youtube

Kafeaulait
Author by

Kafeaulait

A bit of Java and Python for OOP, Octave and Linear Algebra for Machine Learning.

Updated on October 06, 2021

Comments

  • Kafeaulait
    Kafeaulait over 2 years

    TL;DR: Named parameters are optional as a result of a conscious design choice. Short of having official language support, is there any way to enforce (and inform) required named arguments?


    I find it extremely useful to use named parameters when defining a class. Take, for instance, an Ability in an MMORPG:

    class Ability {
    
      final name;
      final effectDuration;
      final recast;            // wait time until next use
      // ...
    }
    

    effectDuration and recast both carry the same type of information (i.e. duration of time) and are likely represented by the same datatype. It is easy to mix up which number goes where. However, they are both information vital to the correctness of the object, so they can't be missing during instantiation.

    I could just break the program via a try-catch to enforce the requirement of those parameters, but that doesn't sound like fun for someone who uses the class and has no idea (short of reading the docs and understanding intuitively what the class does) that they are required.

    Is there any way to enforce the requirement of certain named parameters while managing to inform the caller of said requirement and/or help them use it correctly?

  • Kafeaulait
    Kafeaulait about 6 years
    The asserts act as a run-time fail-safe, right? Ideally the @required annotation should throw some warning at the user when they invoke the constructor incorrectly. Neat, thanks!
  • rmtmckenzie
    rmtmckenzie about 6 years
    At compile time it will throw an error if you don't pass a name:something, but it can't know if something is null. The assert does that at runtime, but only for debug builds (so it won't slow your production build down!)
  • Rémi Rousselet
    Rémi Rousselet about 6 years
    It's worth adding that flutter has a different parameters convention : always use named parameters.
  • Günter Zöchbauer
    Günter Zöchbauer about 6 years
    I also expect the Dart team to change how parameters work after Dart 2.
  • Sisir
    Sisir over 3 years
    Any updates on the required keyword yet. I am somehow not able to find it from the docuementations.
  • Suragch
    Suragch over 3 years
    @Sisir, It will come with the null safety update. The beta is scheduled for the end of this year and it should enter the stable channel early next year.
  • Richard Haven
    Richard Haven over 3 years
    How is a parameter without a default not implicitly required?
  • Suragch
    Suragch over 3 years
    @RichardHaven, currently it defaults to null.
  • Kamlesh
    Kamlesh about 3 years
    Case optional named parameters - I am using dart class in flutter and code is as: class MyDataObject { final int anInt; final String aString; final double aDouble; MyDataObject({ this.anInt = 1, this.aString = 'Old!', this.aDouble = 2.0, }); } getting error that need to 'Add required keyword' before this.anInt = 1, this.aString = 'Old!' and this.aDouble = 2.0, Kindly suggest what is the issue and how can we fix it. Thanks.
  • Kamlesh
    Kamlesh about 3 years
    Case optional named parameters - I am using dart class in flutter and code is as: class MyDataObject { final int anInt; final String aString; final double aDouble; MyDataObject({ this.anInt = 1, this.aString = 'Old!', this.aDouble = 2.0, }); } getting error that need to 'Add required keyword' before this.anInt = 1, this.aString = 'Old!' and this.aDouble = 2.0, Kindly suggest what is the issue and how can we fix it. Thanks.
  • Kamlesh
    Kamlesh about 3 years
    this solution does not work in NULL SAFATY
  • Kamlesh
    Kamlesh about 3 years
    asserts solution does not work for me in NULL SAFETY. Any suggestion? Please share. Thanks.
  • rmtmckenzie
    rmtmckenzie about 3 years
    @Kamlesh I've updated the example for null-safety. That doesn't seem right; there must be something else going on in your code as what you've posted compiles just fine for me. I would suggest opening a new question with a more complete code example (and ideally reproducible in DartPad) rather than commenting on a question from 2 years ago.