DART · STEP 1 · INTRO

Dart · Intro to the language

Dart 3.13 (any 3.0+ works): variables, input, conditionals, loops, lists, maps and functions — each shown first in the JavaScript you know, then in null-safe Dart.

Step 1 of 5shelf© Arthur Bernier Jrrewritten · verified16 code blocks821 words

What you will learn#

  • Creating Variables in a New Language
  • Conditionals in a New Language
  • Looping in a New Language
  • Collections in a New Language
  • Writing Functions in a New Language

Getting Started#

Install the Dart SDK from dart.dev/get-dart (3.13 as of this writing; everything on this page works on any Dart 3). If you already have Flutter installed, you already have Dart — it ships inside the Flutter SDK. Check it from a terminal:

SH
dart --version

To run a single file: dart run hello.dart. To start a real project with a pubspec.yaml: dart create my_first_dart && cd my_first_dart && dart run.

If you'd rather not install anything yet, DartPad runs Dart in the browser — everything below works there except the "Receiving Input" example, because DartPad has no terminal (dart:io).

WHY: Dart is statically typed and null-safe. The compiler knows the type of every variable and whether it can hold null, and it refuses to build code that could blow up on a missing value. You'll get more red squiggles than in JavaScript, and far fewer runtime surprises.

Creating Variables#

How we did it in Javascript

JS
// We create constant variables with the const keywordconst myConstant = 5;// We create re-assignable variables with the let keywordlet myVariable = "Hello World"; // We print variables to the console using console.logconsole.log(myConstant);console.log(myVariable);

How we do this in Dart

DART
void main() {  // const: a compile-time constant, can never change  const myConstant = 5;  // var: the type is inferred (String here) and fixed, but the value can be reassigned  var myVariable = 'Hello World';  myVariable = 'Hello again'; // fine  // myVariable = 42;         // error: an int is not a String  // the same thing with the type written out explicitly  String myVariable2 = 'Goodbye World';  // final: set once at runtime, then read-only (the closest thing to JS const)  final myFinal = myVariable.toUpperCase();  // dynamic opts out of static typing; the type can change (use sparingly)  dynamic anything = false;  anything = 'now a string';   // print() is console.log; $name interpolates into a string  print(myConstant);  print(myVariable);  print('$myVariable2 and $myFinal');  print(anything);}

Every Dart program starts in main(). Semicolons are required, single and double quotes both work (single is the style-guide default), and the naming convention is lowerCamelCase for variables and functions, UpperCamelCase for types.

Receiving Input#

In JavaScript in the browser, input comes from forms or prompt. In Node, reading from the terminal goes through the readline module; the promise-based version reads cleanly with await:

JS
// Node: ask a question in the terminal and wait for the answerimport readline from "node:readline/promises";import { stdin as input, stdout as output } from "node:process"; const rl = readline.createInterface({ input, output });const name = await rl.question("Who are you? ");console.log(`Hey there ${name}!`);rl.close();

How would we do that in Dart?

DART
// dart:io gives us stdin and stdout (terminal only, not DartPad or the web)import 'dart:io'; void main() {  stdout.write('Who are you? ');  // readLineSync() returns String? — null if the input ends before a line arrives.  // ?? supplies a fallback, so `name` is a plain String and safe to use.  final name = stdin.readLineSync() ?? 'stranger';   print('Hey there, $name!');}

String? (with the question mark) means "a String or null". The compiler will not let you use a String? where a String is needed until you handle the null case — ?? for a default, if (name != null) to check, or name! to promise it isn't null (and crash if you're wrong).

Conditionals#

If statements in Javascript

JS
const num = 5; if (num > 3) {  console.log("num is greater than 3");} else if (num > 1) {  console.log("num is greater than 1");} else {  console.log("num is 1 or less");}

If statements in Dart

DART
void main() {  var number = 5;   if (number > 3) {    print('number is greater than 3');  } else if (number > 1) {    print('number is greater than 1');  } else {    print('number is 1 or less');  }}

Same shape as JavaScript, with two differences: the condition must be a real bool (no truthy/falsy — if (number) is a compile error), and there is no === because == never coerces types in Dart. &&, || and ! are the same. Dart 3 also has switch expressions (var size = switch (n) { < 10 => 'small', _ => 'big' };), which you'll meet in the OOP lesson.

