Functions in Dart
Dart

Functions in Dart

Hey everyone, welcome to our new article! Today, we’ll learn Functions in Dart programming language. Let’s get started!

Each week, there are tasks that you repeat over and over: eat breakfast, brush your teeth, write your name, read articles about Dart, and so on. Each of those tasks can be divided up into smaller tasks. Brushing your teeth, for example includes putting toothpaste on the brush, brushing each tooth and rinsing your mouth out with water.

The same idea exist in computer programming. A function is one small task, or sometimes a collection of several smaller, related tasks that you can use in conjunction with other functions to accomplish a larger task.

In this article you’ll learn how to write functions in Dart, including both named functions and anonymous functions.

Function basics

You can think of function like machines; they take something you provide to them (the input), and produce something different (the output).

Functions in Dart

There are many example of this in daily life. With an apple juicer, you put in apples and you get out apple juice. The input is applies; the output is juice. A dishwasher is another example. The input is dirty dishes, and the output is clean dishes. Blenders, coffee makers, microwaves and ovens are all like real-world functions that accept an input and produce an output.

Don’t repeat yourself

Assume you have a small, useful piece of code that you’ve repeated n multiple places throughout your program:

Functions in Dart

Now, that code works rather well, but repeating that code in multiple sports presents at least two problems. The first problem is that you’re duplicating effort by having this code in multiple places in your program. The second, and more troubling problem, is that if you need to change the logic in that bit of code later on, you’ll have to track down all of those instances of the code change them in the same way. That creates a high possibility that you’ll make a mistake somewhere, or even miss changing one of the instance because you didn’t see it.

Over time, this problem has led to some sound advice for writing clean code: don’t repeat yourself, abbreviated as DRY. This term was originally coined in the book The Pragmatic Programmer by Andrew Hund and David Thomas.
Writing DRY code will help you prevent many bugs from creeping into your programs.

Function are one of the main solutions to the duplication problem in the example above. Instead of repeating blocks of code in multiple places, you can simple package that code into a function and call that function from wherever you need to.

Anatomy of a Dart function

In Dart, a function consists of a return type, a name, a parameter list in parentheses and a body enclosed in braces.

Functions in Dart

Here is a short summary of the labeled parts of the function:

  • Return Type: This comes first; it tells you immediately what the type will be of the function output. This particular function will return a String, but your function can return any type you like. If the function won’t return anything, that is, if it performs some work but doesn’t produce an output value, you can use void as return type.
  • Function name: You can name function almost anything you like, but you should follow the lowerCamelCase naming convention.
  • Parameters: Parameters are the input to the function; they go inside the parentheses after the function name.
    This example has only one parameter, but if you had more than one, you would separate them with commas.
    For each parameter, you write the type first, followed by the name. Just as with variable names, you should use lowerCamelCase for your parameter names.
  • Return value: This is the function’s output, and it should match the return type. In the example above, the function returns a String value by using the return keyword. If the return type is void, though, then you don’t return anything.

The return type, function name and parameters are collectively known as the function signature. The code between the braces is known as the function body.

This is what the function above looks like in the context of a program:

Functions in Dart

What have we here? Not one function, but two? Yes, main is also a function, and one you’ve seen many times already. It’s the function that every Dart program starts with. Since main doesn’t return a value, the return type of main must be void.

Although main can take parameters, there aren’t any in this case, so there’s only a pair of empty parentheses that follow the function name.

Notice that the compliment function is outside of main. Dart supports top-level functions, which are functions that aren’t inside a class or another function. Conversely, you may nest one function inside another. And when a function is inside a class, it’s called a method.

You call a function by writing its name, and providing the argument, which is the value you provide inside the parentheses as the parameter to the function. In this case, you’re calling the compliment function and passing in an argument of 12. Run the code now and you’ll see the following result:

12 is very nice number. 

It’s easy to get the words parameter and argument mixed up. A parameter is the name and type that you define as an input for your function. An argument, on the other hand, is the actual value that you pass in. A parameter is abstract, while an argument is concrete.

Note

More about parameters

Parameter are incredibly flexible in Dart, so they deserve their own section.

Using multiple parameters

In a Dart function, you can any number of parameters. If you have more then one parameter for you function, simply separate them with commas, Here’s a function with two parameters:

Functions in Dart

Parameter like the ones above are called positional parameters, because you have to supply the arguments in the same order that you defined the parameter when you wrote the function. If you call the function with the parameter in the wrong order, you’ll get something obviously wrong:

Making parameters optional

The function above was very nice, but it was a little rigid. For example, try the follwoing:

helloPersonAndPet();

If you don’t have exactly the right number of parameters, the compiler will complain to you:

2 positional argument(s) expected, but 0 found. 

You defined helloPersonAndPet to take two arguments, but in this case, you didn’t pass in any.
It would be nice if the code could detect this, and just say, “Hello, you two!” if no names are provided. Thankfully, it’s possible to have optional parameters in a Dart function!

Imagine you want a function that takes a first name, a last name and a title, and returns a single string with the various pieces of the person’s name strung together:

Functions in Dart

The thing is, not everyone has a title, or wants to use their title, so your function needs to treat the title as optional. To indicate that a parameter is optional, you surround the parameter with square brackets and add a question mark after the type, like so:

Functions in Dart

Writing [String? title] makes title optional. If you don’t pass in a value for title, then it will have the value of null, which means “no value”. The updated code checks for null to decide how to format the return string.

Here are two example to test it out:

Functions in Dart

Run that now and you’ll see the following:

Tony Stark
The Avengers Tony Stark

The function correctly handles the optional title.

Providing default values

In the example above, you saw that the default values for an optional parameter was null. This isn’t always the best value for a default, though. That’s why Dart also gives you the power to change the default value of any parameter in your function by using the assignment operator.
Take a look at this example:

Functions in Dart

There are three parameters here, two of which are optional: min and max. If you don’t specify a value for them, then min will be 0 and max will be 10.

Here are some specific examples to illustrate that:

Functions in Dart

Since 5 is between 0 and 10, this evaluates to true; but since 15 is greater than the default max of 10, it evaluates to false.

If you want to specify values other than the defaults, you can do that as well:

Functions in Dart

Since 20 is between 10 and 50, the function returns true.

Naming parameter

Dart allows you to use named parameters to make the meaning of the parameters more clear in function calls.
To create a named parameter, you surround it with curly braces instead of square brackets. Here’s the same function as above, but using named parameters instead:

Functions in Dart

Note the following:

  • min and max are surrounded by braces, which means you must use the parameter names when you provide their argument values to the function.
  • Like square brackets, curly braces make the parameters inside optional. Since value isn’t inside the braces, though, it’s still required.

To provide an argument, you use the parameter name, followed by a colon and then the argument value. Here is how you call the function now:

Functions in Dart
withinTolerance(20, min: 10, max: 50);

That’s a lot clearer, isn’t it? The names min and max make it obvious where the tolerance limits are now.

Making named parameters required

You might like to make value a named parameter as well.
That way you could call the function like so:

Functions in Dart

However, this brings up a problem. Named parameter are optional by default, but value can’t be optional. If it were, someone might try to use your function like this:

withinTolerance()

Should that return true or false? It doesn’t make sense to return anything if you don’t give the function a value. This is just a bug waiting to happen.

What you want is to make value required instead of optional, while still keeping it as a named parameter. You can achieve this by including value inside the curly braces and adding the required keyword in front:

Functions in Dart

Since the function signature was getting a little long, adding a comma after the last parameter lets the IDE format it vertically.

Using named parameters makes your code more readable and is an important part of writing clean code when you have multiple inputs to a function.

Avoiding side effects

When you take medicine to cure a medical problem, but that medicine makes you fat, that’s known as side effect. If you put some bread in a toaster to make toast, but the toaster burns your house down, that’s also a side effect. Not all side effects are bad, though. If you take a business trip to Paris, you also get to see the Eiffel Tower.

When you write a function, you know what the inputs are: the parameters. You also know what the output is: the return value. Anything beyond that, that is, anything that effects the world outside of the function, is a side effect.

Functions in Dart

Have a look at this function:

Functions in Dart

Printing something to the console is a side effect, because it’s affecting the world outside of the function. If you wanted to rewrite your function so that there were no side effects, you could write it like this:

Functions in Dart

Now, there’s nothing inside the function body that affects the outside world. You’ll have to write the string to the console somewhere outside of the function.

It’s fine, and even necessary, for some functions to have side effects. But as a general rule, functions without side effects are easier to deal with and reasons about. You can rely on them to do exactly what you expect because they always return the same output for any given input.
These kinds of function are also called pure functions.

Here is another function with side effects to further illustrate the point:

Functions in Dart

Unless you took the time to study the code inside of anInoocentFunction, you’d have no idea that calling this innocent – function would also change your precious data. That’s because the function had an unknown side effect. This is also a good reminder about the dangers of using global variables like myPreciousData, as you never know who might change it.

Make it your ambition to maximize your use of pure functions, and minimize your use of functions with side effects.

Doing only one thing

Proponents of “clean code” recommend keeping your functions small and logically coherent. Small here means only a handful of lines of code. If a function is too big, or contains unrelated parts, consider breaking it into smaller functions.

