Getting started
Introduction
Dart is a client-optimized language for fast apps on any platform. Primary language for Flutter development with sound null safety.
Installation
# macOS via Homebrew
brew install dart
# Windows via Chocolatey
choco install dart-sdk
# Linux via apt
sudo apt update
sudo apt install dart
Hello World
void main() {
print('Hello, World!');
}
Variables
// Type inference
var name = 'Alice';
var age = 30;
// Explicit types
String city = 'New York';
int count = 42;
double price = 9.99;
bool isActive = true;
// Final (runtime constant)
final currentDate = DateTime.now();
// Const (compile-time constant)
const pi = 3.14159;
Null Safety
Nullable Types
// Non-nullable (default)
String name = 'Alice';
// name = null; // Error!
// Nullable with ?
String? nickname;
nickname = null; // OK
int? age;
age = 30; // OK
Null-Aware Operators
// ?. (null-aware access)
String? name;
int? length = name?.length;
// ?? (null-coalescing)
String msg = name ?? 'Guest';
// ??= (null-aware assignment)
String? value;
value ??= 'default';
// ! (null assertion)
String? text = 'Hello';
int length = text!.length; // Assert non-null
Late Variables
// Late initialization
late String description;
void init() {
description = 'Ready';
}
// Late final
late final String config;
void setup() {
config = loadConfig();
}
Functions
Function Syntax
// Named function
int add(int a, int b) {
return a + b;
}
// Arrow function
int multiply(int a, int b) => a * b;
// Anonymous function
var divide = (int a, int b) {
return a / b;
};
Optional Parameters
// Named parameters
void greet({String? name, int age = 0}) {
print('Hello $name, age $age');
}
greet(name: 'Alice', age: 30);
greet(); // OK
// Required named parameters
void register({required String email}) {
print('Email: $email');
}
register(email: 'user@example.com');
Positional Parameters
// Optional positional
String say(String msg, [String? from]) {
if (from != null) {
return '$msg from $from';
}
return msg;
}
say('Hello'); // OK
say('Hello', 'Alice'); // OK
Async/Await
Future Basics
// Async function
Future<String> fetchData() async {
await Future.delayed(Duration(seconds: 1));
return 'Data loaded';
}
// Using await
void loadData() async {
String data = await fetchData();
print(data);
}
Error Handling
// Try-catch with async
Future<void> getData() async {
try {
var result = await fetchData();
print(result);
} catch (e) {
print('Error: $e');
} finally {
print('Cleanup');
}
}
// Multiple awaits
Future<void> loadAll() async {
var user = await fetchUser();
var posts = await fetchPosts(user.id);
print(posts);
}
Parallel Execution
// Wait for multiple futures
Future<void> loadParallel() async {
var results = await Future.wait([
fetchUser(),
fetchPosts(),
fetchComments(),
]);
print(results);
}
// First to complete
var result = await Future.any([
fetchFromServer1(),
fetchFromServer2(),
]);
Streams
Stream Basics
// Create stream
Stream<int> countStream() async* {
for (int i = 1; i <= 5; i++) {
await Future.delayed(Duration(seconds: 1));
yield i;
}
}
// Listen to stream
await for (var value in countStream()) {
print(value);
}
Stream Methods
// Transform stream
var doubled = stream.map((x) => x * 2);
// Filter stream
var evens = stream.where((x) => x % 2 == 0);
// First value
var first = await stream.first;
// Listen with callback
stream.listen(
(data) => print(data),
onError: (e) => print('Error: $e'),
onDone: () => print('Done'),
);
StreamController
// Create controller
var controller = StreamController<String>();
// Add data
controller.sink.add('Hello');
controller.sink.add('World');
// Listen
controller.stream.listen(print);
// Close when done
controller.close();
Collections
Lists
// List literal
var numbers = [1, 2, 3, 4, 5];
// Typed list
List<String> names = ['Alice', 'Bob'];
// List operations
names.add('Charlie');
names.remove('Bob');
var first = names[0];
var length = names.length;
// Spread operator
var all = [...names, 'Dave'];
// If/for in collections
var items = [
'Home',
if (loggedIn) 'Logout',
for (var i in numbers) 'Item $i',
];
Sets
// Set literal
var tags = {'flutter', 'dart', 'mobile'};
// Typed set
Set<int> ids = {1, 2, 3};
// Set operations
tags.add('async');
tags.remove('mobile');
var contains = tags.contains('dart');
// Set methods
var union = tags.union({'web'});
var intersection = tags.intersection({'dart'});
Maps
// Map literal
var user = {
'name': 'Alice',
'age': 30,
'active': true,
};
// Typed map
Map<String, int> scores = {
'alice': 95,
'bob': 87,
};
// Map operations
scores['charlie'] = 92;
var aliceScore = scores['alice'];
scores.remove('bob');
// Iteration
scores.forEach((key, value) {
print('$key: $value');
});
Classes
Basic Class
class Person {
String name;
int age;
// Constructor
Person(this.name, this.age);
// Named constructor
Person.guest() : name = 'Guest', age = 0;
// Method
void introduce() {
print('I am $name, $age years old');
}
}
// Usage
var person = Person('Alice', 30);
var guest = Person.guest();
Getters and Setters
class Rectangle {
double width;
double height;
Rectangle(this.width, this.height);
// Getter
double get area => width * height;
// Setter
set dimensions(List<double> dims) {
width = dims[0];
height = dims[1];
}
}
var rect = Rectangle(10, 20);
print(rect.area); // 200
rect.dimensions = [15, 25];
Inheritance
class Animal {
String name;
Animal(this.name);
void makeSound() {
print('Some sound');
}
}
class Dog extends Animal {
Dog(String name) : super(name);
void makeSound() {
print('Woof!');
}
}
var dog = Dog('Rex');
dog.makeSound(); // Woof!
Mixins
Mixin Definition
// Define mixin
mixin Flyable {
void fly() {
print('Flying!');
}
}
mixin Swimmable {
void swim() {
print('Swimming!');
}
}
// Use mixins
class Duck with Flyable, Swimmable {
void quack() {
print('Quack!');
}
}
var duck = Duck();
duck.fly();
duck.swim();
duck.quack();
Mixin Constraints
class Animal {
void breathe() => print('Breathing');
}
// Mixin requires Animal
mixin Walker on Animal {
void walk() {
breathe();
print('Walking');
}
}
class Dog extends Animal with Walker {}
Extensions
Extension Methods
// Extend existing class
extension StringExtension on String {
String get capitalized {
if (isEmpty) return this;
return '${this[0].toUpperCase()}${substring(1)}';
}
bool get isEmail {
return contains('@') && contains('.');
}
}
// Usage
var name = 'alice';
print(name.capitalized); // Alice
print('test@example.com'.isEmail); // true
Extension Types
// Extension on int
extension NumberExtension on int {
int get doubled => this * 2;
bool get isEven => this % 2 == 0;
String times(String str) {
return str * this;
}
}
print(5.doubled); // 10
print(4.isEven); // true
print(3.times('Ha')); // HaHaHa
Isolates
Basic Isolate
import 'dart:isolate';
// Function to run in isolate
void heavyTask(SendPort sendPort) {
var result = 0;
for (int i = 0; i < 1000000; i++) {
result += i;
}
sendPort.send(result);
}
// Spawn isolate
void main() async {
var receivePort = ReceivePort();
await Isolate.spawn(
heavyTask,
receivePort.sendPort,
);
var result = await receivePort.first;
print('Result: $result');
}
Isolate Communication
import 'dart:isolate';
void isolateFunction(SendPort sendPort) {
var receivePort = ReceivePort();
sendPort.send(receivePort.sendPort);
receivePort.listen((message) {
if (message == 'ping') {
sendPort.send('pong');
}
});
}
void main() async {
var receivePort = ReceivePort();
await Isolate.spawn(isolateFunction, receivePort.sendPort);
SendPort sendPort = await receivePort.first;
sendPort.send('ping');
var response = await receivePort.first;
print(response); // pong
}
Error Handling
Try-Catch
// Basic try-catch
try {
var result = divide(10, 0);
print(result);
} catch (e) {
print('Error: $e');
}
// Catch specific types
try {
var data = parseData();
} on FormatException catch (e) {
print('Format error: $e');
} on Exception catch (e) {
print('Exception: $e');
} catch (e) {
print('Unknown error: $e');
}
Finally Block
// Finally always executes
void processFile() {
var file;
try {
file = openFile();
readFile(file);
} catch (e) {
print('Error: $e');
} finally {
file?.close();
}
}
Custom Exceptions
// Define exception
class NetworkException implements Exception {
final String message;
NetworkException(this.message);
String toString() => 'NetworkException: $message';
}
// Throw exception
void fetchData() {
throw NetworkException('Connection failed');
}
// Catch custom exception
try {
fetchData();
} on NetworkException catch (e) {
print(e);
}
Generics
Generic Classes
// Generic class
class Box<T> {
T value;
Box(this.value);
T getValue() => value;
}
// Usage
var intBox = Box<int>(42);
var stringBox = Box<String>('Hello');
print(intBox.getValue()); // 42
print(stringBox.getValue()); // Hello
Generic Methods
// Generic function
T getFirst<T>(List<T> items) {
return items[0];
}
// Usage
var first = getFirst<int>([1, 2, 3]); // 1
var name = getFirst(['Alice', 'Bob']); // Alice
// Type constraint
T combine<T extends num>(T a, T b) {
return (a + b) as T;
}
Generic Collections
// List of strings
List<String> names = ['Alice', 'Bob'];
// Map with typed keys/values
Map<String, int> ages = {
'Alice': 30,
'Bob': 25,
};
// Set of ints
Set<int> numbers = {1, 2, 3};
Dart CLI Commands
Running Code
# Run Dart file
dart run main.dart
# Run with arguments
dart run app.dart arg1 arg2
# Compile to native
dart compile exe main.dart
# Compile to JavaScript
dart compile js main.dart
# Run in AOT mode
dart compile aot-snapshot main.dart
Testing
# Run all tests
dart test
# Run specific test file
dart test test/widget_test.dart
# Run tests with coverage
dart test --coverage=coverage
# Generate coverage report
dart run coverage:format_coverage \
--lcov \
--in=coverage \
--out=coverage/lcov.info
Analysis
# Analyze code
dart analyze
# Fix issues automatically
dart fix --apply
# Format code
dart format .
# Format and overwrite
dart format -o write .
# Check formatting
dart format --set-exit-if-changed .
Pub Package Manager
Package Commands
# Get dependencies
dart pub get
# Add package
dart pub add http
# Add dev dependency
dart pub add --dev test
# Remove package
dart pub remove http
# Upgrade packages
dart pub upgrade
# Outdated packages
dart pub outdated
Publishing
# Check package before publish
dart pub publish --dry-run
# Publish package
dart pub publish
# Get package info
dart pub deps
# Show dependency tree
dart pub deps --style=compact
pubspec.yaml
name: my_app
description: A sample app
version: 1.0.0
environment:
sdk: ">=3.0.0 <4.0.0"
dependencies:
http: ^1.0.0
collection: ^1.17.0
dev_dependencies:
test: ^1.24.0
lints: ^3.0.0
String Operations
String Basics
// String literals
var single = 'Single quotes';
var double = "Double quotes";
var multiline = '''
Multiple
lines
''';
// String interpolation
var name = 'Alice';
var age = 30;
var msg = 'Name: $name, Age: $age';
var expr = 'Next year: ${age + 1}';
String Methods
var text = 'Hello, World!';
// Properties
text.length; // 13
text.isEmpty; // false
text.isNotEmpty; // true
// Methods
text.toUpperCase(); // HELLO, WORLD!
text.toLowerCase(); // hello, world!
text.contains('World'); // true
text.startsWith('Hello'); // true
text.endsWith('!'); // true
text.substring(0, 5); // Hello
text.split(','); // ['Hello', ' World!']
text.replaceAll('World', 'Dart'); // Hello, Dart!
text.trim(); // Remove whitespace
String Building
// StringBuffer for efficient concatenation
var buffer = StringBuffer();
buffer.write('Hello');
buffer.write(' ');
buffer.write('World');
var result = buffer.toString(); // Hello World
// Join list
var words = ['Dart', 'is', 'awesome'];
var sentence = words.join(' '); // Dart is awesome
Pattern Matching
Switch Expressions
// Switch expression (Dart 3.0+)
String getMessage(int code) {
return switch (code) {
200 => 'OK',
404 => 'Not Found',
500 => 'Server Error',
_ => 'Unknown',
};
}
// With patterns
String describe(Object obj) {
return switch (obj) {
int() => 'Integer',
String() => 'String',
List() => 'List',
_ => 'Other',
};
}
If-Case
// If-case pattern matching
void checkValue(Object value) {
if (value case int n when n > 0) {
print('Positive integer: $n');
} else if (value case String s) {
print('String: $s');
} else {
print('Other type');
}
}
Destructuring
// List patterns
var [a, b, c] = [1, 2, 3];
// Map patterns
var {'name': name, 'age': age} = {
'name': 'Alice',
'age': 30,
};
// Record patterns
var (x, y) = (10, 20);
// Rest pattern
var [first, ...rest] = [1, 2, 3, 4];
// first = 1, rest = [2, 3, 4]
Records (Dart 3.0+)
Record Basics
// Record literal
var record = (1, 2);
var named = (x: 10, y: 20);
// Access fields
print(record.$1); // 1
print(record.$2); // 2
print(named.x); // 10
print(named.y); // 20
// Mixed record
var mixed = (1, x: 'Hello');
print(mixed.$1); // 1
print(mixed.x); // Hello
Record Types
// Function returning record
(int, String) getUserInfo() {
return (42, 'Alice');
}
var (id, name) = getUserInfo();
// Named record type
({int age, String name}) getUser() {
return (age: 30, name: 'Bob');
}
var user = getUser();
print(user.name); // Bob
Record Patterns
// Destructuring
var (a, b) = (1, 2);
// In switch
switch (point) {
case (0, 0):
print('Origin');
case (var x, 0):
print('On x-axis: $x');
case (0, var y):
print('On y-axis: $y');
case (var x, var y):
print('Point: ($x, $y)');
}
Practical Examples
HTTP Request
import 'dart:convert';
import 'package:http/http.dart' as http;
Future<Map<String, dynamic>> fetchUser(int id) async {
final url = Uri.parse('https://api.example.com/users/$id');
try {
final response = await http.get(url);
if (response.statusCode == 200) {
return jsonDecode(response.body);
} else {
throw Exception('Failed to load user');
}
} catch (e) {
print('Error: $e');
rethrow;
}
}
// Usage
void main() async {
var user = await fetchUser(1);
print('Name: ${user['name']}');
}
JSON Serialization
import 'dart:convert';
class User {
final String name;
final int age;
User(this.name, this.age);
// From JSON
factory User.fromJson(Map<String, dynamic> json) {
return User(
json['name'] as String,
json['age'] as int,
);
}
// To JSON
Map<String, dynamic> toJson() {
return {
'name': name,
'age': age,
};
}
}
// Usage
var json = '{"name": "Alice", "age": 30}';
var user = User.fromJson(jsonDecode(json));
var encoded = jsonEncode(user.toJson());
print(encoded); // {"name":"Alice","age":30}
File Operations
import 'dart:io';
// Read file
Future<String> readFile(String path) async {
final file = File(path);
return await file.readAsString();
}
// Write file
Future<void> writeFile(String path, String content) async {
final file = File(path);
await file.writeAsString(content);
}
// Check if exists
Future<bool> fileExists(String path) async {
final file = File(path);
return await file.exists();
}
// List directory
Future<void> listFiles(String path) async {
final dir = Directory(path);
await for (var entity in dir.list()) {
print(entity.path);
}
}
Stream Processing
import 'dart:async';
// Create stream from events
Stream<int> countStream(int max) async* {
for (int i = 1; i <= max; i++) {
await Future.delayed(Duration(seconds: 1));
yield i;
}
}
// Transform and filter
Future<void> processStream() async {
var stream = countStream(10)
.where((n) => n % 2 == 0) // Even numbers only
.map((n) => n * 2) // Double them
.take(3); // First 3 results
await for (var value in stream) {
print(value); // 4, 8, 12
}
}
// Reduce stream
Future<int> sumStream(Stream<int> stream) async {
return await stream.reduce((a, b) => a + b);
}
Gotchas
Null Safety
- Use
?for nullable types, not just optional parameters - Null assertion
!crashes if null - use carefully - Late variables must be initialized before access
- Cannot assign null to non-nullable types
Async/Await
- Forgetting
asynckeyword preventsawaitusage - Unhandled async errors crash the app - always use try-catch
awaitonly works inasyncfunctions- Don't use
awaitin loops unless sequential processing needed
Collections
- Lists are zero-indexed (first element is
list[0]) - Maps return
nullfor missing keys (use??operator) - Sets don't allow duplicates - adding same value ignored
- Collection literals create mutable collections
Type System
varinfers type from assignment - cannot reassign different typedynamicdisables type checking - use sparingly- Generic types are erased at runtime (no
is List<String>) - Integer division uses
~/operator (5 ~/ 2 = 2)
Also see
- Dart Official Documentation - Complete language guide
- Dart Language Tour - Comprehensive syntax guide
- Effective Dart - Best practices and style guide
- Dart API Reference - Core library documentation
- Flutter Documentation - Flutter framework (uses Dart)
- Pub.dev - Official Dart package repository
- DartPad - Online Dart editor and playground
- Dart Null Safety - Sound null safety guide