While Loops#

In Javascript

JS
let counter = 0; //This will loop 10 timeswhile (counter < 10) {  console.log(counter);  counter += 1;}

In Dart

DART
void main() {  // an int for counting (inferred from the 0)  var counter = 0;   // this will loop 10 times  while (counter < 10) {    print(counter);    counter += 1; // counter++ works too  }}

Dart also has the classic for loop and for-in, which walks any Iterable (lists, sets, map keys…):

DART
void main() {  for (var i = 0; i < 10; i++) {    print(i);  }   for (final letter in ['a', 'b', 'c']) {    print(letter);  }}

10 Minute Exercise#

Write the code to do the following:

  • Loop 10 times starting the counter at 0
  • On each loop if the counter is even print "it's even" (counter.isEven exists, so does %)
  • If odd, print "meow" if the number is divisible by 3
  • Otherwise print nothing

Collections#

In Javascript we have arrays and objects

JS
const myArray = [1, 2, 3]; console.log(myArray); //logging the entire arrayconsole.log(myArray[0]); // logging an individual item const myObject = {  cheese: "gouda",  bread: "rye",}; console.log(myObject); //logging the entire objectconsole.log(myObject.cheese); // logging a property using dot notationconsole.log(myObject["bread"]); // logging a property using square bracket notation

In Dart we have lists and maps

DART
void main() {  // a List<int> — Dart infers the element type from the contents  var myList = [1, 2, 3];   print(myList); // the entire list  print(myList[0]); // an individual item  print(myList.length); // 3  myList.add(4); // lists grow like JS arrays  myList.remove(1); // removes the first 1, by value   // a Map<String, String>. Keys are quoted (they're values, not identifiers).  const myMap = {    'cheese': 'gouda',    'bread': 'rye',  };   print(myMap); // the entire map  print(myMap['cheese']); // one value — its type is String?, because the key might be missing  print(myMap['bread']);  print(myMap['jam'] ?? 'none'); // a missing key gives null; ?? supplies a default}

There is no dot access for map keys (myMap.cheese is an error) — square brackets only. const in front of a list or map makes it deeply immutable (myMap['jam'] = 'x' throws); var/final lists can be changed. Dart also has Set ({1, 2, 3}, unique values) and records ((1, 'a'), fixed-size tuples) in the standard library.

10 Minute Exercise#

Look up Dart's List methods in the collections tour and the List API and apply them to the following list.

DART
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  • How to find a list's length (.length)
  • How to loop over a list (for (final item in list), .forEach, .map)
  • How to add and remove items from a list (add, insert, removeAt, remove, removeLast)

Functions#

in Javascript

JS
const addNums = (x, y) => {  return x + y;}; console.log(addNums(5, 5));

In Dart

DART
// return type, then name, then typed parametersint addNums(int x, int y) {  return x + y;} // the same thing with arrow syntax (single-expression bodies only)int addNumsShort(int x, int y) => x + y; void main() {  print(addNums(5, 5));  print(addNumsShort(5, 5));   // anonymous functions look like JS arrows and can be stored in variables  final twice = (int x) => x * 2;  print(twice(21));}

Two things JavaScript doesn't give you: typed parameters and named parameters. Named parameters go in {}; they're optional unless marked required, and can have defaults:

DART
String greet({required String name, String greeting = 'Hello'}) {  return '$greeting, $name!';} void main() {  print(greet(name: 'Arthur')); // Hello, Arthur!  print(greet(name: 'Arthur', greeting: 'Welcome')); // Welcome, Arthur!}

An optional named parameter without a default must be nullable (String? nickname), because its value is null when the caller leaves it out — null safety again.

10 minute exercise#

Write the following functions

  • subNums that takes two arguments and returns their difference
  • sayHello that takes a name as an argument and says hello to that name
  • sayHelloAdv that takes a map with a name and age key and prints "hello {name}, how does it feel to be {age} years old"
  • looper takes one list as an argument, loops over the list and prints each item individually

Resources to Learn More#


See also