Section 01

Installing Dart

Set up Dart SDK on your system and write your first program.

What is Dart SDK?

The Dart SDK includes everything you need: the Dart VM, the dart2js compiler, core libraries, and package manager (pub). You can install Dart standalone or get it bundled with Flutter.

Installation Methods

Option 1: Install with Flutter (Recommended)

If you're learning Dart for Flutter, installing Flutter automatically includes Dart SDK.

1

Download Flutter SDK

Visit https://flutter.dev/docs/get-started/install and download for your OS.

2

Extract & Add to PATH

Extract the zip and add flutter/bin to your system PATH.

Windows (PowerShell)
## Add to Environment Variables or run:
$env:PATH += ";C:\flutter\bin"
dart --version
flutter doctor
macOS / Linux
# Add to ~/.bashrc or ~/.zshrc
export PATH="$HOME/flutter/bin:$PATH"
source ~/.zshrc
dart --version

Option 2: Standalone Dart SDK

Windows / macOS / Linux
# Windows (Chocolatey)
choco install dart-sdk

# macOS (Homebrew)
brew tap dart-lang/dart
brew install dart

# Linux (apt)
sudo apt update && sudo apt install apt-transport-https
sudo sh -c 'wget -qO- https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -'
sudo sh -c 'wget -qO- https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list > /etc/apt/sources.list.d/dart_stable.list'
sudo apt update && sudo apt install dart

IDE Setup

💙

VS Code (Recommended)

Install the Dart extension from marketplace. Provides IntelliSense, debugging, formatting.

🧠

Android Studio / IntelliJ

Install Dart plugin from Preferences → Plugins. Full debugging and refactoring.

🌐

DartPad (Online)

Try Dart at dartpad.dev — no install needed. Great for quick experiments.

Your First Dart Program

hello.dart
// Every Dart program starts with a main() function
void main() {
  print('Hello, Dart!');
  print('Welcome to Dart Programming');
}
Run the program
dart run hello.dart
▶ Output
Hello, Dart! Welcome to Dart Programming
💡 Quick Tip

Use dart create my_project to scaffold a new Dart project with proper structure including pubspec.yaml, lib/, and test/ directories.

Section 02

Introduction to Dart

Understand what Dart is, why it matters, and its key features.

What is Dart?

Dart is a client-optimized programming language developed by Google. It powers Flutter, Google's UI toolkit for building natively compiled apps for mobile, web, and desktop from a single codebase. First unveiled in 2011, it has evolved into a modern, powerful language.

Why Learn Dart?

Fast on All Platforms

Compiles to ARM & x64 machine code for mobile/desktop, and JavaScript for web. Supports AOT and JIT.

🔒

Sound Null Safety

Built-in null safety prevents null reference errors at compile time. Makes code safer.

🎯

Type Safe

Static type checking and runtime checks ensure variables always match their declared type.

⚙️

Object-Oriented

Everything is an object, including numbers and functions. Supports classes, mixins, interfaces, generics.

🔄

Async Support

First-class support for asynchronous programming with async, await, Future, and Stream.

📦

Rich Ecosystem

Built-in core libraries plus a huge package ecosystem via pub.dev.

Dart Compilation Modes

ModeDescriptionUse Case
JITJust-In-Time — compiles at runtimeDevelopment (hot reload)
AOTAhead-Of-Time — compiles before runningProduction (fast startup)
dart2jsCompiles Dart to optimized JavaScriptWeb applications

Dart vs Other Languages

FeatureDartJavaScriptJavaPython
TypingStatic + DynamicDynamicStaticDynamic
Null Safety✅ Built-in
CompilationAOT + JITInterpretedAOT (JVM)Interpreted
OOP✅ FullPrototype-based✅ Full✅ Full
Asyncasync/awaitasync/awaitCompletableFutureasyncio
Mobile DevFlutterReact NativeAndroid NativeKivy

Program Structure

Dart Program Structure
// 1. Import statements
import 'dart:math';

// 2. Top-level variables
const appName = 'My App';

// 3. Top-level functions
String greet(String name) => 'Hello, $name!';

// 4. Classes
class Calculator {
  int add(int a, int b) => a + b;
}

// 5. Main function — entry point
void main() {
  print(greet('Dart'));
  var calc = Calculator();
  print(calc.add(5, 3));  // 8
}
📝 Key Points

Every Dart app must have a main() function. Files use .dart extension. Statements end with semicolons. Dart is case-sensitive.

Section 03

Variables & Data Types

Learn how to declare variables and understand Dart's type system.

Variable Declaration

All Declaration Methods
void main() {
  // 1. var — Type inferred, can reassign same type
  var name = 'Jobin';       // Inferred as String
  var age = 25;             // Inferred as int
  name = 'Alice';            // ✅ OK
  // name = 42;              // ❌ Error — can't change type

  // 2. Explicit type annotation
  String city = 'Kerala';
  int score = 100;
  double price = 29.99;
  bool isActive = true;

  // 3. dynamic — Type can change at runtime
  dynamic value = 'Hello';
  value = 42;               // ✅ OK
  value = true;             // ✅ OK

  // 4. final — Assigned once, runtime constant
  final username = 'dart_user';
  final now = DateTime.now();  // Runtime value OK!

  // 5. const — Compile-time constant (truly immutable)
  const pi = 3.14159;
  const maxItems = 100;

  // 6. late — Initialized later, must set before use
  late String description;
  description = 'Set later';
  print(description);
}

var vs final vs const vs late

KeywordReassign?Type Change?When Set?Example
var✅ Yes❌ NoAnytimevar x = 5; x = 10;
final❌ No❌ NoRuntime (once)final now = DateTime.now();
const❌ No❌ NoCompile-timeconst pi = 3.14;
dynamic✅ Yes✅ YesAnytimedynamic x = 5; x = 'hi';
late✅ Yes❌ NoBefore first uselate String s;

Data Types

Numbers: int & double

Numbers
void main() {
  int age = 25;
  int hex = 0xFF;            // 255
  double price = 29.99;
  double exponent = 1.5e3;  // 1500.0
  num value = 42;            // Parent of int & double
  value = 3.14;             // ✅ OK

  // Type conversion
  double b = 10.toDouble();  // 10.0
  int c = 3.7.toInt();       // 3 (truncates)
  int d = 3.7.round();       // 4
  int e = 3.7.ceil();        // 4
  int f = 3.7.floor();       // 3

  // Parse from String
  int parsed = int.parse('42');
  double parsed2 = double.parse('3.14');
  int? safe = int.tryParse('abc'); // null (no crash)
}

Boolean

Booleans
void main() {
  bool isLoggedIn = true;
  bool isAdult = (25 >= 18);  // true

  // Dart does NOT allow truthy/falsy like JS
  // if (1) {}      // ❌ Error in Dart
  if (isLoggedIn) { // ✅ Must be actual bool
    print('Welcome!');
  }
}

Type Checking & Casting

Type Operations
void main() {
  var value = 42;
  print(value is int);       // true
  print(value is String);    // false
  print(value is! double);   // true (is NOT)

  num number = 42;
  int integer = number as int; // Type cast
  print(value.runtimeType);  // int
}
💡 Best Practice

Use var when the type is obvious. Use explicit types for APIs and class fields. Prefer final over var when you don't need to reassign. Use const for compile-time constants.