Write your functions so that each one has only a single job to do. If you find yourself adding comments to describe sections of a complex function, that’s usually a good clue that you should break your function up into smaller functions.

In clean coding, this is knows as the Single Responsibility Principle. In addition to functions, this principle also applies to classes and libraries. But that’s a topic for article.

Optional types

Earlier you saw this function:

Functions in Dart

The return type is String, and the parameter type is int.
Dart is an optionally- typed language, so it’s possible to omit the types from your function declaration. In that case, the function would look like this:

Functions in Dart

Dart can infer that the return type here is String, but it has to fall back on dynamic for the unknown parameter type. The following function is the equivalent of what Dart sees:

Functions in Dart

While it’s permissible to omit return and parameter types, am recommends that you include them at the very least for situation where Dart can’t infer the type.

Anonymous functions

All the functions you’ve seen previously, such as main, hello, and withinTolerance are named functions, which means, well, they have a name.

Functions in Dart

But not every function needs a name. If you remove the return type and the function name, then what you have left is an anonymous function:

Functions in Dart

The return type will be inferred from the return value of the function body, String in this case.

So, why all the stealth by being anonymous, you ask? Are functions concerned about their online privacy, too? Well, that’s not quite it. Sometimes you only need functions in one specific spot in your code, for one specific reason, and there’s no reason to give that function a name. You’ll see some example of this soon.

First-class citizens

In Dart, functions are first-class citizens. That means you can treat them like any other other type, assigning functions as values to variables and even passing functions around as parameters or returning them from other functions.

Assigning functions to variables

When assigning a value to a variable, functions behave just like other types:

Functions in Dart

As you can see to call the function that the variable multiply refers to, simply use the variable name followed by the arguments in parentheses

Passing functions to functions

Just as you can write a function to take int or String as a parameter, you can also have Function as a parameter

Functions in Dart

Here, namedFunction takes an anonymous function as a parameter.

Returning functions from functions

Just as you can pass in functions as input parameters, you can also return them as output:

Functions in Dart

The return value is an anonymous function of type Function.

Function that return functions, or that accept them as parameters, are called higher order functions.

Returning a function

Have a look at a different example:

Functions in Dart

This one looks a little crazy at first. There are two return statements! To make sense of a function like this, look at it this way: applyMultiplier is a named function that returns an anonymous function. It’s like a machine that makes a machine. That second return statement belongs to the anonymous function and won’t get called when applyMultiper is called.

triple is a constant variable of type Function; that is, the anonymous function that applyMultiplier returned.

Passing arguments to the variable runs the function. Because the parameter type was num, it can accept both int and double inputs. This is the result:

6

Anonymous functions in forEach loops

So if you have a list of numbers, like so, you can call forEach on the list pass in an anonymous function that triples each number in the list and prints out that value.

Functions in Dart

The parameter type of number is inferred from the list element types: in this case, int. Run the code and you’ll see the following result:

3
6
9

Closures and scope

Anonymous functions in Dart act as what are known as closures. The term closure means that the code “close around” the surrounding scope, and therefore has access to variables and functions defined within that scope.

Functions in Dart

A scope in Dart is defined by a pair of curly braces. All the code within these braces is a scope. You can even have nested scopes within other scopes. Examples of scopes are function bodies and the bodies of loops.

The return value of the applyMultiplier function from before is an example of closure.

Functions in Dart

The anonymous function it returns closes over the multiplier value that’s passed in as a parameter to applyMultiplier.
As another example, if you have a variable counter and then define an anonymous function below it, that anonymous function acts like a closure and has access to counter, and so can change it.

Functions in Dart

The anonymous function that defines incrementCounter can access counter, even though counter is not a parameter to the anonymous function, nor is it defined in the function body.

Call incremnetCounter five times and print counter, you’ll see that counter now has a value of 5.

You can use a closure as a function return value, and , as in in this example, count the number of times a given function has been called.

Functions in Dart

Arrow functions

Dart has a special syntax for functions whose body is only one line. Consider the following named function add that adds two numbers together:

Since the body is only one line, you can convert it to the following form, you replaced the function’s braces and body with an arrow (=>) and left off the return keyword. The return value is whatever the value of the expression is. Writing a function in this way is known as arrow syntax or arrow notation.

You can also use arrow syntax with anonymous function. You’re simply left with the parameter list, the arrow, and a single expression:

(parameters) => expression;

Check out more articles related to Dart Tutorial:

Switch Statement in Dart

Control Flow in Dart

String in Dart

Type Inference Variable in Dart | Dart Tutorial #2

Dart Tutorial is for Beginners from Scratch #1

2 thoughts on “Functions in Dart

Leave a Reply

Your email address will not be published. Required fields are marked *

Back To Top