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.
Download Flutter SDK
Visit https://flutter.dev/docs/get-started/install and download for your OS.
Extract & Add to PATH
Extract the zip and add flutter/bin to your system PATH.
## Add to Environment Variables or run:
$env:PATH += ";C:\flutter\bin"
dart --version
flutter doctor# Add to ~/.bashrc or ~/.zshrc
export PATH="$HOME/flutter/bin:$PATH"
source ~/.zshrc
dart --versionOption 2: Standalone Dart SDK
# 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 dartIDE 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
// Every Dart program starts with a main() function
void main() {
print('Hello, Dart!');
print('Welcome to Dart Programming');
}dart run hello.dartHello, Dart!
Welcome to Dart ProgrammingUse dart create my_project to scaffold a new Dart project with proper structure including pubspec.yaml, lib/, and test/ directories.
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
| Mode | Description | Use Case |
|---|---|---|
JIT | Just-In-Time — compiles at runtime | Development (hot reload) |
AOT | Ahead-Of-Time — compiles before running | Production (fast startup) |
dart2js | Compiles Dart to optimized JavaScript | Web applications |
Dart vs Other Languages
| Feature | Dart | JavaScript | Java | Python |
|---|---|---|---|---|
| Typing | Static + Dynamic | Dynamic | Static | Dynamic |
| Null Safety | ✅ Built-in | ❌ | ❌ | ❌ |
| Compilation | AOT + JIT | Interpreted | AOT (JVM) | Interpreted |
| OOP | ✅ Full | Prototype-based | ✅ Full | ✅ Full |
| Async | async/await | async/await | CompletableFuture | asyncio |
| Mobile Dev | Flutter | React Native | Android Native | Kivy |
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
}Every Dart app must have a main() function. Files use .dart extension. Statements end with semicolons. Dart is case-sensitive.
Variables & Data Types
Learn how to declare variables and understand Dart's type system.
Variable Declaration
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
| Keyword | Reassign? | Type Change? | When Set? | Example |
|---|---|---|---|---|
var | ✅ Yes | ❌ No | Anytime | var x = 5; x = 10; |
final | ❌ No | ❌ No | Runtime (once) | final now = DateTime.now(); |
const | ❌ No | ❌ No | Compile-time | const pi = 3.14; |
dynamic | ✅ Yes | ✅ Yes | Anytime | dynamic x = 5; x = 'hi'; |
late | ✅ Yes | ❌ No | Before first use | late String s; |
Data Types
Numbers: int & double
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
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
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
}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
- Declare variables of each data type (
int,double,String,bool) and print theirruntimeType. - Create a
constvariable for PI and afinalvariable for the current date. Explain whyconstwon't work for the date. - Write a program that declares a
latevariable, assigns it inside a function, and prints it. - Create a program that converts
String → int,int → double, anddouble → String. - Demonstrate the difference between
var,dynamic, and explicit types by reassigning values.
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.
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.
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.
/// 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;
}
}Run dart doc . in your project to generate HTML documentation from /// comments. Use [ClassName] syntax for cross-references.
Operators
All the operators you need to work with data in Dart.
Arithmetic Operators
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
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
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]
}Strings in Dart
String creation, interpolation, methods, StringBuffer, and RegExp.
String Basics
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
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)
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
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
}User Input
Read input from the console using dart:io library.
Reading String Input
import 'dart:io';
void main() {
stdout.write('Enter your name: '); // No newline
String? name = stdin.readLineSync();
print('Hello, $name!');
}Enter your name: Jobin
Hello, Jobin!Reading Numbers
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
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');
// }stdin is NOT available in DartPad. It only works in command-line Dart programs. Use dart run file.dart to test.
Control Flow
Conditionals, loops, assert, and flow control statements.
If / Else If / Else
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
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
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)
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 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
- Write a program that checks if a number is positive, negative, or zero using
if-else. - Create a program using
switchthat prints the name of the day based on a number (1-7). - Write a
forloop to print the multiplication table of any given number. - Create a program that prints all even numbers from 1 to 50 using a
whileloop withcontinue. - Write a program that takes a month name and prints the number of days using Dart 3 switch expressions.
- Use
do-whileto keep asking for input until the user types "exit" (simulate with a counter).
Functions
Define reusable blocks of code with various function types.
Function Types
// 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
// 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
// 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
- Write a function that takes two numbers and returns their sum. Call it with positional parameters.
- Create a function with named parameters to calculate the area of a rectangle (with default width of 10).
- Write an arrow function that checks if a given number is even.
- Create a higher-order function called
applyOperationthat takes a number and a function, then applies the function to the number. - Demonstrate a closure by creating a function that returns a counter function.
- Write an anonymous function that sorts a list of strings by their length.
Math Library
Mathematical functions and constants from dart:math.
Constants & Basic Functions
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
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)]);
}Collections
Lists, Sets, Maps — Dart's powerful data structures.
List (Array)
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
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]
}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
- Create a list of 10 numbers. Use
whereto filter even numbers andmapto square them. - Create two Sets and find their union, intersection, and difference.
- Create a Map of 5 students (name → marks). Print only students who scored above 80.
- Write a program to reverse a List without using
.reversed. - Use spread operator and collection if/for to combine two lists conditionally.
- Sort a list of Maps by a specific key (e.g., sort students by age).
File Handling
Read, write, and delete files using dart:io library.
Reading Files
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
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
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
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]}');
}
}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
- Write a program to create a file
hello.txtand write your name to it. - Write a program to append text to an existing file without overwriting.
- Create a program that reads a CSV file and prints each row as a Map.
- Write a program to copy the contents of one file to another.
- Create a program that checks if a file exists before deleting it, and handles errors gracefully.
Null Safety
Dart's sound null safety prevents null errors at compile time.
Nullable vs Non-Nullable
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
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.
// 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'; }
}The ! operator bypasses null safety. If value IS null at runtime, your app crashes. Prefer ??, ?., or null checks for safe code.
🏋️ Practice Questions
- Declare a nullable
String?variable. Use??to provide a default value and print it. - Write a function that accepts a nullable
int?and returns the value doubled, or0if null. - Demonstrate type promotion by using an
if (value != null)check inside a function. - Create a class with a
latevariable. Show what happens if you access it before initialization. - Refactor a function that uses
!operator to instead use safe null checks (??orif-null).
OOP — Classes & Objects
Classes, all constructor types, properties, getters/setters, and static members.
Classes & Objects
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
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.
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.
// 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
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
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
- Create a
Bookclass with propertiesname,author, andprice. Add adisplay()method. Create 3 objects. - Create a class
BankAccountwith encapsulation — private_balance, public getter, anddeposit()/withdraw()methods with validation. - Write a class with a named constructor
fromMapthat creates an object from a Map (simulating JSON parsing). - Create a constant constructor class called
Colorwith final fieldsr,g,b. Prove two const objects with same values are identical. - Implement a factory constructor that acts as a Singleton pattern.
- Create a class with a static method that keeps count of how many objects have been created.
Inheritance
Extend classes, override behavior, and use polymorphism.
extends & super
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+)
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');
}Subclasses do NOT inherit constructors. You must define constructors in the subclass and use super() or super. to call the parent constructor.
Abstract Classes & Interfaces
Define contracts and blueprints for your 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)
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 = Inherit code (GET parent's methods). implements = Contract only (MUST write all methods). Extend ONE, implement MANY.
Mixins & Extensions
Reuse code without inheritance and add methods to existing types.
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
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
}Generics
Write type-safe, reusable code with generic types.
// 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
}Async Programming
Handle Future, async/await, and Streams for non-blocking code.
Future & async/await
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
// 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 returns a single Future. async* returns a Stream of multiple values using yield.
🏋️ Practice Questions
- Write an
asyncfunction that returns your name after a 2-second delay usingFuture.delayed. - Create 3 async functions and run them in parallel using
Future.wait. Print all results. - Build a
Streamusingasync*andyieldthat emits numbers 1-5 with a 1-second interval. - Write a function that fetches data and handles errors using
try-catchwithasync/await. - Create a stream controller that accepts user events, transforms them, and listens for changes.
Error Handling
Handle exceptions gracefully with 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
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
}
}DateTime & Duration
Working with dates, times, formatting, and calculations.
Creating DateTime
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
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
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
}For advanced formatting use the intl package: DateFormat('yyyy-MM-dd').format(now). Install via dart pub add intl.
Advanced Concepts
Enums, sealed classes, records, patterns, isolates.
Enhanced Enums
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+)
(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+)
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
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
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)
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!
}Dart How-To Recipes
Common tasks and patterns you'll use daily.
Convert String to int / double
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
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
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
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
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
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
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?;
}Dart Interview Questions
Top questions and answers to prepare for Dart/Flutter interviews.
Basics & Language
// 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 inferencevoid 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
}// 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 useOOP & Constructors
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();
}// 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 freeAsync & Collections
// 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// 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 associationsAdvanced Concepts
// 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;// 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
}// 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!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.