Functions in Dart

By Syncfusion®, Inc. · Last updated

How to write and call functions, return values, positional, optional and named parameters, arrow and anonymous functions, closures, recursion and scope.

What Is a Function?

Function = a named block of code that does one job. You can run it again and again by its name.

For example:

  • greet() prints a welcome message
  • addGst(price) works out a price with tax
  • isPass(marks) answers true or false

You have already used functions: main() and print().

Simple lesson: a function is a named piece of work you can reuse.

A Real-World Example

Think of a pressure cooker.

  • You put in rice and water (the inputs).
  • It does the same job every time: cook.
  • It gives back cooked rice (the output).
  • You do not need to know what happens inside. You just use it.

A function is that machine: inputs go in, it does its job, and an answer comes out.

Every Run box on this page runs on the course's Dart runner in your browser.

Some boxes fail on purpose, to show a real error.

What You Will Learn

In this lesson, you will learn:

  1. How to define and call a function, and get a value back with return.
  2. Positional, optional and named parameters, and default values.
  3. Anonymous functions and arrow functions.
  4. Higher-order functions, callbacks and closures.
  5. Recursion and scope.

Function Fundamentals

Why Functions Are Used

  • Reuse: write the code once, call it many times.
  • Fewer mistakes: fix a bug in one place, and every call is fixed.
  • Easy to read: printReportCard() says what it does.
  • Small pieces: a big program becomes many small, simple functions.

Defining a Function

returnType functionName(parameters) {
  // body
}
  • Return type = the type of value it gives back. void = nothing.
  • Function name = lowerCamelCase, usually a verb: printHeader, addMarks.
  • Parameters = the inputs, inside ( ).
  • Body = the code, inside { }.

Calling a Function

Call = run a function by writing its name and ( ).

Define once, call many times

void printLine() {
  print('------------------------');
}

void main() {
  printLine();
  print('GREEN VALLEY SCHOOL');
  printLine();
  print('Report Card 2026');
  printLine();
}

printLine was written once, and called three times.

Simple lesson: define a function once, then call it by name whenever you need it.

Return Values

return

return = send a value back to the caller, and stop the function.

Return value = the value a function gives back.

Call, run, return

Parameter = the name in the function's definition, like a.

Argument = the actual value you pass in a call, like 78.

Returning Different Types

Functions that answer questions

bool isPass(int marks) {
  return marks >= 35;
}

String grade(int marks) {
  if (marks >= 90) return 'A';
  if (marks >= 75) return 'B';
  return 'C';
}

void main() {
  print(isPass(42));
  print(isPass(20));
  print(grade(81));
}

return ends the function straight away. Code after it in that path does not run.

void Functions

void = the function gives nothing back. It just does something, like printing.

A void function does a job

void welcome(String name) {
  print('Welcome to Green Valley, $name!');
}

void main() {
  welcome('Kavya');
  welcome('Imran');
}

Simple lesson: return hands a value back, and void means the function only does a job.

Function Parameters

Required Positional Parameters

Positional parameter = an input matched by its position in the call.

  • The first argument goes to the first parameter, and so on.
  • All of them must be given, in the right order.

Order matters

void showMarks(String name, int marks) {
  print('$name scored $marks.');
}

void main() {
  showMarks('Asha', 88);
  showMarks('Ravi', 74);
}

Optional Positional Parameters

Optional positional parameter = an input in [ ] that the caller may leave out.

An optional section

void showClass(String name, [String? section]) {
  print('$name, section: ${section ?? 'not given'}');
}

void main() {
  showClass('Asha', '8B');
  showClass('Ravi');
}
  • Optional parameters go last, inside [ ].
  • If one is left out, it is null, so its type needs ? (see Null Safety).

Named Parameters

Named parameter = an input in { } that the caller passes by name.

  • In the call, write name: value.
  • The order does not matter.
  • Named parameters are optional, unless marked required.

Named parameters

void addStudent({required String name, required int rollNumber, String? house}) {
  print('$rollNumber: $name, house: ${house ?? 'none'}');
}

void main() {
  addStudent(name: 'Meena', rollNumber: 12, house: 'Tagore');
  addStudent(rollNumber: 13, name: 'Kiran');
}

Named parameters make calls easy to read. addStudent(name: 'Meena', rollNumber: 12) explains itself.

Default Values

Default value = the value a parameter uses when the caller leaves it out.

Default values

void orderLunch(String item, {int quantity = 1, bool extraSpicy = false}) {
  print('$quantity x $item, extra spicy: $extraSpicy');
}

void main() {
  orderLunch('Samosa');
  orderLunch('Dosa', quantity: 2);
  orderLunch('Pav Bhaji', extraSpicy: true, quantity: 3);
}

Compare the kinds of parameters

Simple lesson: positional inputs go by order, named inputs go by name, and defaults fill the gaps.

Anonymous Functions

Anonymous function = a function with no name. Also called a function expression.

(parameters) {
  // body
}

You can store it in a variable, or pass it straight to another function.

A function without a name

void main() {
  var shout = (String text) {
    return text.toUpperCase() + '!';
  };
  print(shout('jai hind'));

  var marks = [78, 92, 65];
  marks.forEach((m) {
    print('Mark: $m');
  });
}

Simple lesson: an anonymous function is a function you use right where you write it.

Arrow Functions

Arrow syntax (=>) = a short way to write a function whose body is one expression.

=> expr means { return expr; }.

Arrow functions

int square(int n) => n * n;
bool isEven(int n) => n % 2 == 0;
String greet(String name) => 'Hello, $name!';