🏋️ Practice Questions

  1. Declare variables of each data type (int, double, String, bool) and print their runtimeType.
  2. Create a const variable for PI and a final variable for the current date. Explain why const won't work for the date.
  3. Write a program that declares a late variable, assigns it inside a function, and prints it.
  4. Create a program that converts String → int, int → double, and double → String.
  5. Demonstrate the difference between var, dynamic, and explicit types by reassigning values.
Section 04

Comments in Dart

Add explanations, disable code, and generate documentation.

Single-Line Comments

Use // for single-line comments. Everything after // on that line is ignored by the compiler.

Single-Line Comments
void main() {
  // This is a single-line comment
  var name = 'Dart'; // Inline comment

  // print('This line is disabled');
  print(name);
}

Multi-Line Comments

Use /* */ for comments spanning multiple lines. They can also be nested.

Multi-Line Comments
void main() {
  /*
   * This is a multi-line comment.
   * It can span as many lines as needed.
   * Useful for disabling blocks of code.
   */
  print('Multi-line comments demo');

  /* You can also nest: /* inner comment */ */
}

Documentation Comments

Use /// (or /** */) for documentation comments. These are used by dartdoc to generate API documentation.

Documentation Comments
/// A class representing a bank account.
///
/// Use [deposit] to add money and [withdraw] to remove.
/// Example:
/// ```dart
/// var acc = BankAccount('Jobin', 1000);
/// acc.deposit(500);
/// ```
class BankAccount {
  /// The account holder's name.
  final String owner;

  /// Current balance in the account.
  double _balance;

  /// Creates a new account with [owner] and initial [balance].
  BankAccount(this.owner, this._balance);

  /// Deposits [amount] into the account.
  ///
  /// Throws [ArgumentError] if [amount] is negative.
  void deposit(double amount) {
    if (amount < 0) throw ArgumentError('Amount must be positive');
    _balance += amount;
  }
}
💡 Generate Docs

Run dart doc . in your project to generate HTML documentation from /// comments. Use [ClassName] syntax for cross-references.

Section 05

Operators

All the operators you need to work with data in Dart.

Arithmetic Operators

Arithmetic
void main() {
  int a = 10, b = 3;
  print(a + b);    // 13   Addition
  print(a - b);    // 7    Subtraction
  print(a * b);    // 30   Multiplication
  print(a / b);    // 3.33 Division (returns double)
  print(a ~/ b);   // 3    Integer Division
  print(a % b);    // 1    Modulus (remainder)
  print(-a);       // -10  Unary negation

  int x = 5;
  x++;              // 6  Post-increment
  ++x;              // 7  Pre-increment
  x--;              // 6  Post-decrement
}

Comparison & Logical Operators

Comparison & Logical
void main() {
  // Comparison
  print(5 == 5);    // true   Equal
  print(5 != 3);    // true   Not equal
  print(5 > 3);     // true   Greater than
  print(5 < 3);     // false  Less than
  print(5 >= 5);    // true   Greater or equal
  print(5 <= 3);    // false  Less or equal

  // Logical
  bool a = true, b = false;
  print(a && b);    // false  AND
  print(a || b);    // true   OR
  print(!a);        // false  NOT
}

Assignment & Null-Aware Operators

Assignment & Special Operators
void main() {
  // Compound assignment
  int x = 10;
  x += 5;   // 15
  x -= 3;   // 12
  x *= 2;   // 24
  x ~/= 4;  // 6
  x %= 4;   // 2

  // Ternary: condition ? ifTrue : ifFalse
  int age = 20;
  String status = age >= 18 ? 'Adult' : 'Minor';

  // ?? — Null coalescing (default if null)
  String? username;
  print(username ?? 'Guest');       // Guest

  // ??= — Assign only if null
  int? y;
  y ??= 10;   // y was null → 10
  y ??= 20;   // NOT null → stays 10

  // ?. — Null-aware access
  String? name;
  print(name?.toUpperCase());  // null (no crash)

  // Cascade (..) — chain calls on same object
  var list = [3, 1, 2]..sort()..add(4);
  print(list); // [1, 2, 3, 4]

  // Spread (...)
  var first = [1, 2];
  var combined = [0, ...first, 3]; // [0, 1, 2, 3]
}
Section 06

Strings in Dart

String creation, interpolation, methods, StringBuffer, and RegExp.

String Basics

String Creation
void main() {
  String s1 = 'Single quotes';
  String s2 = "Double quotes";

  // String interpolation
  String name = 'Dart';
  print('Hello, $name!');                  // Variable
  print('Length: ${name.length}');           // Expression
  print('Upper: ${name.toUpperCase()}');    // Method call

  // Multi-line strings
  String multi = '''
  Line 1
  Line 2
  Line 3''';

  // Raw string — ignores escape characters
  String raw = r'No \n newline here';

  // Concatenation & repetition
  String full = s1 + ' ' + s2;
  String repeated = 'Ha' * 3;  // HaHaHa
}

Essential String Methods

String Methods
void main() {
  String text = '  Hello, Dart World!  ';

  // Case conversion
  print(text.toUpperCase());   // HELLO, DART WORLD!
  print(text.toLowerCase());   // hello, dart world!

  // Trimming whitespace
  print(text.trim());          // 'Hello, Dart World!'
  print(text.trimLeft());      // 'Hello, Dart World!  '
  print(text.trimRight());     // '  Hello, Dart World!'

  // Searching
  print(text.contains('Dart'));  // true
  print(text.startsWith('  H')); // true
  print(text.endsWith('!  '));   // true
  print(text.indexOf('Dart'));   // 9

  // Replacing & splitting
  print(text.replaceAll('Dart', 'Flutter'));
  print('a,b,c'.split(','));       // [a, b, c]

  // Substring & padding
  print('Hello'.substring(1, 4)); // ell
  print('42'.padLeft(5, '0'));    // 00042
  print('Hi'.padRight(6, '!'));   // Hi!!!!

  // Checking empty
  print(''.isEmpty);     // true
  print('Hi'.isNotEmpty); // true
}

StringBuffer (Efficient Concatenation)

StringBuffer
void main() {
  var sb = StringBuffer();
  sb.write('Hello');
  sb.write(' ');
  sb.writeln('World');  // Adds newline
  sb.writeAll(['Dart', 'is', 'awesome'], ' ');
  print(sb.toString());
  // Hello World
  // Dart is awesome
}

Regular Expressions

RegExp
void main() {
  var email = '[email protected]';
  var emailRegex = RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$');
  print(emailRegex.hasMatch(email));  // true

  // Find all matches
  var text = 'Price: \$10 and \$25';
  var nums = RegExp(r'\d+');
  for (var match in nums.allMatches(text)) {
    print(match.group(0));  // 10, 25
  }

  // Replace with regex
  var cleaned = 'Hello   World'.replaceAll(RegExp(r'\s+'), ' ');
  print(cleaned);  // Hello World
}
Section 07

User Input

Read input from the console using dart:io library.

Reading String Input

String Input
import 'dart:io';

void main() {
  stdout.write('Enter your name: '); // No newline
  String? name = stdin.readLineSync();
  print('Hello, $name!');
}
▶ Output
Enter your name: Jobin Hello, Jobin!

Reading Numbers

Number Input with Parsing
import 'dart:io';

void main() {
  // Reading integer
  stdout.write('Enter your age: ');
  int age = int.parse(stdin.readLineSync()!);

  // Reading double
  stdout.write('Enter price: ');
  double price = double.parse(stdin.readLineSync()!);

  print('Age: $age, Price: $price');
}

Safe Input with Validation

Validated Input
import 'dart:io';

