Dart is the language Flutter is written in. If you've used any typed language before (Java, TypeScript, Swift, C#), you'll recognize almost everything here. If not, this is a short tour through the syntax you'll see in every Flutter file.
You don't need to master Dart before writing Flutter. You just need enough of it to stop tripping over syntax.
For the full reference, the Dart docs.
Values
Every variable in Dart has a type. You can write it explicitly, or let Dart infer it with var.
String name = 'Mitch';
int age = 25;
double price = 9.99;
bool isActive = true;
var city = 'Toronto'; // inferred
final id = 12345; // set once
const pi = 3.14159; // compile-timeUse final when a value is assigned once and never changes. Use const when the value is known before the app runs (a literal, a piece of math, a const constructor). Both are safer than var and help Flutter skip unnecessary rebuilds.
Values can't be null unless you explicitly mark them with ?. This prevents an entire class of bugs you'd run into in JavaScript.
String name = 'Mitch'; // cannot be null
String? nickname; // can be null
nickname ?? 'fallback' // fallback if null
nickname?.length // safe access
nickname! // assert (crashes if null)Prefer ?? and ?. over !. The bang operator promises Dart that a value isn't null, but if you're wrong, your app crashes. Reach for it only when you're sure.
Operators
Math, comparison, and logic. Same as most languages, with one Dart-specific quirk: there are two division operators. / always returns a double, and ~/ returns an integer.
// Arithmetic
a + b // add
a - b // subtract
a * b // multiply
a / b // divide (returns double)
a ~/ b // integer divide
a % b // remainder
// Comparison
a == b // equal
a != b // not equal
a > b a >= b a < b a <= b
// Logical
a && b // and
a || b // or
!a // notControl Flow
How your code makes decisions and repeats itself. Nothing surprising if you've written any other language.
if (age >= 18) {
print('Adult');
} else {
print('Minor');
}
// Ternary
String label = age >= 18 ? 'Adult' : 'Minor';
// Switch
switch (status) {
case 'active': print('Active');
case 'inactive': print('Inactive');
default: print('Unknown');
}
// For
for (int i = 0; i < 5; i++) print(i);
// For-in
for (final name in ['Alice', 'Bob']) print(name);
// While
while (count < 3) count++;Two notes. Dart's switch doesn't fall through by default, so you don't need break. And for-in works on any collection, which you'll use constantly once you start loading data from an API.
Functions
Dart functions have one feature most languages don't: named parameters. Wrap them in {}, add required or give a default value, and callers can pass them in any order.
int add(int a, int b) => a + b;
// Arrow syntax for single expressions above.
// Full body below with named params.
void greet(String name, {String? title, int level = 0}) {
print('[$level] ${title ?? ""} $name');
}
greet('Mitch', title: 'Mr.');Named parameters make calls read like English. Flutter leans on them everywhere, so this pattern will feel natural quickly.
Classes
A class groups data (properties) and behavior (methods). Almost everything in Dart is an object, including numbers, strings, and functions.
class User {
final String name;
final int age;
User({required this.name, required this.age});
String greet() => 'Hi, I am $name';
}
final user = User(name: 'Mitch', age: 25);
print(user.greet());Extend a class to build on top of it. The child inherits every property and method, and can override any of them.
class Admin extends User {
final String role;
Admin({required super.name, required super.age, required this.role});
@override
String greet() => 'Hi, I am $name ($role)';
}Collections
Three built-in collection types cover almost every case. Lists are ordered and allow duplicates — the one you'll use most. Maps are key-value pairs. Sets hold unique values only.
// List
List<String> fruits = ['apple', 'banana'];
fruits.add('cherry');
fruits.remove('banana');
String first = fruits[0];
// Map
Map<String, int> scores = {'math': 95};
scores['english'] = 92;
int? math = scores['math'];
// Set
Set<int> ids = {1, 2, 3};
ids.add(2); // ignored, already existsEvery collection supports the same functional operations. If you've used these in JavaScript or Python, they work the same way here.
fruits.where((f) => f.startsWith('a')); // filter
fruits.map((f) => f.toUpperCase()); // transform
fruits.contains('apple'); // check
fruits.length; // countEnums
A fixed set of named values. Great when a variable should be one of a few specific options. You'll see enums everywhere in Flutter: alignment, colors, status types, keyboard types.
enum Status { active, inactive, banned }
Status userStatus = Status.active;
if (userStatus == Status.active) {
print('Active');
}Async
Anything that takes time (a network request, reading a file, a timer) returns a Future. Think of a Future as a value that hasn't arrived yet. Mark the function async and use await to pause until the result is ready.
Future<String> fetchUsername() async {
await Future.delayed(Duration(seconds: 2));
return 'Mitch';
}
void loadUser() async {
try {
final name = await fetchUsername();
print(name);
} catch (e) {
print('Error: $e');
}
}Always wrap anything that can fail in try/catch. Networks go down, files disappear, APIs return errors. Handle the failure case and your app won't crash on users.
That's all the Dart you need to start writing Flutter.