Functions are one of the most important aspects of any programming language and Dart is no different.

Named Functions

Syntax:

Return Type    Function Name ()   Function Body

Unlike JavaScript where we use the keyword function to declare a named function, we do not use function keyword in Dart.

example:

int myFunction() {
  return 100;
}

In the above example we are returning an integer value from myFunction.

Though the return type is optional, it is recommended to use return type for all function declarations. If you are not returning anything, you can use void as return type. If we do not mention any return type, Dart will automatically give dynamic for return type.

Arrow Syntax:

We can also use arrow syntax with named functions in Dart which is not allowed in JavaScript.

The above function can be re-written as this with arrow syntax

int myFunction() => 100;

Passing Parameters:

We can pass parameters to Dart function like in any other programming language.

void main() {
  int additionFunction(int a, int b) => a + b;
  print(additionFunction(100, 200));
}

Output:

300

Anonymous Functions:

Anonymous functions are mostly used as callback functions. The functions that we have seen earlier have names to it and we can invoke them using its name. Anonymous functions usually gets triggered when some event happens, such as when user clicks on a button or when we iterate through a list using map method, we run an anonymous function (though we can pass named functions also, mostly we use anonymous functions) .

Sometimes we use anonymous functions to store a function into a variable.

example:

void main() {
  Function(int, int) additionFunction = (int a, int b) {
    return (a + b);
  };
  print(additionFunction(100, 200));

  var myList = [1, 2, 3, 4];

  var result = myList.map((e) {
    return e + e;
  });
  print(result);
}

Output:

300
(2, 4, 6, 8)

Similar to the way we used arrow syntax with named functions, we can use the same for anonymous functions also. The previous example can be re-written with arrow syntax like this

void main() {
  Function(int, int) additionFunction = (int a, int b) => (a + b);
  print(additionFunction(100, 200));

  var myList = [1, 2, 3, 4];

  var result = myList.map((e) => e + e);
  print(result);
}

Function Parameters:

Required Parameters:

We have seen earlier that we can pass parameters to functions in Dart like in any other programming language.

If we pass the parameters like we did earlier, then all of those parameters are going to be Required Parameters. That means, we must pass all these parameters during a function call, else Dart will show us an error similar to this

“2 positional argument(s) expected, but 1 found.”

Named Parameters:

When we work with functions with required parameters, we must pass the parameters in the same order. If we are passing many parameters, then it may confuse the developer.

example:

void main() {
  void myFunction(int id, String name, String city, String country, int pin,
      String street, String phone) {}

  myFunction(123, 'John', 'NYC', 'US', 10001, 'ABC', '+1-34234');
}

In the above example, we can see that myFunction accepts several parameters of different data types. When we are passing values to this type of functions, it will become difficult to remember the order and also it will be difficult to read the code later.

Named parameters functions makes these types of requirements handle easily and also offer very good readability. We can re-write the above function using named parameters like this

One thing to note with Named parameters is that, they are by default optional. If we want to use them to replace required parameters, then we need to add the required keyword.

void main() {
  void myFunction(
      {required int id,
      required String name,
      required String city,
      required String country,
      required int pin,
      required String street,
      required String phone}) {}

  myFunction(
      id: 123,
      name: 'John',
      street: 'ABC',
      pin: 10001,
      city: 'NYC',
      country: 'US',
      phone: '+1-34234');
}

Optional Named Parameters:

As we discussed before, named parameters are optional by default. If you do not pass a value to a named parameter, Dart will automatically assign null value to that parameter.

example:

void main() {
  void myFunction(
      {required int id,
      required String name,
      required String city,
      String? country,
      int? pin,
      required String street,
      String? phone}) {}

  myFunction(
    id: 123,
    name: 'John',
    city: 'NYC',
    street: 'ABC',
  );
}

In the above example, we made country, pin and phone parameters as optional by removing required and adding ? to the type. In modern JavaScript, the optional parameters needs to be passed only after required parameters, but it is not the same with Dart as you can see in the above example that country and pin optional parameters came before street which is required.

When we are dealing with optional parameters, it is better to do a null check on these parameters before trying to access them.

example:

....
if( country != null ) {
          print(country);
        } else {
          country = 'US';
        }
...

Optional Positional Parameters

We can also receive optional parameters without needing to enter names by using Positional Parameters syntax. Similar to optional named parameters, if we do not pass a value to positional parameters, Dart will automatically assign null value to them.

example:

void main() {
  int myFunction([int? a, int? b]) {
    a = a ?? 100;
    b = b ?? 200;
    return a + b;
  }

  print(myFunction(111, 222));
  print(myFunction(123));
  print(myFunction());
}

Output:

333
323
300

Default Values:

Instead of doing null checks inside the function body to assign a value to a parameter, we can assign default values to named / positional parameters like this

Default values for Named Parameters
void main() {
  int myFunction({int a = 100, int b = 200}) {
    return a + b;
  }

  print(myFunction(a: 111, b: 222));
  print(myFunction(a: 111));
  print(myFunction());
}

Output:

333
311
300
Default Values for Positional Parameters
void main() {
  int myFunction([int a = 100, int b = 200]) {
    return a + b;
  }

  print(myFunction(111, 222));
  print(myFunction(111));
  print(myFunction());
}

Output:

333
311
300

Combining Required and Optional Parameters

We can also receive a combination of required and optional parameters in Dart.

example:

void main() {
  int myFunction(int a, int b, [int? c, int? d]) {
    c = c ?? 100;
    d = d ?? 200;
    return a + b + c + d;
  }

  print(myFunction(111, 222, 333, 444));
  print(myFunction(111, 222, 333));
  print(myFunction(111, 222));
}

In the above example, we are taking a combination of required and optional positional parameters.

Output:

1110
866
633

Similarly we can receive required parameters with optional named parameters.

example:

void main() {
  int myFunction(int a, int b, {int? c, int? d}) {
    c = c ?? 100;
    d = d ?? 200;
    return a + b + c + d;
  }

  print(myFunction(111, 222, c: 333, d: 444));
  print(myFunction(111, 222, d: 333));
  print(myFunction(111, 222));
}

Output:

1110
766
633

One thing to note here is, when we are receiving a combination of required and optional (either named or positional) parameters, then the optional parameters should come only after required parameters.