int readInt(String prompt) {
  while (true) {
    stdout.write(prompt);
    int? value = int.tryParse(stdin.readLineSync() ?? '');
    if (value != null) return value;
    print('Invalid! Please enter a number.');
  }
}

void main() {
  int age = readInt('Enter age: ');
  print('You are $age years old');
}

// Simple calculator with user input
// void main() {
//   stdout.write('First number: ');
//   double a = double.parse(stdin.readLineSync()!);
//   stdout.write('Operator (+, -, *, /): ');
//   String op = stdin.readLineSync()!;
//   stdout.write('Second number: ');
//   double b = double.parse(stdin.readLineSync()!);
//
//   double result = switch (op) {
//     '+' => a + b, '-' => a - b,
//     '*' => a * b, '/' => a / b,
//     _ => throw 'Invalid operator',
//   };
//   print('Result: $result');
// }
⚠️ DartPad Limitation

stdin is NOT available in DartPad. It only works in command-line Dart programs. Use dart run file.dart to test.

Section 08

Control Flow

Conditionals, loops, assert, and flow control statements.

If / Else If / Else

Conditionals
void main() {
  int score = 85;
  if (score >= 90) {
    print('Grade: A');
  } else if (score >= 80) {
    print('Grade: B');
  } else if (score >= 70) {
    print('Grade: C');
  } else {
    print('Grade: F');
  }
}

Switch Statement & Expression

Switch
void main() {
  String day = 'Monday';

  // Classic switch
  switch (day) {
    case 'Monday': case 'Tuesday':
    case 'Wednesday': case 'Thursday':
    case 'Friday':
      print('Weekday'); break;
    case 'Saturday': case 'Sunday':
      print('Weekend!'); break;
    default: print('Invalid');
  }

  // Dart 3+ Switch Expression
  String result = switch (day) {
    'Monday' || 'Tuesday' || 'Wednesday'
      || 'Thursday' || 'Friday' => 'Weekday',
    'Saturday' || 'Sunday' => 'Weekend',
    _ => 'Invalid',
  };
}

Loops

For, While, Do-While, For-Each
void main() {
  // Standard for loop
  for (int i = 1; i <= 5; i++) { print('Count: $i'); }

  // For-in loop
  var fruits = ['Apple', 'Banana', 'Cherry'];
  for (var fruit in fruits) { print(fruit); }

  // forEach method
  fruits.forEach((fruit) => print(fruit));

  // While loop
  int count = 0;
  while (count < 3) { print(count++); }

  // Do-while — runs at least once
  int num = 10;
  do { print(num++); } while (num < 5); // Prints 10

  // break & continue
  for (int i = 0; i < 10; i++) {
    if (i == 5) break;       // Exit loop at 5
    if (i % 2 == 0) continue; // Skip even numbers
    print(i);                 // 1, 3
  }
}

Assert (Debug Mode Only)

Assert Statements
void main() {
  int age = 25;

  // assert(condition) — throws error in debug if false
  assert(age >= 0);                 // ✅ Passes
  assert(age >= 18, 'Must be adult'); // With message

  // Only runs in debug mode (dart run)
  // Ignored in production (dart compile)

  var url = 'https://dart.dev';
  assert(url.startsWith('https'), 'URL must be HTTPS');
}
📝 Assert Tips

assert only works in debug mode. Use it for development-time checks — API validations, sanity checks during testing. In production, they are completely ignored.

🏋️ Practice Questions

  1. Write a program that checks if a number is positive, negative, or zero using if-else.
  2. Create a program using switch that prints the name of the day based on a number (1-7).
  3. Write a for loop to print the multiplication table of any given number.
  4. Create a program that prints all even numbers from 1 to 50 using a while loop with continue.
  5. Write a program that takes a month name and prints the number of days using Dart 3 switch expressions.
  6. Use do-while to keep asking for input until the user types "exit" (simulate with a counter).
Section 09

Functions

Define reusable blocks of code with various function types.

Function Types

Basic Functions
// Full syntax
String greet(String name) {
  return 'Hello, $name!';
}

// Arrow function (single expression)
String greetShort(String name) => 'Hello, $name!';

// void — no return value
void sayHello() => print('Hello!');

// Anonymous function (lambda)
var multiply = (int a, int b) => a * b;

void main() {
  print(greet('Dart'));    // Hello, Dart!
  print(multiply(3, 5));   // 15
}

Named & Optional Parameters

Parameters
// Named parameters { } — optional unless 'required'
void createUser({
  required String name,
  required String email,
  int age = 0,         // Default value
  String? phone,       // Optional (nullable)
}) {
  print('$name ($email) Age: $age');
}

// Optional positional parameters [ ]
String buildGreeting(String name, [String? title, String suffix = '!']) {
  if (title != null) return 'Hello, $title $name$suffix';
  return 'Hello, $name$suffix';
}

void main() {
  createUser(name: 'John', email: '[email protected]', age: 30);
  print(buildGreeting('John'));              // Hello, John!
  print(buildGreeting('John', 'Dr.', '.')); // Hello, Dr. John.
}

Scope, Closures & Higher-Order Functions

Scope & Closures
// Lexical scope: inner can access outer variables
var globalVar = 'I am global';

void main() {
  var localVar = 'I am local';

  void innerFunction() {
    print(globalVar);  // ✅ Can access global
    print(localVar);   // ✅ Can access outer local
  }
  innerFunction();

  // Closure — captures surrounding scope
  Function makeCounter() {
    int count = 0;
    return () => ++count;
  }
  var counter = makeCounter();
  print(counter()); // 1
  print(counter()); // 2 — remembers count!

  // Higher-order: returns a function
  Function makeMultiplier(int factor) {
    return (int value) => value * factor;
  }
  var triple = makeMultiplier(3);
  print(triple(10));  // 30

  // typedef — type alias for function signatures
  // typedef MathOp = int Function(int, int);
}

🏋️ Practice Questions

  1. Write a function that takes two numbers and returns their sum. Call it with positional parameters.
  2. Create a function with named parameters to calculate the area of a rectangle (with default width of 10).
  3. Write an arrow function that checks if a given number is even.
  4. Create a higher-order function called applyOperation that takes a number and a function, then applies the function to the number.
  5. Demonstrate a closure by creating a function that returns a counter function.
  6. Write an anonymous function that sorts a list of strings by their length.
Section 10

Math Library

Mathematical functions and constants from dart:math.

Constants & Basic Functions

dart:math Basics
import 'dart:math';

void main() {
  // Constants
  print(pi);    // 3.141592653589793
  print(e);     // 2.718281828459045
  print(ln2);   // 0.6931471805599453

  // Power & Roots
  print(pow(2, 10));  // 1024
  print(sqrt(144));   // 12.0

  // Min, Max
  print(min(10, 20));  // 10
  print(max(10, 20));  // 20

  // Logarithmic
  print(log(1));   // 0.0
  print(log(e));   // 1.0

  // Trigonometric (radians)
  print(sin(pi / 2));  // 1.0
  print(cos(0));        // 1.0
  print(tan(pi / 4));  // ~1.0

  // Number properties
  print((-5).abs());     // 5
  print(3.14.ceil());    // 4
  print(3.14.floor());   // 3
  print(3.14159.toStringAsFixed(2)); // 3.14
}

Random Numbers

Random Number Generation
import 'dart:math';

