Flutter: Dart Language Crash Course
Dart is the soul of Flutter — mastering Dart is like learning to read sheet music before playing a symphony.
📋 Prerequisites: You need to have completed the following first
1. What You Will Learn
- Variables and type system:
int,double,String,bool; differences betweenvar/final/const - Collection operations:
List<E>,Map<K,V>,Set<E>; collection-if / collection-for - Function features: optional parameters, named parameters, anonymous functions and closures
- Asynchronous programming:
Future<T>,async/await,Stream<T> - Classes and mixins: constructors, factory pattern,
abstract class,mixin
2. A Real Story from a Developer
(1) The Pain: Callback Hell and Type Confusion
Bob is a backend developer transitioning to Flutter. Accustomed to JavaScript's flexibility, he kept running into walls in Dart: confusing var and final, nesting async callbacks 5 levels deep, and getting constant generic collection type errors. During a shopping cart price calculation, an implicit int to double conversion bug turned 99.99 USD into 99 USD — resulting in a flood of user complaints after launch.
(2) Dart's Solution
Dart is a strongly typed language that catches type errors at compile time; async/await makes asynchronous code as clear as synchronous code; final/const prevents accidental mutation.
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
// Simplified class definition (for demonstration)
class Product {
final int id;
final String name;
final double price;
final String? imageUrl;
const Product({required this.id, required this.name, required this.price, this.imageUrl});
factory Product.fromJson(Map<String, dynamic> json) => Product(
id: json['id'] as int,
name: json['name'] as String,
price: (json['price'] as num).toDouble(),
imageUrl: json['image_url'] as String?,
);
Product copyWith({String? name, double? price}) => Product(
id: id, name: name ?? this.name, price: price ?? this.price, imageUrl: imageUrl,
);
static List<Product> fromJsonList(List data) => data.map((j) => Product.fromJson(j as Map<String, dynamic>)).toList();
}
class CartItem {
final Product product;
final double price;
final int quantity;
CartItem({required this.product, required this.price, this.quantity = 1});
}
// Strong typing prevents price bugs
double calculateTotal(List<CartItem> items) {
return items.fold(0.0, (sum, item) => sum + item.price * item.quantity);
}
// async/await eliminates callback hell
Future<List<Product>> fetchProducts() async {
final response = await http.get(Uri.parse('/api/v1/products'));
return Product.fromJsonList(jsonDecode(response.body));
}
> Output: Run locally with Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly across platforms.
(3) The Result: Type Safety + Clear Async
After switching to Dart, type errors are caught at compile time, async code is flattened, and the shopping cart price precision issue never occurred again.
3. Variables and Type System
Dart is a strongly typed language with type inference and Sound Null Safety.
sequenceDiagram
participant Bob
participant ShopAPI
Bob->>ShopAPI: fetchProducts() [async]
ShopAPI-->>Bob: Future<List<Product>>
Bob->>Bob: await parses JSON
Bob->>Bob: Renders product list
> Output: Run locally with Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly across platforms.
(1) Basic Types
| Type | Description | Example |
|---|---|---|
int |
64-bit integer | int quantity = 3; |
double |
64-bit floating point | double price = 99.99; |
String |
UTF-16 string | String name = "ShopApp"; |
bool |
Boolean | bool inStock = true; |
num |
Parent of int + double | num value = 42; |
(2) Variable Declaration Keywords
| Keyword | Mutability | Assignment Timing | Use Case |
|---|---|---|---|
var |
Mutable | Runtime | Local variables with inferable types |
final |
Immutable | Runtime | Runtime constants (e.g., API responses) |
const |
Immutable | Compile time | Compile-time constants (e.g., color values) |
late |
Deferred initialization | Runtime | Must be assigned before use |
▶ Example
> Output: Run locally with Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly across platforms.
: Variable declaration comparison
// var: type inferred, mutable
var productName = 'Flutter T-Shirt';
productName = 'Dart Hoodie'; // OK
// final: runtime constant, immutable
final double totalPrice = 29.99 * 3;
// totalPrice = 100.0; // Error: final variable
// const: compile-time constant
const double discountRate = 0.15;
const String storeName = 'ShopApp';
// late: deferred initialization
late String userToken;
void login() {
userToken = 'eyJhbGciOiJIUzI1NiIs...';
}
> Output: Run locally with Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly across platforms.
4. Collection Operations
(1) List, Map, Set
| Collection | Characteristics | Literal Syntax |
|---|---|---|
List<E> |
Ordered, allows duplicates | [1, 2, 3] |
Map<K,V> |
Key-value pairs | {'key': 'value'} |
Set<E> |
Unordered, unique | {1, 2, 3} |
▶ Example
> Output: Run locally with Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly across platforms.
: Basic collection operations
import 'package:flutter/material.dart';
// Product class definition shown above
// List: ordered collection
List<Product> products = [
Product(id: 1, name: 'Laptop', price: 1299.99),
Product(id: 2, name: 'Phone', price: 899.99),
];
// Map: key-value pairs
Map<String, double> prices = {
'Laptop': 1299.99,
'Phone': 899.99,
'Tablet': 599.99,
};
// Set: unique items
Set<String> categories = {'Electronics', 'Clothing', 'Books'};
// Access and modify
products.add(Product(id: 3, name: 'Tablet', price: 599.99));
prices['Headphones'] = 199.99;
categories.add('Electronics'); // Ignored, already exists
> Output: Run locally with Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly across platforms.
(2) Collection-if / Collection-for
Dart's unique in-collection conditional/loop syntax greatly simplifies UI construction.
▶ Example
> Output: Run locally with Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly across platforms.
: collection-if and collection-for
import 'package:flutter/material.dart';
// Product class definition shown above
// collection-if: conditional items
bool isAdmin = true;
var menuItems = [
'Home',
'Products',
'Cart',
if (isAdmin) 'Admin Panel',
];
// collection-for: expand items
var productNames = ['Laptop', 'Phone', 'Tablet'];
var descriptions = [
for (var name in productNames) '$name - Best Price'
];
// ['Laptop - Best Price', 'Phone - Best Price', 'Tablet - Best Price']
// Combined in Widget list
Widget buildProductList(List<Product> products) {
return Column(
children: [
for (var p in products)
ListTile(title: Text(p.name), trailing: Text('\$${p.price}')),
],
);
}
> Output: Run locally with Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly across platforms.
5. Function Features
(1) Parameter Types
| Type | Syntax | Required | Default Value |
|---|---|---|---|
| Positional | (a, b) |
Yes | None |
| Optional positional | ([a = 0]) |
No | Supported |
| Named | ({required a}) |
Required when required |
Supported |
| Optional named | ({a = 0}) |
No | Supported |
▶ Example
> Output: Run locally with Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly across platforms.
: Function parameter patterns
import 'package:flutter/material.dart';
// Named parameters (recommended for Flutter)
Widget buildPriceTag({
required double price,
String currency = 'USD',
double fontSize = 16.0,
}) {
return Text(
'$currency \$${price.toStringAsFixed(2)}',
style: TextStyle(fontSize: fontSize),
);
}
// Call with named params
buildPriceTag(price: 99.99);
buildPriceTag(price: 49.99, currency: 'EUR', fontSize: 20.0);
> Output: Run locally with Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly across platforms.
(2) Anonymous Functions and Closures
▶ Example
> Output: Run locally with Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly across platforms.
: Closures in Flutter
import 'package:flutter/material.dart';
// Product class definition shown above
// Anonymous function (lambda)
var expensiveItems = products.where((p) => p.price > 500.0).toList();
// Closure: captures surrounding variables
double discountThreshold = 100.0;
var discountedItems = products.map((p) {
// Closure captures discountThreshold
if (p.price > discountThreshold) {
return p.copyWith(price: p.price * 0.9);
}
return p;
}).toList();
// Callback in Flutter widget
ElevatedButton(
onPressed: () {
// Closure captures context variables
addToCart(product);
},
child: const Text('Add to Cart'),
)
> Output: Run locally with Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly across platforms.
6. Asynchronous Programming
(1) Future and async/await
| Concept | Description | Analogy |
|---|---|---|
Future<T> |
Placeholder for an async operation | Restaurant order number |
async |
Marks an async function | Tells the system "I'll wait" |
await |
Waits for a Future to complete | Wait for food before eating |
▶ Example
> Output: Run locally with Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly across platforms.
: async/await network request
import 'dart:convert';
import 'package:http/http.dart' as http;
// Product class definition shown above
Future<List<Product>> fetchProducts({int page = 1}) async {
try {
final response = await http.get(
Uri.parse('https://api.shopapp.com/v1/products?page=$page'),
);
if (response.statusCode == 200) {
final List data = jsonDecode(response.body);
return data.map((json) => Product.fromJson(json)).toList();
} else {
throw Exception('Failed to load products: ${response.statusCode}');
}
} catch (e) {
// Handle network errors
rethrow;
}
}
> Output: Run locally with Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly across platforms.
(2) Stream: Asynchronous Data Flow
▶ Example
> Output: Run locally with Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly across platforms.
: Stream for real-time cart updates
import 'dart:async';
// Simplified class definition (for demonstration)
class Product {
final String name;
final double price;
const Product({required this.name, required this.price});
}
class CartItem {
final Product product;
int quantity;
CartItem({required this.product, this.quantity = 1});
}
// StreamController for cart updates
class CartBloc {
final _cartStream = StreamController<List<CartItem>>.broadcast();
Stream<List<CartItem>> get cartStream => _cartStream.stream;
List<CartItem> _items = [];
void addToCart(Product product) {
_items.add(CartItem(product: product, quantity: 1));
_cartStream.sink.add(_items);
}
void dispose() {
_cartStream.close();
}
}
// Listen to stream
cartBloc.cartStream.listen((items) {
print('Cart updated: ${items.length} items, total: \$${calculateTotal(items)}');
});
> Output: Run locally with Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly across platforms.
7. Classes and Mixins
(1) Classes and Constructors
| Constructor Type | Syntax | Purpose |
|---|---|---|
| Default | ClassName() |
Standard instantiation |
| Named | ClassName.named() |
Multiple initialization methods |
| Factory | factory ClassName() |
Control instance creation (e.g., cache/singleton) |
| Const | const ClassName() |
Compile-time constant objects |
▶ Example
> Output: Run locally with Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly across platforms.
: Product data model
class Product {
final int id;
final String name;
final double price;
final String? imageUrl;
final double rating;
// Main constructor with named params
const Product({
required this.id,
required this.name,
required this.price,
this.imageUrl,
this.rating = 0.0,
});
// Named constructor from JSON
factory Product.fromJson(Map<String, dynamic> json) {
return Product(
id: json['id'] as int,
name: json['name'] as String,
price: (json['price'] as num).toDouble(),
imageUrl: json['image_url'] as String?,
rating: (json['rating'] as num?)?.toDouble() ?? 0.0,
);
}
// Copy with pattern for immutable updates
Product copyWith({String? name, double? price}) {
return Product(
id: id,
name: name ?? this.name,
price: price ?? this.price,
imageUrl: imageUrl,
rating: rating,
);
}
}
> Output: Run locally with Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly across platforms.
(2) Mixin: Code Reuse
▶ Example
> Output: Run locally with Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly across platforms.
: Mixin for shared functionality
// Product class definition shown above
// Mixin: reusable behavior without inheritance
mixin PriceFormatter {
String formatPrice(double price, {String currency = 'USD'}) {
return '$currency \$${price.toStringAsFixed(2)}';
}
String formatDiscount(double original, double discounted) {
double percent = ((original - discounted) / original * 100);
return '${percent.toStringAsFixed(0)}% OFF';
}
}
// Apply mixin to class
class ProductCard with PriceFormatter {
final Product product;
ProductCard(this.product);
String get priceLabel => formatPrice(product.price);
String get discountLabel => formatDiscount(100.0, product.price);
}
// Abstract class: cannot be instantiated
abstract class Repository<T> {
Future<List<T>> getAll();
Future<T> getById(int id);
Future<T> create(T item);
}
> Output: Run locally with Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly across platforms.
8. Complete Example: ShopApp Data Layer
⚙️ Install dependency:
flutter pub add http
import 'dart:convert';
import 'package:http/http.dart' as http;
// Model
class Product {
final int id;
final String name;
final double price;
final String category;
const Product({
required this.id,
required this.name,
required this.price,
required this.category,
});
factory Product.fromJson(Map<String, dynamic> json) => Product(
id: json['id'] as int,
name: json['name'] as String,
price: (json['price'] as num).toDouble(),
category: json['category'] as String,
);
}
// Repository with async/await
class ProductRepository {
final String baseUrl = 'https://api.shopapp.com/v1';
Future<List<Product>> fetchProducts({int page = 1, int limit = 20}) async {
final response = await http.get(
Uri.parse('$baseUrl/products?page=$page&limit=$limit'),
);
if (response.statusCode != 200) {
throw Exception('API error: ${response.statusCode}');
}
final List data = jsonDecode(response.body)['items'] as List;
return data.map((j) => Product.fromJson(j as Map<String, dynamic>)).toList();
}
Future<Product> fetchById(int id) async {
final response = await http.get(Uri.parse('$baseUrl/products/$id'));
return Product.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
}
}
// Usage
void main() async {
final repo = ProductRepository();
final products = await repo.fetchProducts(page: 1);
for (var p in products) {
print('${p.name}: \$${p.price.toStringAsFixed(2)}');
}
}
> Output: Run locally with Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly across platforms.
❓ FAQ
condition ? value1 : value2, plus ?? (null-aware coalescing) and ?. (null-safe access).String cannot be null, String? can be null. The compiler guarantees no null reference errors at runtime.📖 Summary
- Dart is strongly typed with null safety, catching type errors at compile time
var/final/constdistinguish mutability and assignment timing- collection-if/collection-for simplifies UI list construction
async/awaitmakes async code flat and clear- Mixin enables horizontal code reuse; abstract class defines type contracts
- The
copyWithpattern enables updates to immutable objects
📝 Exercises
- Basic (difficulty ⭐): Create a
Userclass with id (int), name (String), email (String?), and implement afromJsonfactory constructor. - Intermediate (difficulty ⭐⭐): Write a simulated network request function using
async/awaitthat returnsFuture<List<Product>>, usingFuture.delayedto simulate a 2-second delay. - Challenge (difficulty ⭐⭐⭐): Implement a
CartManagerclass that usesStreamControllerto broadcast cart changes, supporting add/remove items and clear operations.