void main() {
  print(square(9));
  print(isEven(14));
  print(greet('Zoya'));

  var twice = (int n) => n * 2;
  print(twice(21));
}

Single-expression function = a function that only works out one value. Arrows are made for them.

  • Use => for one expression.
  • Use { } when you need several statements.

Dart's own code style uses => for every function whose body is just one return.

You will see it everywhere in Flutter code, so it is worth getting used to now.

Simple lesson: => value is short for { return value; }.

Higher-Order Functions

Higher-order function = a function that takes another function as an input, or gives one back.

Functions as Values

In Dart, a function is a value, like a number. You can store it in a variable.

Functions as Parameters and Callbacks

Callback = a function you pass to another function, so it can call it later.

Passing a function as an input

int addGrace(int m) => m + 5;
int doubleIt(int m) => m * 2;

void changeAll(List<int> marks, int Function(int) rule) {
  for (var m in marks) {
    print(rule(m));
  }
}

void main() {
  var marks = [30, 50];
  changeAll(marks, addGrace);
  changeAll(marks, doubleIt);
  changeAll(marks, (m) => m - 1);
}
  • int Function(int) = the type "a function that takes an int and gives an int".
  • changeAll does not know the rule. The caller decides it.
  • You will use this a lot in Working with Iterables, with map and where.

Simple lesson: a higher-order function takes a function as an input, so the caller can choose the behaviour.

Closures

Closure = a function that remembers the variables around it, even after that code has finished.

Lexical scope = a function can use the variables of the place where it was written.

A counter that remembers

Function makeTokenCounter() {
  var token = 0;
  return () {
    token++;
    return 'Token $token';
  };
}

void main() {
  var nextToken = makeTokenCounter();
  print(nextToken());
  print(nextToken());
  print(nextToken());
}
  • makeTokenCounter finished long ago.
  • But the returned function still remembers token, and keeps counting.

Simple lesson: a closure carries its own variables with it, like a backpack.

Recursion

Recursive function = a function that calls itself.

Every recursive function needs two parts:

  • Base case = when to stop. No more calls.
  • Recursive case = call itself with a smaller problem.

Factorial Example

Factorial = 5! = 5 × 4 × 3 × 2 × 1 = 120.

Notice: 5! = 5 × 4!, and 4! = 4 × 3!, and so on, until 1! = 1.

Factorial with recursion

int factorial(int n) {
  if (n <= 1) {
    return 1;
  }
  return n * factorial(n - 1);
}

void main() {
  print(factorial(5));
  print(factorial(3));
}
  • Base case: n <= 1 returns 1.
  • Recursive case: n * factorial(n - 1). Each call has a smaller n.

Without a base case, the function calls itself forever, and the program crashes with a Stack Overflow.

Simple lesson: recursion solves a problem by solving a smaller copy of it, until it reaches a base case.

Function Scope

Scope = the part of the code where a variable can be used.

  • Local variable = made inside a function. It exists only inside it.
  • Global variable = made outside every function, at the top level. Every function can use it.

Scope Rules

  • Code inside a block can see variables from outside it.
  • Code outside a block cannot see variables made inside it.
  • A local variable disappears when its function ends.

Global and local variables

var school = 'Green Valley';

void showTotal() {
  var total = 92 + 88;
  print('$school: total $total');
}

void main() {
  showTotal();
  print('School: $school');
}

total lives only inside showTotal. Try to use it outside:

A local variable cannot be seen outside

void showTotal() {
  var total = 92 + 88;
}

void main() {
  print(total);
}

Dart says: Undefined name 'total'.

Easy way to remember

  • Parameters = what goes in. return = what comes out.
  • => = a one-line function.
  • Local variables stay inside their function.
  • Prefer local variables. Global ones can be changed from anywhere, which causes bugs.

Simple lesson: a variable lives inside the { } where it was made, and inner code can see outward.

TL;DR

  1. Function = a named, reusable block. Call it with name().

    • return gives a value back. void gives nothing.
  2. Parameters:

    • Positional = by order. Optional [ ] = may be left out.
    • Named { } = by name, optional unless required. Defaults fill gaps.
  3. Anonymous function = no name. Arrow => = one-expression body.
  4. Higher-order function = takes or returns a function. Callback = a function passed in.

    • Closure = a function that remembers its surrounding variables.
  5. Recursion = a function calls itself, with a base case to stop.

    • Scope = local inside a function, global at the top level.

Common mistakes

MistakeWrong ideaCorrect ideaWhat goes wrong
Forgetting returnThe last value is returnedWrite return value;The function gives back null, or an error.
Using a void resultvoid functions give a valueThey give nothingThis expression has a type of 'void'.
Arguments in the wrong orderDart matches by namePositional arguments go by orderThe wrong values in the wrong places.
Leaving out a required named argumentAll named ones are optionalrequired ones must be givenRequired named parameter must be provided.
No base caseRecursion stops by itselfEvery recursion needs a base caseStack Overflow.
Using a local variable outsideVariables are visible everywhereLocals live inside their functionUndefined name.
=> with several statementsArrows can hold a block=> takes one expressionA syntax error.

Check Yourself

Exercises

What to Remember

  • A function is a named, reusable block. return gives a value back, and void gives nothing.
  • Positional inputs go by order, [ ] makes them optional, and { } makes them named.
  • => is a one-expression function, and an anonymous function has no name.
  • Functions can be passed to other functions, and closures remember their variables.
  • Recursion needs a base case. A local variable lives only inside its function.

Next: Lists. You will learn to store many values in one variable, in order.