void main() {
  var rng = Random();

  // Random int (0 to max-1)
  print(rng.nextInt(100));   // 0–99

  // Random double (0.0 to 1.0)
  print(rng.nextDouble());   // 0.0–1.0

  // Random bool
  print(rng.nextBool());     // true or false

  // Random in range (min to max)
  int randomRange(int min, int max) => min + rng.nextInt(max - min + 1);
  print(randomRange(10, 50)); // 10–50

  // Generate list of random numbers
  var randomList = List.generate(5, (_) => rng.nextInt(100));
  print(randomList);  // e.g. [42, 7, 83, 15, 61]

  // Dice roll simulation
  var dice = List.generate(6, (_) => rng.nextInt(6) + 1);
  print('Dice rolls: $dice');

  // Random element from list
  var colors = ['Red', 'Blue', 'Green'];
  print(colors[rng.nextInt(colors.length)]);
}
Section 11

Collections

Lists, Sets, Maps — Dart's powerful data structures.

List (Array)

Lists
void main() {
  List<String> fruits = ['Apple', 'Banana', 'Cherry'];
  var numbers = [1, 2, 3, 4, 5];
  var filled = List.filled(5, 0);           // [0, 0, 0, 0, 0]
  var gen = List.generate(5, (i) => i * 2); // [0, 2, 4, 6, 8]

  // Accessing
  print(fruits[0]);     // Apple
  print(fruits.first);  // Apple
  print(fruits.last);   // Cherry
  print(fruits.length); // 3

  // Modifying
  fruits.add('Mango');
  fruits.insert(1, 'Grape');
  fruits.addAll(['Kiwi', 'Fig']);
  fruits.remove('Banana');
  fruits.removeAt(0);
  fruits.removeWhere((f) => f.startsWith('K'));

  // Functional methods (where = filter in JS)
  var scores = [85, 92, 78, 95, 88];
  var doubled = scores.map((s) => s * 2).toList();
  var high = scores.where((s) => s >= 90).toList(); // [92, 95]
  var total = scores.reduce((sum, s) => sum + s);   // 438
  var anyPerfect = scores.any((s) => s == 100);     // false
  var allPass = scores.every((s) => s > 50);        // true

  // Spread & Collection If/For
  var base = [1, 2];
  bool extra = true;
  var result = [0, ...base, if (extra) 3, for (var i in [4, 5]) i];
  print(result);  // [0, 1, 2, 3, 4, 5]
}

Set & Map

Set — Unique Values
void main() {
  Set<String> colors = {'Red', 'Green', 'Blue'};
  colors.add('Red');  // Ignored — duplicate!

  var a = {1, 2, 3, 4}, b = {3, 4, 5, 6};
  print(a.union(b));         // {1, 2, 3, 4, 5, 6}
  print(a.intersection(b));  // {3, 4}
  print(a.difference(b));    // {1, 2}

  // Remove duplicates from list
  var list = [1, 2, 2, 3, 3];
  var unique = list.toSet().toList(); // [1, 2, 3]
}
Map — Key-Value Pairs
void main() {
  Map<String, dynamic> user = {
    'name': 'Jobin', 'age': 25,
    'skills': ['Dart', 'Flutter', 'Django'],
  };

  print(user['name']);               // Jobin
  user['phone'] = '123456';       // Add
  user.remove('phone');             // Remove
  print(user.containsKey('email')); // false

  // Iterating
  user.forEach((k, v) => print('$k: $v'));

  // Filtering
  var prices = {'apple': 1.5, 'banana': 0.8, 'cherry': 2.0};
  var expensive = prices.entries
    .where((e) => e.value > 1.0)
    .map((e) => e.key).toList();   // [apple, cherry]

  prices.putIfAbsent('mango', () => 3.0);
}

🏋️ Practice Questions

  1. Create a list of 10 numbers. Use where to filter even numbers and map to square them.
  2. Create two Sets and find their union, intersection, and difference.
  3. Create a Map of 5 students (name → marks). Print only students who scored above 80.
  4. Write a program to reverse a List without using .reversed.
  5. Use spread operator and collection if/for to combine two lists conditionally.
  6. Sort a list of Maps by a specific key (e.g., sort students by age).
Section 12

File Handling

Read, write, and delete files using dart:io library.

Reading Files

Read File Contents
import 'dart:io';

void main() {
  // Synchronous read
  File file = File('test.txt');
  String contents = file.readAsStringSync();
  print(contents);

  // Read as lines
  List<String> lines = file.readAsLinesSync();
  for (var line in lines) { print(line); }

  // File info
  print('Path: ${file.path}');
  print('Size: ${file.lengthSync()} bytes');
  print('Exists: ${file.existsSync()}');
  print('Modified: ${file.lastModifiedSync()}');
}

Writing Files

Write & Append to Files
import 'dart:io';

void main() {
  File file = File('output.txt');

  // Write (creates or overwrites)
  file.writeAsStringSync('Hello, Dart!');
  print('File written.');

  // Append to existing content
  file.writeAsStringSync('\nAppended line.', mode: FileMode.append);
  print('Content appended.');

  // Write CSV data
  File csv = File('students.csv');
  csv.writeAsStringSync('Name,Age,Grade\n');
  csv.writeAsStringSync('Jobin,25,A\n', mode: FileMode.append);
  csv.writeAsStringSync('Alice,22,B\n', mode: FileMode.append);
}

Delete Files & Error Handling

Delete & Safe File Operations
import 'dart:io';

void main() {
  // Delete a file
  File file = File('temp.txt');
  if (file.existsSync()) {
    file.deleteSync();
    print('File deleted.');
  }

  // Safe file reading with try-catch
  try {
    String data = File('config.txt').readAsStringSync();
    print(data);
  } on FileSystemException catch (e) {
    print('File error: ${e.message}');
  }
}

// Async file operations (recommended for large files)
Future<void> asyncFileOps() async {
  var file = File('data.txt');
  await file.writeAsString('Async write!');
  String content = await file.readAsString();
  print(content);
  if (await file.exists()) await file.delete();
}

Reading CSV Files

Parse CSV
import 'dart:io';

void main() {
  File file = File('students.csv');
  List<String> lines = file.readAsLinesSync();

  for (var line in lines) {
    List<String> cols = line.split(',');
    print('Name: ${cols[0]}, Age: ${cols[1]}');
  }
}
⚠️ dart:io Limitation

File handling only works in command-line Dart and server-side apps. It's NOT available in Flutter web or DartPad. For Flutter mobile, use packages like path_provider.

🏋️ Practice Questions

  1. Write a program to create a file hello.txt and write your name to it.
  2. Write a program to append text to an existing file without overwriting.
  3. Create a program that reads a CSV file and prints each row as a Map.
  4. Write a program to copy the contents of one file to another.
  5. Create a program that checks if a file exists before deleting it, and handles errors gracefully.
Section 13

Null Safety

Dart's sound null safety prevents null errors at compile time.

Nullable vs Non-Nullable

Null Safety Basics
void main() {
  String name = 'Dart';   // Cannot be null
  // name = null;          // ❌ Compile error!

  String? nickname;        // CAN be null (default: null)
  nickname = 'DartDev';
  nickname = null;          // ✅ OK
}

All Null Safety Operators

Null Operators
void main() {
  String? name;

  // ?. — Safe access
  print(name?.length);       // null (no crash)

  // ?? — Default if null
  print(name ?? 'Guest');     // Guest

  // ??= — Assign only if null
  name ??= 'Default';
  name ??= 'Other';           // Stays 'Default'

  // ! — Null assertion ("I KNOW it's not null")
  String? maybe = 'Hello';
  String sure = maybe!;       // ✅ OK
  // ⚠️ If null → CRASH at runtime!
}

Type Promotion

