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


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.

DART
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));
}
TEXT
> 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.

100%
sequenceDiagram
    participant Bob
    participant ShopAPI
    Bob->>ShopAPI: fetchProducts() [async]
    ShopAPI-->>Bob: Future<List<Product>>
    Bob->>Bob: await parses JSON
    Bob->>Bob: Renders product list
TEXT
> 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

TEXT
> 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

DART
// 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...';
}
TEXT
> 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

TEXT
> 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

DART
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
TEXT
> 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

TEXT
> 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

DART
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}')),
    ],
  );
}
TEXT
> 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

TEXT
> 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

DART
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);
TEXT
> 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

TEXT
> 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

DART
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'),
)
TEXT
> 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

TEXT
> 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

DART
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;
  }
}
TEXT
> 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

TEXT
> 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

DART
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)}');
});
TEXT
> 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

TEXT
> 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

DART
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,
    );
  }
}
TEXT
> 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

TEXT
> 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

DART
// 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);
}
TEXT
> 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

DART
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)}');
  }
}
TEXT
> 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

Q What's the real difference between final and const?
A final is assigned once at runtime and cannot be changed; const is determined at compile time and must be a literal or const constructor.
Q Does Dart have a ternary operator?
A Yes, condition ? value1 : value2, plus ?? (null-aware coalescing) and ?. (null-safe access).
Q Must an async function return a Future?
A No, but it's recommended. An async function executes asynchronously even if it returns void.
Q What's the difference between Stream and Future?
A Future is a one-time async result; Stream is a continuously producing async data sequence, like real-time stock prices.
Q How to choose between mixin and abstract class?
A Use mixin for adding behavior (e.g., logging, formatting) without an is-a relationship; use abstract class to define a type contract requiring an is-a relationship.
Q Does Dart support null safety?
A Dart 2.12+ enforces Sound Null Safety. String cannot be null, String? can be null. The compiler guarantees no null reference errors at runtime.

📖 Summary


📝 Exercises

  1. Basic (difficulty ⭐): Create a User class with id (int), name (String), email (String?), and implement a fromJson factory constructor.
  2. Intermediate (difficulty ⭐⭐): Write a simulated network request function using async/await that returns Future<List<Product>>, using Future.delayed to simulate a 2-second delay.
  3. Challenge (difficulty ⭐⭐⭐): Implement a CartManager class that uses StreamController to broadcast cart changes, supporting add/remove items and clear operations.

← Previous | Next →

Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