Getting Familiar with Dart

Functions


Learning Objectives

  • You know how to define functions in Dart.
  • You know of pass-by-value and pass-by-reference.
  • You know of functions as objects and you know of higher-order functions.

Almost all programming languages provide a mechanism to divide the code into smaller, reusable parts — typically functions. Functions are blocks of code that perform a specific task and can be called from other parts of the program. Functions help in organizing code, making it more readable, and reducing redundancy.

Defining functions

In Dart, functions consist of a return type, function name, optional parameters, and a function body, like in many other programming languages. For example, the following program shows the function main, which is the entrypoint to all Dart applications.

Run the program to see the output

Above, the return type is void (i.e., nothing), the name is main, there are no parameters, and the function body is empty.

Dart uses lowerCamelCase for naming functions and variables. This means that the first letter of the function name is lowercase, and the first letter of each subsequent word is capitalized. For example, displaySum is a function name that follows the lowerCamelCase naming convention.

Function parameters

Function parameters are defined inside the parentheses of the function declaration. If there are multiple parameters, the parameters are separated by commas. Below, the function displaySum takes two integers a and b as parameters, and prints their sum.

Run the program to see the output

Functions can also have optional parameters. Optional parameters are defined by enclosing the parameter name in square brackets. In the following example, the function displaySum has an optional parameter c that defaults to 0.

Run the program to see the output

Optional parameters can also be named parameters. Named parameters are defined by enclosing the parameter name in curly braces. Named parameters can be passed in any order, and they can be omitted if they have default values.

In the following example, the function displaySum has named parameters b and c that both default to 0. When the function is called, the values of b and c are passed by name.

Run the program to see the output

Pass-by-value and object references

Pass-by-value means that the function receives a copy of the value of the variable, not the original variable itself. An alternative option would be pass-by-reference, where the function receives a reference to the original variable. The key difference between these two are that pass-by-value does not allow the function to modify the original variable, while pass-by-reference does.

Dart uses pass-by-value for all variable passing, but the behavior to some extent depends on the type of the variable being passed. When passing primitive types (e.g. int, double, String, bool), the behavior is the same as pass-by-value in other languages. When passing objects, the value being passed is the reference to the object, i.e. the whole object is not copied.

For primitive types, the function receives a copy of the value, and any modifications to the parameter inside the function do not affect the original variable outside the function.

Run the program to see the output

When passing objects, the value being passed is the reference to the object. This means that calling methods or making modifications to the object’s properties within the function can affect the original object, as the copy of the reference points to the same object.

Run the program to see the output

At the same time, reassigning the variable inside the function does not affect the original variable outside the function, as the reference is changed to point to a new object.

Run the program to see the output

This distinction is important to understand the behavior of functions when working with both primitive types and complex objects.

Primitive variables like String and int are immutable in Dart, meaning that their values cannot be changed once they are assigned. When you modify an immutable variable, you are actually creating a new variable with a new value that replaces the old one. This is why the original variable remains unchanged when passed to a function.

Loading Exercise...

Returning values

Functions can return values using the return keyword, which is followed by the value to be returned. The return type is declared before the function name.

Run the program to see the output

Note that although in some languages like Scala where the last expression in a function is automatically returned, in Dart, you need to explicitly use the return keyword to return a value from a function.

That is, the following function would not return the sum of a and b. When you try to run the program, you’ll notice an error from the compiler.

Run the program to see the output

Dart also supports the arrow syntax for single expression functions. The arrow => replaces the curly braces and the (optional) return keyword.

Run the program to see the output

While Dart could infer the types of parameters and return values, they should always be explicitly declared. This makes the code more readable and helps catch errors early in the development process.

Loading Exercise...

Functions as objects and higher-order functions

In Dart, functions are first-class objects. This means that functions can be assigned to variables, passed as parameters to other functions, and even returned from other functions.

The following example shows assigning a function to a variable and calling the function using the variable.

Run the program to see the output

Functions can be passed as parameters to other functions, enabling the creation of higher-order functions. A higher-order function is a function that accepts another function as a parameter or returns a function.

Run the program to see the output

Higher-order functions can also return other functions. This allows you to create closures or dynamically generate new functions based on input.

Run the program to see the output

Above, the getMultiplier function generates and returns a closure, which is a function that remembers its surrounding context (the factor variable in this case).

Loading Exercise...