Dart's flow analysis automatically promotes nullable types to non-nullable after null checks.

Type Promotion Patterns
// Pattern 1: if-null check promotes type
void greet(String? name) {
  if (name != null) {
    // name is promoted to String (non-null) here!
    print('Hello, ${name.toUpperCase()}');
  }
}

// Pattern 2: Early return
String getUserName(String? name) {
  if (name == null) return 'Anonymous';
  return name.toUpperCase(); // Promoted!
}

// Pattern 3: is check promotes type
void process(Object? value) {
  if (value is String) {
    print(value.length); // Promoted to String!
  } else if (value is int) {
    print(value.isEven);  // Promoted to int!
  }
}

// Pattern 4: late keyword
class Config {
  late String apiKey; // Non-null, initialized later
  void init() { apiKey = 'abc123'; }
}
⚠️ Avoid Overusing !

The ! operator bypasses null safety. If value IS null at runtime, your app crashes. Prefer ??, ?., or null checks for safe code.

🏋️ Practice Questions

  1. Declare a nullable String? variable. Use ?? to provide a default value and print it.
  2. Write a function that accepts a nullable int? and returns the value doubled, or 0 if null.
  3. Demonstrate type promotion by using an if (value != null) check inside a function.
  4. Create a class with a late variable. Show what happens if you access it before initialization.
  5. Refactor a function that uses ! operator to instead use safe null checks (?? or if-null).
Section 14

OOP — Classes & Objects

Classes, all constructor types, properties, getters/setters, and static members.

Classes & Objects

Basic Class
class Person {
  String name;
  int age;
  Person(this.name, this.age);  // Shorthand constructor

  void introduce() => print('I am $name, $age years old.');
  bool get isAdult => age >= 18;
  @override String toString() => 'Person($name, $age)';
}

void main() {
  var p = Person('Jobin', 25);
  p.introduce();       // I am Jobin, 25 years old.
  print(p.isAdult);    // true
  print(p);            // Person(Jobin, 25)
}

All Constructor Types

Default, Named, fromMap Constructors
class Product {
  String name;
  double price;
  String category;

  // 1. Named parameters with required & defaults
  Product({required this.name, required this.price, this.category = 'General'});

  // 2. Named constructor
  Product.free(String name) : name = name, price = 0.0, category = 'Free';

  // 3. From Map (common for JSON)
  Product.fromMap(Map<String, dynamic> map)
      : name = map['name'],
        price = map['price'].toDouble(),
        category = map['category'] ?? 'General';

  Map<String, dynamic> toMap() => {'name': name, 'price': price, 'category': category};
}

void main() {
  var p1 = Product(name: 'Laptop', price: 999.99);
  var p2 = Product.free('Trial');
  var p3 = Product.fromMap({'name': 'Phone', 'price': 699});
}

Constant Constructor

Create compile-time constant objects. All fields must be final. Two const objects with same values share the same instance in memory.

Constant Constructor
class Point {
  final int x;
  final int y;
  const Point(this.x, this.y); // const constructor
}

void main() {
  // Created as compile-time constants
  const p1 = Point(1, 2);
  const p2 = Point(1, 2);
  print(identical(p1, p2)); // true! Same instance

  // Same hash code
  print(p1.hashCode == p2.hashCode); // true

  // Can also create without const (different instances)
  var p3 = Point(1, 2);
  print(identical(p1, p3)); // false
}

Factory Constructor

A factory constructor can return existing instances from a cache, return subtypes, or validate input before creating objects.

Factory Constructor — Caching & Singleton
// Caching pattern — reuse instances
class Logger {
  final String name;
  static final Map<String, Logger> _cache = {};

  Logger._internal(this.name);  // Private constructor

  factory Logger(String name) {
    return _cache.putIfAbsent(name, () => Logger._internal(name));
  }

  void log(String msg) => print('[$name] $msg');
}

// Singleton pattern
class Database {
  static final Database _instance = Database._internal();
  Database._internal();
  factory Database() => _instance;
}

// Factory with validation
class Area {
  final double width, height;
  Area._(this.width, this.height);

  factory Area(double w, double h) {
    if (w <= 0 || h <= 0) throw ArgumentError('Must be positive');
    return Area._(w, h);
  }
  double get value => width * height;
}

void main() {
  var a = Logger('app');
  var b = Logger('app');
  print(identical(a, b)); // true! Same cached instance

  var db1 = Database();
  var db2 = Database();
  print(identical(db1, db2)); // true! Singleton
}

Getters, Setters & Encapsulation

Private Fields with _ prefix
class BankAccount {
  String _owner;     // _ prefix = private to library
  double _balance;

  BankAccount(this._owner, this._balance);

  String get owner => _owner;
  double get balance => _balance;

  set balance(double val) {
    if (val < 0) { print('Cannot be negative'); return; }
    _balance = val;
  }

  void deposit(double amt) => _balance += amt;
}

void main() {
  var acc = BankAccount('Jobin', 1000);
  acc.deposit(500);
  print('${acc.owner}: \$${acc.balance}');
}

Static Members

Static Fields & Methods
class MathHelper {
  static const double pi = 3.14159;
  static int _count = 0;
  MathHelper() { _count++; }
  static double circleArea(double r) => pi * r * r;
  static int get count => _count;
}

void main() {
  print(MathHelper.circleArea(5)); // 78.53975
}

🏋️ Practice Questions

  1. Create a Book class with properties name, author, and price. Add a display() method. Create 3 objects.
  2. Create a class BankAccount with encapsulation — private _balance, public getter, and deposit()/withdraw() methods with validation.
  3. Write a class with a named constructor fromMap that creates an object from a Map (simulating JSON parsing).
  4. Create a constant constructor class called Color with final fields r, g, b. Prove two const objects with same values are identical.
  5. Implement a factory constructor that acts as a Singleton pattern.
  6. Create a class with a static method that keeps count of how many objects have been created.
Section 15

Inheritance

Extend classes, override behavior, and use polymorphism.

extends & super

Inheritance
class Animal {
  String name;
  int age;
  Animal(this.name, this.age);
  void eat() => print('$name is eating');
  void sleep() => print('$name is sleeping');
}

class Dog extends Animal {
  String breed;
  Dog(String name, int age, this.breed) : super(name, age);

  void bark() => print('$name: Woof!');

  @override
  void eat() {
    super.eat();           // Call parent's eat()
    print('$name loves bones!');
  }
}

void main() {
  var dog = Dog('Buddy', 3, 'Golden Retriever');
  dog.eat();     // Buddy is eating → Buddy loves bones!
  dog.bark();    // Buddy: Woof!
  dog.sleep();   // Buddy is sleeping (inherited)

  // Polymorphism
  Animal myPet = dog;
  myPet.eat();  // Calls Dog's eat()
  print(dog is Animal);  // true
}

Super Parameters (Dart 3+)

Modern super Syntax
class Vehicle {
  String brand; int year;
  Vehicle({required this.brand, required this.year});
}

class Car extends Vehicle {
  int doors;
  // super. forwards params directly!
  Car({required super.brand, required super.year, this.doors = 4});
}

void main() {
  var car = Car(brand: 'Toyota', year: 2024);
  print('${car.brand} ${car.year} — ${car.doors} doors');
}
📝 Inheritance of Constructors

Subclasses do NOT inherit constructors. You must define constructors in the subclass and use super() or super. to call the parent constructor.

Section 16

Abstract Classes & Interfaces

Define contracts and blueprints for your classes.

Abstract Classes

Abstract Classes
abstract class Shape {
  double area();       // Abstract — MUST override
  double perimeter();  // Abstract
  void describe() {   // Concrete — CAN inherit
    print('Area: ${area().toStringAsFixed(2)}');
  }
}

class Circle extends Shape {
  double radius;
  Circle(this.radius);
  @override double area() => 3.14159 * radius * radius;
  @override double perimeter() => 2 * 3.14159 * radius;
}

class Rectangle extends Shape {
  double w, h;
  Rectangle(this.w, this.h);
  @override double area() => w * h;
  @override double perimeter() => 2 * (w + h);
}

void main() {
  List<Shape> shapes = [Circle(5), Rectangle(4, 5)];
  for (var s in shapes) s.describe();
}

Interfaces (implements)

Multiple Interfaces
abstract class Printable { void printData(); }
abstract class Serializable { Map<String, dynamic> toJson(); }

class User implements Printable, Serializable {
  String name, email;
  User(this.name, this.email);

  @override
  void printData() => print('User: $name ($email)');

  @override
  Map<String, dynamic> toJson() => {'name': name, 'email': email};
}
📝 extends vs implements

extends = Inherit code (GET parent's methods). implements = Contract only (MUST write all methods). Extend ONE, implement MANY.

Section 17

Mixins & Extensions

Reuse code without inheritance and add methods to existing types.

Mixins

Mixins
mixin Flyable { void fly() => print('Flying!'); }
mixin Swimmable { void swim() => print('Swimming!'); }
mixin Walkable {
  int steps = 0;
  void walk() { steps += 100; print('Walking ($steps steps)'); }
}

// Apply with 'with'
class Duck with Flyable, Swimmable, Walkable {
  String name;
  Duck(this.name);
}

// Combine extends + with
class Animal { String name; Animal(this.name); }

// Restrict with 'on' — only Animal subclasses can use
mixin Hunting on Animal {
  void hunt() => print('$name is hunting');
}

class Lion extends Animal with Hunting {
  Lion(String name) : super(name);
}

void main() {
  var duck = Duck('Donald');
  duck.fly(); duck.swim(); duck.walk();
  Lion('Simba').hunt();
}

Extension Methods

Extensions on Existing Types
extension StringX on String {
  String get capitalize =>
    isEmpty ? '' : '${this[0].toUpperCase()}${substring(1)}';
  bool get isEmail =>
    RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(this);
  String truncate(int max) =>
    length > max ? '${substring(0, max)}...' : this;
}

extension IntX on int {
  Duration get seconds => Duration(seconds: this);
  Duration get minutes => Duration(minutes: this);
  String get ordinal {
    if (this % 100 >= 11 && this % 100 <= 13) return '${this}th';
    return switch (this % 10) {
      1 => '${this}st', 2 => '${this}nd',
      3 => '${this}rd', _ => '${this}th',
    };
  }
}

void main() {
  print('hello'.capitalize);       // Hello
  print('[email protected]'.isEmail);  // true
  print(1.ordinal);               // 1st
  print(5.seconds);               // 0:00:05.000000
}
Section 18

Generics

Write type-safe, reusable code with generic types.

Generic Classes & Functions
// Generic class
class Box<T> {
  T value;
  Box(this.value);
  @override String toString() => 'Box<${value.runtimeType}>($value)';
}

// Multiple type parameters
class Pair<A, B> {
  A first; B second;
  Pair(this.first, this.second);
}

// Bounded generics
class NumberBox<T extends num> {
  T value;
  NumberBox(this.value);
  bool get isPositive => value > 0;
}

// Practical: API Response wrapper
class ApiResponse<T> {
  T? data; String? error; bool success;
  ApiResponse.success(this.data) : success = true;
  ApiResponse.failure(this.error) : success = false, data = null;
}

// Generic function
T findMax<T extends Comparable>(List<T> items) {
  T max = items.first;
  for (var item in items) { if (item.compareTo(max) > 0) max = item; }
  return max;
}

void main() {
  var intBox = Box<int>(42);
  var strBox = Box<String>('Hello');
  print(findMax([3, 1, 4, 1, 5])); // 5
}
Section 19

Async Programming

Handle Future, async/await, and Streams for non-blocking code.

Future & async/await

Futures
Future<String> fetchUser() async {
  await Future.delayed(Duration(seconds: 2));
  return 'Jobin — Flutter Developer';
}

void main() async {
  print('Fetching...');
  String user = await fetchUser();
  print(user);

  // Multiple futures in parallel
  var results = await Future.wait([
    fetchUser(),
    Future.delayed(Duration(seconds: 1), () => 42),
  ]);
  print(results); // [Jobin..., 42]

  // .then() callback style
  fetchUser()
    .then((data) => print('Got: $data'))
    .catchError((e) => print('Error: $e'));
}

Streams

Streams — Async Data Sequence
// async* returns Stream, yield emits values
Stream<int> countDown(int from) async* {
  for (int i = from; i >= 0; i--) {
    await Future.delayed(Duration(seconds: 1));
    yield i;
  }
}

void main() async {
  await for (var count in countDown(3)) {
    print(count);  // 3, 2, 1, 0
  }
}
💡 async* vs async

async returns a single Future. async* returns a Stream of multiple values using yield.

🏋️ Practice Questions

  1. Write an async function that returns your name after a 2-second delay using Future.delayed.
  2. Create 3 async functions and run them in parallel using Future.wait. Print all results.
  3. Build a Stream using async* and yield that emits numbers 1-5 with a 1-second interval.
  4. Write a function that fetches data and handles errors using try-catch with async/await.
  5. Create a stream controller that accepts user events, transforms them, and listens for changes.
Section 20

Error Handling

Handle exceptions gracefully with try-catch-finally.

Try / Catch / Finally
void main() {
  try {
    var list = [1, 2, 3];
    print(list[10]);
  } on RangeError catch (e) {
    print('Range Error: $e');
  } on FormatException catch (e) {
    print('Format Error: $e');
  } catch (e, stackTrace) {
    print('Unknown: $e');
    print(stackTrace);
  } finally {
    print('Always runs!');
  }
}

Custom Exceptions

Custom Exception Class
class AuthException implements Exception {
  final String message;
  final int code;
  AuthException(this.message, {this.code = 401});
  @override String toString() => 'AuthException [$code]: $message';
}

Future<String> login(String email, String pwd) async {
  if (email.isEmpty) throw AuthException('Email required', code: 400);
  if (pwd.length < 6) throw AuthException('Password too short');
  return 'token_abc123';
}

void main() async {
  try {
    await login('[email protected]', '123');
  } on AuthException catch (e) {
    print(e); // AuthException [401]: Password too short
  }
}
Section 21

DateTime & Duration

Working with dates, times, formatting, and calculations.

Creating DateTime

DateTime Basics
void main() {
  // Current date & time
  var now = DateTime.now();
  print(now);  // 2025-02-06 14:30:00.000

  // Specific date
  var birthday = DateTime(1998, 5, 15);
  var meeting = DateTime(2025, 3, 1, 14, 30); // With time

  // Parse from string
  var parsed = DateTime.parse('2025-12-25 10:00:00');
  var iso = DateTime.parse('2025-03-15T09:30:00Z'); // ISO 8601

  // UTC
  var utcNow = DateTime.now().toUtc();
  var utcDate = DateTime.utc(2025, 6, 15);

  // Accessing components
  print('Year: ${now.year}');
  print('Month: ${now.month}');
  print('Day: ${now.day}');
  print('Hour: ${now.hour}');
  print('Minute: ${now.minute}');
  print('Weekday: ${now.weekday}'); // 1=Mon, 7=Sun
  print('Timestamp: ${now.millisecondsSinceEpoch}');
}

DateTime Operations

Calculations & Comparisons
void main() {
  var now = DateTime.now();

  // Add/subtract duration
  var tomorrow = now.add(Duration(days: 1));
  var yesterday = now.subtract(Duration(days: 1));
  var nextWeek = now.add(Duration(days: 7));
  var inTwoHours = now.add(Duration(hours: 2));

  // Difference between dates
  var birthday = DateTime(1998, 5, 15);
  Duration age = now.difference(birthday);
  print('Days alive: ${age.inDays}');
  print('Hours alive: ${age.inHours}');

  // Comparison
  print(now.isAfter(birthday));   // true
  print(now.isBefore(tomorrow));  // true
  print(now.isAtSameMomentAs(now)); // true

  // Custom formatting (without package)
  String formatDate(DateTime d) =>
    '${d.day.toString().padLeft(2, '0')}/'
    '${d.month.toString().padLeft(2, '0')}/'
    '${d.year}';
  print(formatDate(now));  // 06/02/2025
}

Duration

Duration Operations
void main() {
  var d1 = Duration(hours: 2, minutes: 30);
  var d2 = Duration(minutes: 45);

  print(d1 + d2);       // 3:15:00.000000
  print(d1 - d2);       // 1:45:00.000000
  print(d1.inMinutes);  // 150
  print(d1.inSeconds);  // 9000
  print(d1 > d2);       // true
}
💡 Tip: intl Package

For advanced formatting use the intl package: DateFormat('yyyy-MM-dd').format(now). Install via dart pub add intl.

Section 22

Advanced Concepts

Enums, sealed classes, records, patterns, isolates.

Enhanced Enums

Enums with Fields & Methods
enum Status {
  pending('Pending', '⏳'),
  active('Active', '✅'),
  inactive('Inactive', '❌');

  final String label;
  final String icon;
  const Status(this.label, this.icon);
  bool get isUsable => this == active;
  @override String toString() => '$icon $label';
}

Records (Dart 3+)

Records — Multiple Return Values
(int, int) findMinMax(List<int> nums) {
  int min = nums.first, max = nums.first;
  for (var n in nums) {
    if (n < min) min = n;
    if (n > max) max = n;
  }
  return (min, max);
}

void main() {
  var (min, max) = findMinMax([3, 1, 4, 1, 5]);
  print('Min: $min, Max: $max');

  // Named records
  ({String name, int age}) user = (name: 'Jobin', age: 25);
  print(user.name);

  // Records compare by value
  print(('a', 1) == ('a', 1)); // true!
}

Pattern Matching (Dart 3+)

Patterns & Destructuring
void main() {
  // Destructuring
  var [first, second, ...rest] = [1, 2, 3, 4, 5];
  print('$first, $second, $rest');  // 1, 2, [3, 4, 5]

  // If-case pattern
  var json = {'type': 'user', 'name': 'Jobin'};
  if (json case {'type': 'user', 'name': String n}) {
    print('Found user: $n');
  }

  // Switch with patterns & guards
  describeValue(42);
  describeValue('hello');
}

void describeValue(Object? value) {
  switch (value) {
    case int n when n > 0: print('Positive int: $n');
    case String s: print('String: "$s" (${s.length})');
    case null: print('Null');
    default: print('Other: $value');
  }
}

Sealed Classes

Exhaustive Pattern Matching
sealed class Result<T> {}
class Success<T> extends Result<T> { final T data; Success(this.data); }
class Failure<T> extends Result<T> { final String error; Failure(this.error); }
class Loading<T> extends Result<T> {}

String handleResult(Result<String> result) => switch (result) {
  Success(data: var d) => 'Data: $d',
  Failure(error: var e) => 'Error: $e',
  Loading() => 'Loading...',
};

Operator Overloading & Callable Classes

Custom Operators & call()
class Vector {
  final double x, y;
  const Vector(this.x, this.y);
  Vector operator +(Vector o) => Vector(x + o.x, y + o.y);
  Vector operator *(double s) => Vector(x * s, y * s);
  @override String toString() => 'Vector($x, $y)';
}

// Callable class — use instance like a function
class Greeter {
  String prefix;
  Greeter(this.prefix);
  String call(String name) => '$prefix, $name!';
}

void main() {
  print(Vector(3, 4) + Vector(1, 2));  // Vector(4.0, 6.0)
  print(Vector(3, 4) * 2);           // Vector(6.0, 8.0)
  var greet = Greeter('Hello');
  print(greet('Dart'));              // Hello, Dart!
}

Isolates (Parallel Computing)

Isolates
import 'dart:isolate';

int heavyTask(int n) {
  int sum = 0;
  for (int i = 0; i < n; i++) sum += i;
  return sum;
}

void main() async {
  var result = await Isolate.run(() => heavyTask(1000000));
  print('Result: $result'); // Main thread stays responsive!
}
Section 23

Dart How-To Recipes

Common tasks and patterns you'll use daily.

Convert String to int / double

String ↔ Number Conversion
void main() {
  // String → int
  int a = int.parse('42');
  int? b = int.tryParse('abc');  // null (safe)

  // String → double
  double c = double.parse('3.14');

  // Number → String
  String d = 42.toString();
  String e = 3.14159.toStringAsFixed(2);  // "3.14"

  // Binary, Hex, Octal
  print(255.toRadixString(16));  // ff
  print(255.toRadixString(2));   // 11111111
  print(int.parse('ff', radix: 16)); // 255
}

Capitalize First Character

String Capitalization
extension Capitalize on String {
  String get capitalized =>
    isEmpty ? '' : '${this[0].toUpperCase()}${substring(1)}';
  String get titleCase =>
    split(' ').map((w) => w.capitalized).join(' ');
}

void main() {
  print('hello world'.capitalized); // Hello world
  print('hello world'.titleCase);   // Hello World
}

Generate Random Numbers

Random Number Generation
import 'dart:math';

void main() {
  var random = Random();

  // Random int (0 to 99)
  int randInt = random.nextInt(100);
  print('Random int: $randInt');

  // Random double (0.0 to 1.0)
  double randDouble = random.nextDouble();
  print('Random double: $randDouble');

  // Random bool
  bool randBool = random.nextBool();
  print('Random bool: $randBool');

  // Random number in range (min to max)
  int randomInRange(int min, int max) =>
    min + random.nextInt(max - min + 1);

  print('Dice roll: ${randomInRange(1, 6)}');

  // Generate OTP / random password
  String otp = List.generate(6, (_) =>
    random.nextInt(10).toString()).join();
  print('OTP: $otp');

  // Random item from list
  var colors = ['Red', 'Blue', 'Green', 'Yellow'];
  print('Pick: ${colors[random.nextInt(colors.length)]}');
}

Reverse a List

List Reversal
void main() {
  var list = [1, 2, 3, 4, 5];

  // Method 1: .reversed
  var reversed = list.reversed.toList();  // [5, 4, 3, 2, 1]

  // Method 2: In-place using sort
  var list2 = ['c', 'a', 'b'];
  list2.sort();  // [a, b, c]
  list2 = list2.reversed.toList(); // [c, b, a]
}

Make HTTP Request

HTTP with dart:io
import 'dart:convert';
import 'dart:io';

Future<void> main() async {
  var client = HttpClient();
  try {
    var request = await client.getUrl(
      Uri.parse('https://jsonplaceholder.typicode.com/todos/1'),
    );
    var response = await request.close();
    var body = await response.transform(utf8.decoder).join();
    var json = jsonDecode(body);
    print('Title: ${json['title']}');
  } finally {
    client.close();
  }
}

// With http package (simpler):
// import 'package:http/http.dart' as http;
// var response = await http.get(Uri.parse('...'));
// var data = jsonDecode(response.body);

JSON Encoding & Decoding

JSON Operations
import 'dart:convert';

class User {
  String name; int age;
  User(this.name, this.age);
  User.fromJson(Map<String, dynamic> j) : name = j['name'], age = j['age'];
  Map<String, dynamic> toJson() => {'name': name, 'age': age};
}

void main() {
  // Encode → JSON string
  var user = User('Jobin', 25);
  String jsonStr = jsonEncode(user.toJson());
  print(jsonStr); // {"name":"Jobin","age":25}

  // Decode → Map/Object
  var decoded = jsonDecode(jsonStr);
  var user2 = User.fromJson(decoded);
  print('${user2.name}, ${user2.age}');

  // List of objects
  var jsonList = '[{"name":"A","age":20},{"name":"B","age":30}]';
  List items = jsonDecode(jsonList);
  var users = items.map((j) => User.fromJson(j)).toList();
}

Useful Patterns

Stopwatch, Enum Parsing, Sorting
void main() {
  // Measure execution time
  var sw = Stopwatch()..start();
  var sum = List.generate(1000000, (i) => i).reduce((a, b) => a + b);
  sw.stop();
  print('Time: ${sw.elapsedMilliseconds}ms');

  // Sort objects by property
  var students = [
    {'name': 'Alice', 'grade': 85},
    {'name': 'Bob', 'grade': 92},
    {'name': 'Charlie', 'grade': 78},
  ];
  students.sort((a, b) => (b['grade'] as int).compareTo(a['grade'] as int));
  print(students.map((s) => s['name']).toList());
  // [Bob, Alice, Charlie]

  // Null-safe chaining
  String? getValue(Map<String, dynamic>? data) =>
    data?['user']?['profile']?['name'] as String?;
}
Section 24

Dart Interview Questions

Top questions and answers to prepare for Dart/Flutter interviews.

Basics & Language

Q1: What is Dart?
// Dart is a client-optimized, object-oriented language by Google.
// It powers Flutter and compiles to ARM, x86, JavaScript.
// Key features:
//   - Sound null safety
//   - AOT (ahead-of-time) & JIT (just-in-time) compilation
//   - Async/await with Futures & Streams
//   - Strong typing with type inference
Q2: var vs dynamic vs final vs const
void main() {
  // var — type inferred at assignment, can't change TYPE
  var name = 'Dart';     // String (fixed type)
  name = 'Flutter';       // ✅ OK, still String
  // name = 42;            // ❌ Error: can't assign int

  // dynamic — type CAN change at runtime
  dynamic value = 'Hello';
  value = 42;             // ✅ OK
  value = 3.14;           // ✅ OK

  // final — assigned once at RUNTIME, can't reassign
  final now = DateTime.now(); // ✅ Runtime value OK

  // const — compile-time constant, must be known at compile
  const pi = 3.14159;    // ✅ Known at compile time
  // const time = DateTime.now(); // ❌ Not compile-time
}
Q3: What is null safety?
// Sound null safety = variables are NON-nullable by default.
// You must explicitly opt-in to allow null.

String name = 'Dart';     // Cannot be null
String? nickname;          // CAN be null (? suffix)

// Null-aware operators:
//   ?.   — safe member access
//   ??   — default if null
//   ??=  — assign if null
//   !    — null assertion (dangerous)
//   late — promise to initialize before use

OOP & Constructors

Q4: Types of Constructors in Dart
class User {
  String name; int age;

  // 1. Default constructor
  User(this.name, this.age);

  // 2. Named constructor
  User.guest() : name = 'Guest', age = 0;

  // 3. Named constructor from Map
  User.fromMap(Map<String, dynamic> map)
    : name = map['name'], age = map['age'];
}

// 4. Constant constructor — all fields must be final
class Point {
  final int x, y;
  const Point(this.x, this.y);
}

// 5. Factory constructor — controls instance creation
class Logger {
  static final Logger _instance = Logger._internal();
  factory Logger() => _instance; // Singleton
  Logger._internal();
}
Q5: abstract vs interface vs mixin
// ABSTRACT CLASS — can have implementation + abstract methods
abstract class Shape {
  double area();  // Must be overridden
  void describe() => print('I am a shape'); // Has body
}

// INTERFACE — every class is implicitly an interface
// Use 'implements' — must override ALL methods
class Circle implements Shape {
  @override double area() => 3.14 * 25;
  @override void describe() => print('Circle');
}

// MIXIN — reusable code blocks, no constructor
mixin Flyable {
  void fly() => print('Flying!');
}
class Bird with Flyable {} // Gets fly() for free

Async & Collections

Q6: Future vs Stream
// FUTURE — single async result (like a Promise in JS)
Future<String> fetchUser() async {
  await Future.delayed(Duration(seconds: 1));
  return 'John';
}

// STREAM — multiple async values over time
Stream<int> countDown(int from) async* {
  for (var i = from; i >= 0; i--) {
    await Future.delayed(Duration(seconds: 1));
    yield i;
  }
}

// Key difference:
//   Future → await → one value
//   Stream → await for / listen → many values
Q7: List vs Set vs Map
// LIST — ordered, allows duplicates, index-based
List<int> list = [1, 2, 2, 3];  // [1, 2, 2, 3]

// SET — unordered, NO duplicates
Set<int> set = {1, 2, 2, 3};   // {1, 2, 3}

// MAP — key-value pairs, keys must be unique
Map<String, int> map = {'a': 1, 'b': 2};

// When to use which?
//   List → ordered data, allow duplicates
//   Set  → unique items, fast lookup
//   Map  → key-value associations

Advanced Concepts

Q8: What are Generics?
// Generics let you write type-safe, reusable code
class ApiResponse<T> {
  final bool success;
  final T? data;
  final String? error;
  ApiResponse.success(this.data) : success = true, error = null;
  ApiResponse.failure(this.error) : success = false, data = null;
}

// T can be ANY type — reusable for User, Product, etc.
var res = ApiResponse<String>.success('Data loaded');

// Bounded generics — restrict allowed types
T biggest<T extends num>(T a, T b) => a > b ? a : b;
Q9: Extension Methods
// Add methods to existing classes without modifying them
extension StringPower on String {
  bool get isEmail => RegExp(r'^[\w-.]+@[\w-]+\.\w{2,}$').hasMatch(this);
  String get reversed => split('').reversed.join();
}

void main() {
  print('test@mail.com'.isEmail); // true
  print('hello'.reversed);       // olleh
}
Q10: Sealed Classes & Pattern Matching (Dart 3+)
// Sealed class = known set of subtypes (exhaustive matching)
sealed class Result {}
class Success extends Result { final String data; Success(this.data); }
class Failure extends Result { final String error; Failure(this.error); }

String handle(Result r) => switch (r) {
  Success(data: var d) => '✅ $d',
  Failure(error: var e) => '❌ $e',
};
// Compiler ensures ALL subtypes are handled!
💡 Interview Preparation Tips

Practice writing code on DartPad (dartpad.dev). Focus on null safety, async/await, OOP concepts, and Dart 3 features like records, patterns, and sealed classes. Be ready to explain final vs const, abstract vs interface, and Future vs Stream with examples.