DART Interview Questions with Answers
Most Asked DART Interview Questions for Software Engineer Roles
Introduction
Dart is a client‑optimized language for fast apps on any platform. This page compiles the most frequently asked Dart interview questions, covering everything from basic syntax to advanced OOP, functional programming, and concurrency – essential for Flutter and full‑stack development.
Why Dart?
- Object‑oriented with sound null safety
- JIT & AOT compilation for fast development and performance
- Modern language features – async/await, generics, mixins
- Powerful standard library with collections and streams
- Used by Flutter – the world’s most popular cross‑platform framework
- Growing community and enterprise adoption
Most Asked DART Interview Questions
Dart is a client-optimized programming language developed by Google. It's used for building web, mobile (Flutter), and desktop applications with a focus on productivity and performance.
- Object-Oriented: Everything is an object
- Type Safety: Strong static typing with type inference
- Sound Null Safety: Prevents null reference errors
- Async/Await: Built-in support for asynchronous programming
- JIT & AOT Compilation: Fast development and production performance
// Hello World in Dart
void main() {
print('Hello, World!');
}Dart provides a rich set of built-in data types including numbers, strings, booleans, lists, sets, and maps. All types are objects and inherit from Object.
int— 64-bit integer (depending on platform)double— 64-bit floating pointString— UTF-16 encoded textbool— true/falseList— ordered collectionSet— unordered unique collectionMap— key-value pairs
// Data Types in Dart
void main() {
int age = 25;
double salary = 50000.50;
double pi = 3.14159265358979;
String grade = 'A';
bool isActive = true;
String name = "Alice";
num price = 99.99;
print('Age: $age');
print('Salary: $salary');
print('Pi: $pi');
print('Grade: $grade');
print('Active: $isActive');
print('Name: $name');
print('Price: $price');
}Dart uses var for type inference, final for runtime constants, and const for compile-time constants. It also supports explicit type declarations.
var— type-inferred variablefinal— set once at runtimeconst— compile-time constantlate— lazy initialization- Explicit typing —
int x = 10;
// Variables, Constants, and Final
class Program {
static const double PI = 3.14159;
static final int MAX = 100;
static void main() {
int x = 10;
const int MIN_VALUE = 0;
// Type inference
var val = 3.14;
var str = "Hello";
print('x = $x');
print('PI = $PI');
print('val = $val');
print('str = $str');
}
}
void main() => Program.main();A class in Dart encapsulates data and behavior. Dart uses constructors, getters, setters, and methods to define class behavior.
class— defines a classnew— creates an instance (optional)this— refers to current instance- Getters/Setters — property access control
@override— method overriding
// OOP - Classes and Objects
class Car {
String brand;
int year;
double price;
// Constructor
Car(this.brand, this.year, this.price);
// Getter methods
String get Brand => brand;
int get Year => year;
double get Price => price;
// Method
void display() {
print('Brand: $brand, Year: $year, Price: $$price');
}
// Destructor (not needed in Dart - garbage collected)
}
void main() {
Car c1 = Car('Toyota', 2022, 25000.0);
Car c2 = Car('BMW', 2023, 55000.0);
c1.display();
c2.display();
print('Brand: ${c1.Brand}');
}Constructors initialize class instances. Dart supports default, parameterized, named, and factory constructors.
ClassName()— default constructorClassName(this.param)— parameterizedClassName.name()— named constructorfactory— factory constructorconst— compile-time constructor
// Constructors in Dart
class Student {
String name;
int age;
// Default constructor
Student() : this.name = 'Unknown', this.age = 0 {
print('Default constructor called');
}
// Parameterized constructor
Student(this.name, this.age) {
print('Parameterized constructor: $name');
}
// Named constructor (copy constructor)
Student.copy(Student other)
: name = other.name,
age = other.age {
print('Copy constructor: $name');
}
void display() {
print('Name: $name, Age: $age');
}
}
void main() {
Student s1 = Student();
Student s2 = Student('Alice', 20);
Student s3 = Student.copy(s2);
s1.display();
s2.display();
s3.display();
}Inheritance allows classes to reuse and extend behavior. Dart uses single inheritance but supports multiple interfaces and mixins.
extends— single inheritancesuper— call parent constructor/method@override— method overridingimplements— interface implementationwith— mixin application
// Inheritance in Dart
class Animal {
String name;
int age;
Animal(this.name, this.age);
void speak() {
print('$name makes a sound.');
}
void info() {
print('Name: $name, Age: $age');
}
}
class Dog extends Animal {
String breed;
Dog(String name, int age, this.breed) : super(name, age);
@override
void speak() {
print('$name says: Woof!');
}
void display() {
info();
print('Breed: $breed');
}
}
class Cat extends Animal {
Cat(String name, int age) : super(name, age);
@override
void speak() {
print('$name says: Meow!');
}
}
void main() {
Dog dog = Dog('Rex', 3, 'German Shepherd');
Cat cat = Cat('Whiskers', 2);
dog.display();
dog.speak();
cat.speak();
// Polymorphism via base reference
Animal a = dog;
a.speak();
}Polymorphism allows objects of different types to be treated uniformly. Abstract classes define contracts that derived classes must implement.
abstract class— cannot be instantiated@override— implement abstract methods- Runtime Polymorphism: Dynamic method dispatch
- Interface Segregation: Define clear contracts
// Polymorphism and Abstract Classes
abstract class Shape {
double area();
double perimeter();
void display() {
print('Area: ${area()}, Perimeter: ${perimeter()}');
}
}
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 width, height;
Rectangle(this.width, this.height);
@override
double area() => width * height;
@override
double perimeter() => 2 * (width + height);
}
void main() {
List<Shape> shapes = [
Circle(5.0),
Rectangle(4.0, 6.0)
];
for (var s in shapes) {
s.display();
}
}Operator Overloading allows defining custom behavior for operators on user-defined types, making code more intuitive and readable.
operator +— addition operatoroperator -— subtraction operatoroperator *— multiplication operatoroperator ==— equality comparisonhashCode— must be overridden with ==
// Operator Overloading
class Vector2D {
double x, y;
Vector2D([this.x = 0, this.y = 0]);
// + operator
Vector2D operator +(Vector2D v) {
return Vector2D(x + v.x, y + v.y);
}
// - operator
Vector2D operator -(Vector2D v) {
return Vector2D(x - v.x, y - v.y);
}
// * scalar
Vector2D operator *(double s) {
return Vector2D(x * s, y * s);
}
// == operator
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
if (other is! Vector2D) return false;
return x == other.x && y == other.y;
}
@override
int get hashCode => Object.hash(x, y);
@override
String toString() => '($x, $y)';
}
void main() {
Vector2D v1 = Vector2D(3, 4);
Vector2D v2 = Vector2D(1, 2);
print('v1 = $v1');
print('v2 = $v2');
print('v1 + v2 = ${v1 + v2}');
print('v1 - v2 = ${v1 - v2}');
print('v1 * 2 = ${v1 * 2}');
print('v1 == v2: ${v1 == v2}');
}Generics provide type safety and code reuse by parameterizing types. They enable writing flexible, reusable code while maintaining compile-time type checking.
class Stack<T>— generic classT— type parameterextends— type constraintsList<int>— generic collection- Type Safety: Compile-time checking
// Generics in Dart
// Generic class
class Stack<T> {
List<T> _data = List.filled(100, null as T);
int _top = -1;
void push(T val) {
_data[++_top] = val;
}
T pop() {
return _data[_top--];
}
T peek() {
return _data[_top];
}
bool isEmpty() {
return _top == -1;
}
}
// Generic method
T max<T extends Comparable<T>>(T a, T b) {
return a.compareTo(b) > 0 ? a : b;
}
void swap<T>(T a, T b) {
// Dart doesn't support ref parameters directly
// This is a demonstration
print('Swapping ${a} and ${b}');
}
// Multiple type parameters
class Pair<K, V> {
K key;
V value;
Pair(this.key, this.value);
void printPair() {
print('$key -> $value');
}
}
void main() {
print(max<int>(10, 20));
print(max<double>(3.5, 2.1));
print(max<String>('B', 'A'));
Stack<int> si = Stack<int>();
si.push(1);
si.push(2);
si.push(3);
print('${si.pop()} ${si.pop()}');
Pair<String, int> p = Pair('age', 25);
p.printPair();
}Lists are ordered collections of elements. Dart provides extensive list operations including sorting, filtering, mapping, and functional transformations.
List<T>— generic list[]— list literaladd(),insert()— element operationswhere(),map()— functional operationssort()— sorting elements
// Collections - Lists
void main() {
List<int> list = [5, 2, 8, 1, 9, 3];
// Add elements
list.add(7);
list.insert(0, 0);
// Size
print('Length: ${list.length}');
print('Capacity: ${list.length}');
// Iterate
print(list.join(' '));
// Sort
list.sort();
print(list.join(' '));
// Find and remove
list.remove(8);
// 2D List
List<List<int>> mat = List.generate(3, (_) => List.filled(3, 0));
mat[1][1] = 5;
print('mat[1][1] = ${mat[1][1]}');
// Functional methods
var evens = list.where((x) => x.isEven).toList();
print('Evens: ${evens.join(', ')}');
}Maps are key-value pairs while Sets store unique elements. Both provide efficient lookup and data organization.
Map<K,V>— key-value dictionarySet<T>— unique elementsforEach()— iterationcontainsKey()— key existenceSplayTreeMap— sorted map
// Map and Set
void main() {
// Map (Dictionary)
Map<String, int> scores = {};
scores['Alice'] = 95;
scores['Bob'] = 87;
scores['Carol'] = 92;
scores.forEach((key, value) {
print('$key: $value');
});
print('Alice: ${scores['Alice']}');
print('Contains Bob: ${scores.containsKey('Bob')}');
// Set
Set<int> set = {5, 2, 8, 2, 1, 9, 5};
print(set.join(' '));
set.add(6);
set.remove(2);
print('Contains 5: ${set.contains(5)}');
// SplayTreeMap (sorted)
import 'dart:collection';
SplayTreeMap<String, int> sorted = SplayTreeMap<String, int>();
sorted['banana'] = 3;
sorted['apple'] = 5;
sorted['cherry'] = 2;
sorted.forEach((key, value) {
print('$key: $value');
});
}Stack (LIFO), Queue (FIFO), and PriorityQueue are specialized collections for specific use cases.
- Stack: LIFO using List
- Queue: FIFO using Queue class
- PriorityQueue: Ordered by priority
removeFirst(),removeLast()— operationsadd(),addAll()— insertion
// Stack, Queue, and PriorityQueue
import 'dart:collection';
void main() {
// Stack (LIFO) using List
List<int> stack = [];
stack.add(10);
stack.add(20);
stack.add(30);
print('Stack top: ${stack.last}');
while (stack.isNotEmpty) {
print('${stack.removeLast()} ');
}
print('');
// Queue (FIFO)
Queue<int> queue = Queue<int>();
queue.add(10);
queue.add(20);
queue.add(30);
print('Queue front: ${queue.first}');
while (queue.isNotEmpty) {
print('${queue.removeFirst()} ');
}
print('');
// PriorityQueue
import 'package:collection/collection.dart';
PriorityQueue<String> pq = PriorityQueue<String>((a, b) => a.compareTo(b));
pq.add('Low');
pq.add('High');
pq.add('Medium');
while (pq.isNotEmpty) {
print('${pq.removeFirst()} ');
}
print('');
// LinkedList (Doubly-linked)
LinkedList<DoubleLinkedEntry> ll = LinkedList<DoubleLinkedEntry>();
// Implementation details omitted for brevity
}Exception Handling provides robust error management with try-catch-finally blocks and custom exception types.
try-catch— exception handlingthrow— raise exceptionsfinally— cleanup codeon— specific exception types- Custom Exceptions: Extend Exception class
// Exception Handling
class ValidationError implements Exception {
final int code;
final String message;
ValidationError(this.message, this.code);
@override
String toString() => 'ValidationError: $message (code: $code)';
}
double divide(double a, double b) {
if (b == 0) {
throw ArgumentError('Division by zero!');
}
return a / b;
}
int getAge(int age) {
if (age < 0 || age > 150) {
throw ValidationError('Invalid age: $age', 400);
}
return age;
}
void main() {
// Basic try-catch
try {
print(divide(10, 2));
print(divide(10, 0));
} on ArgumentError catch (e) {
print('Error: ${e.message}');
}
// Custom exception
try {
getAge(200);
} on ValidationError catch (e) {
print('Validation [${e.code}]: ${e.message}');
} catch (e) {
print('General: ${e}');
}
// Finally block
try {
print('Processing...');
} finally {
print('Cleanup always runs');
}
}Resource Management ensures proper cleanup of resources like files, network connections, and database handles using try-finally patterns.
try-finally— guaranteed cleanup- Disposable Pattern: Explicit resource release
- Garbage Collection: Automatic memory management
dispose()— custom cleanup method
// Disposable and Using Statement
class Resource {
String name;
Resource(this.name) {
print('Resource acquired: $name');
}
void use() {
print('Using: $name');
}
void dispose() {
print('Resource released: $name');
}
}
void main() {
// Try-finally equivalent
Resource r1 = Resource('FileResource');
try {
r1.use();
} finally {
r1.dispose();
}
// Using declaration (simulated)
Resource r2 = Resource('DatabaseResource');
try {
r2.use();
} finally {
r2.dispose();
}
// Try-finally equivalent
Resource r3 = Resource('NetworkResource');
try {
r3.use();
} finally {
r3.dispose();
}
}Lambdas and Functional Programming enable concise, declarative code with functions as first-class citizens.
() =>— arrow function syntax- Higher-order Functions: Functions that take functions
- Closures: Functions with captured variables
map(),where(),reduce()— functional operations
// Lambdas and Functional Programming
void main() {
// Basic lambda
var greet = (String name) {
print('Hello, $name!');
};
greet('Alice');
// Function delegate
int add(int x, int y) => x + y;
print('Add: ${add(10, 20)}');
// Lambda with closures
int x = 10;
var addX = () => x + 5;
print('addX: ${addX()}');
// Functional with lists
List<int> nums = [5, 1, 8, 3, 9, 2, 7];
// Sort
nums.sort((a, b) => a.compareTo(b));
print(nums.join(' '));
// Filter
var evens = nums.where((n) => n.isEven).toList();
print('Evens: ${evens.join(' ')}');
// Transform
var squares = nums.map((n) => n * n).toList();
print('Squares: ${squares.join(' ')}');
// Reduce
int sum = nums.reduce((acc, n) => acc + n);
print('Sum: $sum');
}Async/Await and Futures provide asynchronous programming capabilities for non-blocking operations.
Future<T>— asynchronous resultasync— asynchronous functionawait— wait for FutureFuture.wait()— parallel operations- Error Handling: try-catch with async
// Async/Await and Futures
import 'dart:async';
Future<String> fetchData(String url, int delay) async {
await Future.delayed(Duration(milliseconds: delay));
return 'Data from $url';
}
Future<int> computeAsync(int a, int b) async {
await Future.delayed(Duration(milliseconds: 100));
return a + b;
}
void main() async {
// Basic async
String result = await fetchData('api.example.com', 500);
print(result);
// Parallel async tasks
List<Future<String>> tasks = [
fetchData('source1', 300),
fetchData('source2', 200),
fetchData('source3', 400)
];
List<String> results = await Future.wait(tasks);
print('All results: ${results.join(', ')}');
// Task with exception handling
try {
await Future.delayed(Duration(milliseconds: 100), () {
throw Exception('Task failed');
});
} catch (e) {
print('Caught: ${e}');
}
// Parallel processing
List<int> numbers = List.generate(10, (i) => i + 1);
List<Future<int>> parallelResults = numbers.map((n) async {
await Future.delayed(Duration(milliseconds: 50));
return n * n;
}).toList();
List<int> squares = await Future.wait(parallelResults);
print('Squares: ${squares.join(', ')}');
}File I/O operations enable reading from and writing to files, directories, and streams in Dart applications.
File— file operationsreadAsString()— reading fileswriteAsString()— writing filesDirectory— directory operationsFileStat— file metadata
// File I/O in Dart
import 'dart:io';
void main() async {
// Write to file
String path = 'students.txt';
List<String> lines = [
'Alice 20 3.85',
'Bob 22 3.62',
'Carol 21 3.91'
];
await File(path).writeAsString(lines.join('\n'));
// Read from file
String content = await File(path).readAsString();
print(content);
// Read/Write with Stream
File output = File('output.txt');
IOSink sink = output.openWrite();
sink.writeln('Hello, World!');
sink.writeln('Line 2');
await sink.close();
Stream<String> linesStream = output.openRead()
.transform(utf8.decoder)
.transform(LineSplitter());
await for (String line in linesStream) {
print(line);
}
// File operations
File('temp.txt').exists().then((exists) {
if (exists) File('temp.txt').delete();
});
// Directory operations
Directory('testdir').create();
Directory('testdir').delete();
// FileInfo
FileStat stat = await File('students.txt').stat();
print('File size: ${stat.size} bytes');
print('Created: ${stat.modified}');
// CSV parsing
String csv = 'Alice,Bob,Carol,Dave';
csv.split(',').forEach((token) => print('$token '));
print('');
}Iterable provides LINQ-like query operations for data transformation and aggregation.
map()— transform elementswhere()— filter elementsreduce()— aggregate valuesfold()— aggregate with initial valuegroupBy— grouping operations
// Functional Programming with Iterable
void main() {
List<int> nums = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3];
// LINQ-like queries
var unique = nums.toSet().toList()..sort();
print('Unique: ${unique.join(' ')}');
// Aggregation
print('Sum: ${nums.reduce((a, b) => a + b)}');
print('Min: ${nums.reduce((a, b) => a < b ? a : b)}');
print('Max: ${nums.reduce((a, b) => a > b ? a : b)}');
print('Avg: ${nums.reduce((a, b) => a + b) / nums.length}');
// Conditional counts
int evens = nums.where((x) => x.isEven).length;
print('Evens: $evens');
var firstGreater = nums.firstWhere((x) => x > 4, orElse: () => null);
print('First > 4: $firstGreater');
// Transform
var doubled = nums.map((x) => x * 2).toList();
print('Doubled: ${doubled.join(' ')}');
// Group by
var grouped = nums.fold<Map<String, List<int>>>({}, (map, x) {
String key = x.isEven ? 'Even' : 'Odd';
map.putIfAbsent(key, () => []).add(x);
return map;
});
grouped.forEach((key, value) {
print('$key: ${value.join(', ')}');
});
// Zip
List<int> a = [1, 2, 3];
List<int> b = [4, 5, 6];
var zipped = Iterable.generate(a.length, (i) => a[i] + b[i]).toList();
print('Zipped sum: ${zipped.join(', ')}');
}LinkedList is a fundamental data structure for dynamic data storage with efficient insertion and deletion.
- Node Structure: Data and next pointer
- Head Tracking: Entry point to list
pushFront()— insert at beginningpushBack()— insert at end- Traversal: Iterate through nodes
// LinkedList Implementation
class Node<T> {
T data;
Node<T>? next;
Node(this.data) : next = null;
}
class LinkedList<T> {
Node<T>? head;
void pushFront(T val) {
Node<T> node = Node<T>(val);
node.next = head;
head = node;
}
void pushBack(T val) {
Node<T> node = Node<T>(val);
if (head == null) {
head = node;
return;
}
Node<T> curr = head!;
while (curr.next != null) {
curr = curr.next!;
}
curr.next = node;
}
void display() {
Node<T>? curr = head;
while (curr != null) {
print('${curr.data} -> ');
curr = curr.next;
}
print('null');
}
}
void main() {
LinkedList<int> list = LinkedList<int>();
list.pushBack(10);
list.pushBack(20);
list.pushBack(30);
list.pushFront(5);
list.display();
}Binary Search efficiently finds elements in sorted arrays. Sorting arranges elements in a specific order.
- Binary Search: O(log n) search
sort()— built-in sortingindexOf()— linear search- Complexity Analysis: Time and space
// Binary Search and Sorting
int binarySearch(List<int> arr, int target) {
int left = 0;
int right = arr.length - 1;
while (left <= right) {
int mid = left + (right - left) ~/ 2;
if (arr[mid] == target) return mid;
if (arr[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return -1;
}
void main() {
List<int> arr = [2, 5, 8, 12, 16, 23, 38, 56, 72, 91];
// Manual
print('Index of 23: ${binarySearch(arr, 23)}');
// Built-in binary search
int index = arr.indexOf(56);
print('Index of 56: $index');
// Contains
print('Contains 56: ${arr.contains(56)}');
// Find methods
int found = arr.firstWhere((x) => x > 20, orElse: () => -1);
print('First > 20: $found');
// Find all
var greater = arr.where((x) => x > 30).toList();
print(' > 30: ${greater.join(', ')}');
// Sort
arr.sort();
print('Sorted: ${arr.join(' ')}');
}Recursion is a technique where a function calls itself to solve smaller instances of the same problem.
- Base Case: Stopping condition
- Recursive Case: Self-call with smaller input
- Stack Overflow: Risk of deep recursion
- Tail Recursion: Optimization technique
// Recursion in Dart
int factorial(int n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}
int fibonacci(int n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
void hanoi(int n, String from, String to, String aux) {
if (n == 1) {
print('Move disk 1: $from -> $to');
return;
}
hanoi(n - 1, from, aux, to);
print('Move disk $n: $from -> $to');
hanoi(n - 1, aux, to, from);
}
int power(int base, int exp) {
if (exp == 0) return 1;
if (exp % 2 == 0) {
int half = power(base, exp ~/ 2);
return half * half;
}
return base * power(base, exp - 1);
}
void main() {
print('5! = ${factorial(5)}');
print('fib(8) = ${fibonacci(8)}');
print('2^10 = ${power(2, 10)}');
print('Tower of Hanoi (3 disks):');
hanoi(3, 'A', 'C', 'B');
}Sorting Algorithms like Bubble Sort, Merge Sort, and built-in sort provide different performance characteristics.
- Bubble Sort: O(n²), simple but slow
- Merge Sort: O(n log n), efficient
- Built-in Sort: Optimized implementation
- Custom Comparators: Flexible sorting
// Sorting Algorithms in Dart
void bubbleSort(List<int> arr) {
int n = arr.length;
for (int i = 0; i < n - 1; i++) {
bool swapped = false;
for (int j = 0; j < n - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
swapped = true;
}
}
if (!swapped) break;
}
}
List<int> mergeSort(List<int> arr) {
if (arr.length <= 1) return arr;
int mid = arr.length ~/ 2;
List<int> left = mergeSort(arr.sublist(0, mid));
List<int> right = mergeSort(arr.sublist(mid));
return _merge(left, right);
}
List<int> _merge(List<int> left, List<int> right) {
List<int> result = [];
int i = 0, j = 0;
while (i < left.length && j < right.length) {
if (left[i] <= right[j]) {
result.add(left[i++]);
} else {
result.add(right[j++]);
}
}
result.addAll(left.sublist(i));
result.addAll(right.sublist(j));
return result;
}
void main() {
List<int> v1 = [64, 34, 25, 12, 22, 11, 90];
bubbleSort(v1);
print('Bubble: ${v1.join(' ')}');
List<int> v2 = [38, 27, 43, 3, 9, 82, 10];
v2 = mergeSort(v2);
print('Merge: ${v2.join(' ')}');
// Built-in sort
List<int> v3 = [5, 3, 1, 8, 2, 7];
v3.sort();
print('Built-in: ${v3.join(' ')}');
// Sort with comparison
v3.sort((a, b) => b.compareTo(a));
print('Descending: ${v3.join(' ')}');
}Dynamic Memory and Garbage Collection manage memory allocation and deallocation automatically.
- Heap Memory: Dynamically allocated objects
- Garbage Collector: Automatic memory reclamation
- Memory Management: Reference counting vs tracing
- Memory Leaks: Prevention and detection
// Dynamic Memory and Garbage Collection
class Matrix {
List<List<int>> data;
int rows, cols;
Matrix(this.rows, this.cols)
: data = List.generate(rows, (_) => List.filled(cols, 0));
void set(int r, int c, int val) {
data[r][c] = val;
}
int get(int r, int c) {
return data[r][c];
}
void printMatrix() {
for (var row in data) {
print(row.join(' '));
}
}
}
void main() {
// Arrays
List<int> arr = [10, 20, 30, 40, 50];
print(arr.join(' '));
// Multi-dimensional array
Matrix matrix = Matrix(3, 3);
matrix.set(0, 0, 1);
matrix.set(1, 1, 5);
matrix.set(2, 2, 9);
matrix.printMatrix();
// Jagged array
List<List<int>> jagged = [
[1, 2],
[3, 4, 5],
[6]
];
// Garbage Collection
print('Total memory: ${process.memoryUsage}');
// GC is automatic in Dart
}String Operations provide comprehensive text manipulation including concatenation, interpolation, and transformation.
- Concatenation:
+operator orStringBuffer - Interpolation:
$variableand${expression} - Case Conversion:
toLowerCase(),toUpperCase() - Split/Join:
split(),join() - Padding/Trimming:
padLeft(),trim()
// String Operations in Dart
void main() {
String s = 'Hello, World!';
// Basic operations
print('Length: ${s.length}');
print('Substring: ${s.substring(7, 12)}');
print('Contains: ${s.contains('World')}');
print('Index of: ${s.indexOf('World')}');
// StringBuffer (mutable string)
StringBuffer sb = StringBuffer('Hello');
sb.write(', World!');
sb.write(' Dart');
print(sb.toString());
// Case conversion
String lower = s.toLowerCase();
String upper = s.toUpperCase();
print('Lower: $lower');
print('Upper: $upper');
// Split and join
String csv = 'Alice,Bob,Carol,Dave';
List<String> tokens = csv.split(',');
print(tokens.join(' '));
// String interpolation
String name = 'Alice';
int age = 25;
print('$name is $age years old');
// Trim and padding
String padded = ' Hello ';
print("Trimmed: '${padded.trim()}'");
print("Padded: '${padded.padLeft(10)}'");
// Reverse and palindrome
String pal = 'racecar';
String reversed = pal.split('').reversed.join('');
print('$pal is palindrome: ${pal == reversed}');
}Interfaces and Abstract Classes define contracts for classes to implement, enabling polymorphism and code organization.
abstract class— blueprint for classesimplements— implement multiple interfaces- Interface Segregation: Focused interfaces
- Default Implementation: Abstract classes with default methods
// Interfaces and Abstract Classes
abstract class Drawable {
void draw();
void resize(double factor);
double get area;
}
abstract class Shape implements Drawable {
@override
void draw();
@override
void resize(double factor);
@override
double get area;
}
class Circle extends Shape {
double radius;
Circle(this.radius);
@override
void draw() {
print('Drawing Circle r=$radius');
}
@override
void resize(double factor) {
radius *= factor;
}
@override
double get area => 3.14159 * radius * radius;
}
class Square extends Shape {
double side;
Square(this.side);
@override
void draw() {
print('Drawing Square s=$side');
}
@override
void resize(double factor) {
side *= factor;
}
@override
double get area => side * side;
}
void main() {
List<Shape> shapes = [
Circle(5.0),
Square(4.0)
];
for (var s in shapes) {
s.draw();
print('Area: ${s.area.toStringAsFixed(2)}');
s.resize(2.0);
s.draw();
print('New Area: ${s.area.toStringAsFixed(2)}');
}
}Mixins provide a way to reuse code across class hierarchies without multiple inheritance, offering flexible composition.
mixin— reusable code blockwith— apply mixin to class- Composition: Combine multiple behaviors
- Method Resolution: Mixin order matters
// Multiple Inheritance via Mixins
mixin Vehicle {
int speed = 0;
void move();
}
mixin Electric {
int battery = 0;
void charge();
}
class ElectricCar with Vehicle, Electric {
String model;
ElectricCar(this.model, int speed, int battery) {
this.speed = speed;
this.battery = battery;
}
void display() {
print('Model: $model');
print('Speed: $speed km/h');
print('Battery: $battery%');
}
@override
void move() {
print('$model glides silently at $speed km/h');
}
@override
void charge() {
print('Charging battery: $battery%');
}
}
void main() {
ElectricCar tesla = ElectricCar('Tesla Model 3', 250, 85);
tesla.display();
tesla.move();
tesla.charge();
}Extension Methods allow adding new functionality to existing types without modifying their source code, enhancing code readability.
extension— define extension- Method Chaining: Fluent interface design
- Type Extension: Add methods to any type
- Generic Extensions: Work with generic types
// Extension Methods
extension StringExtensions on String {
bool isPalindrome() {
String cleaned = replaceAll(RegExp(r'[^a-zA-Z0-9]'), '').toLowerCase();
return cleaned == cleaned.split('').reversed.join('');
}
String toTitleCase() {
return this[0].toUpperCase() + substring(1).toLowerCase();
}
}
extension ListExtensions<T> on List<T> {
T second() {
if (length < 2) throw StateError('List has less than 2 elements');
return this[1];
}
T lastOrDefault(T defaultValue) {
return isEmpty ? defaultValue : last;
}
}
void main() {
// Extension methods on strings
String text = 'racecar';
print('$text is palindrome: ${text.isPalindrome()}');
String name = 'alice';
print('Title case: ${name.toTitleCase()}');
// Extension methods on lists
List<int> numbers = [10, 20, 30, 40];
print('Second: ${numbers.second()}');
print('LastOrDefault(100): ${numbers.lastOrDefault(100)}');
List<int> empty = [];
print('Empty LastOrDefault: ${empty.lastOrDefault(100)}');
// Chaining
var result = numbers.where((x) => x > 15).map((x) => x * 2).toList();
print('Chained: ${result.join(', ')}');
}Singleton ensures a class has only one instance. Factory provides an interface for creating objects without specifying concrete classes.
- Singleton: Global instance access
- Factory: Object creation encapsulation
- Lazy Initialization: Create on demand
- Abstract Factory: Family of related objects
// Design Patterns - Singleton and Factory
// Singleton
class Config {
static final Config _instance = Config._internal();
factory Config() => _instance;
String _dbUrl = 'localhost:5432';
Config._internal();
String get dbUrl => _dbUrl;
set dbUrl(String value) => _dbUrl = value;
}
// Factory Pattern
abstract class Logger {
void log(String message);
}
class ConsoleLogger implements Logger {
@override
void log(String message) {
print('[CONSOLE] $message');
}
}
class FileLogger implements Logger {
@override
void log(String message) {
print('[FILE] $message');
}
}
Logger createLogger(String type) {
switch (type.toLowerCase()) {
case 'console':
return ConsoleLogger();
case 'file':
return FileLogger();
default:
throw ArgumentError('Unknown logger type: $type');
}
}
void main() {
Config cfg = Config();
print(cfg.dbUrl);
Logger logger = createLogger('console');
logger.log('App started');
Logger flog = createLogger('file');
flog.log('Error occurred');
}Libraries organize code into reusable modules. Imports allow using code from other libraries with proper visibility.
library— library declarationimport— bring in external codeexport— expose library interface- Visibility Control: Private vs public
// Libraries and Imports
library math_utils;
const double PI = 3.14159265358979;
class Geometry {
static double circleArea(double r) => PI * r * r;
static double rectArea(double w, double h) => w * h;
}
library advanced_math;
class Algebra {
static double power(double base, int exp) {
double result = 1;
for (int i = 0; i < exp; i++) result *= base;
return result;
}
}
library physics;
const double G = 9.81;
class Mechanics {
static double kineticEnergy(double m, double v) => 0.5 * m * v * v;
static double weight(double mass) => mass * G;
}
void main() {
// Using the libraries
print('PI = $PI');
print('Circle area = ${Geometry.circleArea(5.0)}');
print('2^8 = ${Algebra.power(2, 8)}');
print('Weight(70kg) = ${Mechanics.weight(70)} N');
// Using alias
print('Rect area = ${Geometry.rectArea(4, 5)}');
}Two Sum is a classic algorithm problem that finds pairs in an array that sum to a target value using hash maps or two-pointer technique.
- Hash Map Approach: O(n) time, O(n) space
- Two Pointer: O(n log n) with sorting
- Trade-offs: Time vs Space
- Edge Cases: Duplicates, unsorted input
// Two Sum Problem in Dart
// Hash map approach O(n)
List<int> twoSum(List<int> nums, int target) {
Map<int, int> map = {};
for (int i = 0; i < nums.length; i++) {
int complement = target - nums[i];
if (map.containsKey(complement)) {
return [map[complement]!, i];
}
map[nums[i]] = i;
}
return [];
}
// Two pointer (sorted input)
List<(int, int)> twoSumPairs(List<int> arr, int target) {
List<(int, int)> result = [];
int l = 0, r = arr.length - 1;
while (l < r) {
int sum = arr[l] + arr[r];
if (sum == target) {
result.add((arr[l], arr[r]));
l++;
r--;
} else if (sum < target) {
l++;
} else {
r--;
}
}
return result;
}
void main() {
List<int> nums = [2, 7, 11, 15];
List<int> res = twoSum(nums, 9);
print('Indices: [${res[0]}, ${res[1]}]');
List<int> sorted = [1, 2, 3, 4, 6];
for (var pair in twoSumPairs(sorted, 6)) {
print('Pair: ${pair.$1} + ${pair.$2}');
}
}Kadane's Algorithm finds the maximum subarray sum in O(n) time, using dynamic programming to track current and maximum sums.
- Dynamic Programming: Track current max
- O(n) Time: Single pass algorithm
- Negative Numbers: Handle all-negative arrays
- Subarray Tracking: Find start and end indices
// Kadane's Algorithm in Dart
(int, int, int) maxSubarray(List<int> arr) {
int maxSum = -9223372036854775808;
int currSum = 0;
int start = 0, end = 0, tempStart = 0;
for (int i = 0; i < arr.length; i++) {
currSum += arr[i];
if (currSum > maxSum) {
maxSum = currSum;
start = tempStart;
end = i;
}
if (currSum < 0) {
currSum = 0;
tempStart = i + 1;
}
}
return (maxSum, start, end);
}
void main() {
List<int> arr = [-2, 1, -3, 4, -1, 2, 1, -5, 4];
var (sum, s, e) = maxSubarray(arr);
print('Max Sum: $sum');
print('Subarray: ${arr.sublist(s, e + 1).join(' ')}');
}Binary Tree is a hierarchical data structure where each node has at most two children, enabling efficient search and traversal.
- Node Structure: Value, left, right
- Tree Traversal: Inorder, Preorder, Postorder
- Insertion: Level-order insertion
- Height Calculation: Recursive depth measurement
// Binary Tree in Dart
class TreeNode<T> {
T val;
TreeNode<T>? left;
TreeNode<T>? right;
TreeNode(this.val) : left = null, right = null;
}
class BinaryTree<T> {
TreeNode<T>? root;
void insert(T val) {
TreeNode<T> node = TreeNode<T>(val);
if (root == null) {
root = node;
return;
}
List<TreeNode<T>> queue = [root!];
while (queue.isNotEmpty) {
TreeNode<T> curr = queue.removeAt(0);
if (curr.left == null) {
curr.left = node;
return;
} else {
queue.add(curr.left!);
}
if (curr.right == null) {
curr.right = node;
return;
} else {
queue.add(curr.right!);
}
}
}
void inorder() {
_inorder(root);
print('');
}
void _inorder(TreeNode<T>? node) {
if (node == null) return;
_inorder(node.left);
print('${node.val} ');
_inorder(node.right);
}
int height() {
return _height(root);
}
int _height(TreeNode<T>? node) {
if (node == null) return 0;
return 1 + (_height(node.left) > _height(node.right)
? _height(node.left)
: _height(node.right));
}
}
void main() {
BinaryTree<int> bt = BinaryTree<int>();
for (int v in [1, 2, 3, 4, 5, 6, 7]) {
bt.insert(v);
}
bt.inorder();
print('Height: ${bt.height()}');
}Binary Search Tree (BST) maintains sorted order with O(log n) average time for search, insert, and delete operations.
- BST Property: Left < parent < right
- Insertion: Recursive placement
- Search: Binary search on tree
- Traversal: Inorder gives sorted order
// Binary Search Tree in Dart
class BST {
int val;
BST? left;
BST? right;
BST(this.val) : left = null, right = null;
}
BST? insert(BST? root, int val) {
if (root == null) return BST(val);
if (val < root.val) {
root.left = insert(root.left, val);
} else if (val > root.val) {
root.right = insert(root.right, val);
}
return root;
}
bool search(BST? root, int val) {
if (root == null) return false;
if (root.val == val) return true;
return val < root.val ? search(root.left, val) : search(root.right, val);
}
void inorder(BST? root) {
if (root == null) return;
inorder(root.left);
print('${root.val} ');
inorder(root.right);
}
void main() {
BST? root = null;
for (int v in [50, 30, 70, 20, 40, 60, 80]) {
root = insert(root, v);
}
inorder(root);
print('');
print('Search 40: ${search(root, 40) ? 'Found' : 'Not found'}');
print('Search 99: ${search(root, 99) ? 'Found' : 'Not found'}');
}BFS (Breadth-First Search) explores level by level. DFS (Depth-First Search) explores as far as possible before backtracking.
- BFS: Queue-based level-order traversal
- DFS: Stack/recursive depth traversal
- Visited Tracking: Prevent infinite loops
- Applications: Shortest path, connected components
// Graph BFS and DFS in Dart
class Graph {
int v;
List<List<int>> adj;
Graph(this.v) : adj = List.generate(v, (_) => []);
void addEdge(int u, int v) {
adj[u].add(v);
adj[v].add(u);
}
void bfs(int start) {
List<bool> visited = List.filled(v, false);
List<int> queue = [];
visited[start] = true;
queue.add(start);
print('BFS: ');
while (queue.isNotEmpty) {
int v = queue.removeAt(0);
print('$v ');
for (int u in adj[v]) {
if (!visited[u]) {
visited[u] = true;
queue.add(u);
}
}
}
print('');
}
void dfs(int start) {
List<bool> visited = List.filled(v, false);
print('DFS: ');
_dfsHelper(start, visited);
print('');
}
void _dfsHelper(int v, List<bool> visited) {
visited[v] = true;
print('$v ');
for (int u in adj[v]) {
if (!visited[u]) _dfsHelper(u, visited);
}
}
}
void main() {
Graph g = Graph(6);
g.addEdge(0, 1);
g.addEdge(0, 2);
g.addEdge(1, 3);
g.addEdge(2, 4);
g.addEdge(3, 5);
g.bfs(0);
g.dfs(0);
}Dijkstra's Algorithm finds the shortest path from a source to all vertices in a weighted graph with non-negative edges.
- Shortest Path: Single source, all destinations
- Priority Queue: Efficient minimum selection
- Edge Relaxation: Update distances
- Limitations: No negative edges
// Dijkstra's Algorithm in Dart
import 'dart:collection';
void dijkstra(List<List<(int, int)>> graph, int src) {
int v = graph.length;
List<int> dist = List.filled(v, 9223372036854775807);
List<bool> visited = List.filled(v, false);
dist[src] = 0;
for (int i = 0; i < v; i++) {
// Find minimum distance vertex
int minDist = 9223372036854775807;
int minVertex = -1;
for (int j = 0; j < v; j++) {
if (!visited[j] && dist[j] < minDist) {
minDist = dist[j];
minVertex = j;
}
}
if (minVertex == -1) break;
visited[minVertex] = true;
for (var (w, neighbor) in graph[minVertex]) {
if (!visited[neighbor] && dist[minVertex] + w < dist[neighbor]) {
dist[neighbor] = dist[minVertex] + w;
}
}
}
print('Shortest distances from $src:');
for (int i = 0; i < v; i++) {
print(' To $i: ${dist[i] == 9223372036854775807 ? -1 : dist[i]}');
}
}
void main() {
int v = 5;
List<List<(int, int)>> graph = List.generate(v, (_) => []);
void addEdge(int u, int v, int w) {
graph[u].add((w, v));
graph[v].add((w, u));
}
addEdge(0, 1, 10);
addEdge(0, 3, 5);
addEdge(1, 2, 1);
addEdge(1, 3, 2);
addEdge(2, 4, 4);
addEdge(3, 4, 9);
dijkstra(graph, 0);
}Dynamic Programming solves optimization problems by breaking them into overlapping subproblems and storing results.
- 0/1 Knapsack: Maximize value with weight constraint
- LCS: Longest Common Subsequence
- Optimal Substructure: Build from subproblems
- Memoization: Cache computed results
// Dynamic Programming - Classic Problems
// 0/1 Knapsack
int knapsack(List<int> weights, List<int> values, int w) {
int n = weights.length;
List<List<int>> dp = List.generate(n + 1, (_) => List.filled(w + 1, 0));
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= w; j++) {
dp[i][j] = dp[i - 1][j];
if (weights[i - 1] <= j) {
int val = dp[i - 1][j - weights[i - 1]] + values[i - 1];
if (val > dp[i][j]) dp[i][j] = val;
}
}
}
return dp[n][w];
}
// Longest Common Subsequence
int lcs(String s1, String s2) {
int m = s1.length, n = s2.length;
List<List<int>> dp = List.generate(m + 1, (_) => List.filled(n + 1, 0));
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if (s1[i - 1] == s2[j - 1]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
dp[i][j] = dp[i - 1][j] > dp[i][j - 1] ? dp[i - 1][j] : dp[i][j - 1];
}
}
}
return dp[m][n];
}
void main() {
List<int> weights = [1, 3, 4, 5];
List<int> values = [1, 4, 5, 7];
print('Knapsack(W=7): ${knapsack(weights, values, 7)}');
String s1 = 'ABCBDAB';
String s2 = 'BDCAB';
print('LCS: ${lcs(s1, s2)}');
}Hash Map implementation involves creating buckets, handling collisions, and providing efficient key-value operations.
- Hash Function: Map keys to bucket indices
- Collision Handling: Chaining with lists
- Operations: Put, Get, Remove
- Load Factor: Performance optimization
// Hash Map - Custom Implementation
class HashMap<K, V> {
int capacity;
List<List<MapEntry<K, V>>> table;
HashMap({this.capacity = 16})
: table = List.generate(16, (_) => []);
int _hash(K key) {
return key.hashCode.abs() % capacity;
}
void put(K key, V value) {
int idx = _hash(key);
for (int i = 0; i < table[idx].length; i++) {
if (table[idx][i].key == key) {
table[idx][i] = MapEntry(key, value);
return;
}
}
table[idx].add(MapEntry(key, value));
}
V get(K key) {
int idx = _hash(key);
for (var entry in table[idx]) {
if (entry.key == key) return entry.value;
}
throw Exception('Key not found');
}
bool containsKey(K key) {
int idx = _hash(key);
for (var entry in table[idx]) {
if (entry.key == key) return true;
}
return false;
}
void remove(K key) {
int idx = _hash(key);
table[idx].removeWhere((entry) => entry.key == key);
}
}
void main() {
HashMap<String, int> map = HashMap<String, int>();
map.put('alice', 90);
map.put('bob', 85);
map.put('carol', 92);
print('alice: ${map.get('alice')}');
print('Contains bob: ${map.containsKey('bob')}');
map.remove('bob');
print('Contains bob after remove: ${map.containsKey('bob')}');
}Heap and Priority Queue provide efficient priority-based operations with O(log n) insertion and removal.
- Min-Heap/Max-Heap: Priority ordering
- K-Largest Elements: Heap-based selection
- Merge Sorted Arrays: Efficient merging
- Custom Comparators: Flexible ordering
// Heap and Priority Queue in Dart
import 'dart:collection';
// K largest elements
List<int> kLargest(List<int> arr, int k) {
PriorityQueue<int> minHeap = PriorityQueue<int>((a, b) => a.compareTo(b));
for (int x in arr) {
minHeap.add(x);
if (minHeap.length > k) minHeap.removeFirst();
}
return minHeap.toList();
}
// Merge K sorted arrays
List<int> mergeKSorted(List<List<int>> arrays) {
PriorityQueue<(int, int, int)> pq = PriorityQueue<(int, int, int)>(
(a, b) => a.$1.compareTo(b.$1)
);
for (int i = 0; i < arrays.length; i++) {
if (arrays[i].isNotEmpty) {
pq.add((arrays[i][0], i, 0));
}
}
List<int> result = [];
while (pq.isNotEmpty) {
var (val, i, j) = pq.removeFirst();
result.add(val);
if (j + 1 < arrays[i].length) {
pq.add((arrays[i][j + 1], i, j + 1));
}
}
return result;
}
void main() {
List<int> arr = [3, 1, 5, 12, 2, 11, 9];
List<int> top3 = kLargest(arr, 3);
print('Top 3: ${top3.join(' ')}');
List<List<int>> kArr = [
[1, 4, 7],
[2, 5, 8],
[3, 6, 9]
];
List<int> merged = mergeKSorted(kArr);
print('Merged: ${merged.join(' ')}');
}Trie (Prefix Tree) enables efficient prefix-based operations like autocomplete, dictionary search, and pattern matching.
- Node Structure: Children map, end marker
- Insertion: Build tree character by character
- Search: Exact word or prefix
- Applications: Autocomplete, spell checking
// Trie Data Structure in Dart
class TrieNode {
Map<String, TrieNode> children = {};
bool isEnd = false;
}
class Trie {
TrieNode root = TrieNode();
void insert(String word) {
TrieNode curr = root;
for (String c in word.split('')) {
if (!curr.children.containsKey(c)) {
curr.children[c] = TrieNode();
}
curr = curr.children[c]!;
}
curr.isEnd = true;
}
bool search(String word) {
TrieNode curr = root;
for (String c in word.split('')) {
if (!curr.children.containsKey(c)) return false;
curr = curr.children[c]!;
}
return curr.isEnd;
}
bool startsWith(String prefix) {
TrieNode curr = root;
for (String c in prefix.split('')) {
if (!curr.children.containsKey(c)) return false;
curr = curr.children[c]!;
}
return true;
}
}
void main() {
Trie t = Trie();
t.insert('apple');
t.insert('app');
t.insert('apply');
print('Search apple: ${t.search('apple')}');
print('Search app: ${t.search('app')}');
print('Search ap: ${t.search('ap')}');
print('StartsWith appl: ${t.startsWith('appl')}');
print('StartsWith xyz: ${t.startsWith('xyz')}');
}Segment Tree efficiently answers range queries and supports point updates on arrays with O(log n) time complexity.
- Range Queries: Sum, min, max
- Point Updates: Modify single element
- Build Time: O(n) construction
- Applications: RMQ, prefix sums
// Segment Tree in Dart
class SegmentTree {
List<int> tree;
int n;
SegmentTree(List<int> arr)
: n = arr.length,
tree = List.filled(4 * arr.length, 0) {
_build(arr, 1, 0, n - 1);
}
void _build(List<int> arr, int node, int l, int r) {
if (l == r) {
tree[node] = arr[l];
return;
}
int mid = (l + r) ~/ 2;
_build(arr, node * 2, l, mid);
_build(arr, node * 2 + 1, mid + 1, r);
tree[node] = tree[node * 2] + tree[node * 2 + 1];
}
void update(int idx, int val) {
_update(1, 0, n - 1, idx, val);
}
void _update(int node, int l, int r, int idx, int val) {
if (l == r) {
tree[node] = val;
return;
}
int mid = (l + r) ~/ 2;
if (idx <= mid) {
_update(node * 2, l, mid, idx, val);
} else {
_update(node * 2 + 1, mid + 1, r, idx, val);
}
tree[node] = tree[node * 2] + tree[node * 2 + 1];
}
int query(int ql, int qr) {
return _query(1, 0, n - 1, ql, qr);
}
int _query(int node, int l, int r, int ql, int qr) {
if (qr < l || r < ql) return 0;
if (ql <= l && r <= qr) return tree[node];
int mid = (l + r) ~/ 2;
return _query(node * 2, l, mid, ql, qr) +
_query(node * 2 + 1, mid + 1, r, ql, qr);
}
}
void main() {
List<int> arr = [1, 3, 5, 7, 9, 11];
SegmentTree st = SegmentTree(arr);
print('Sum [1,3]: ${st.query(1, 3)}');
st.update(1, 10);
print('Sum [1,3] after update: ${st.query(1, 3)}');
}Union-Find efficiently manages disjoint sets with union and find operations, using path compression and union by rank.
- Find Operation: Locate set representative
- Union Operation: Merge two sets
- Path Compression: Optimize find
- Union by Rank: Efficient merging
// Union-Find in Dart
class UnionFind {
List<int> parent;
List<int> rank;
UnionFind(int n)
: parent = List.generate(n, (i) => i),
rank = List.filled(n, 0);
int find(int x) {
if (parent[x] != x) {
parent[x] = find(parent[x]);
}
return parent[x];
}
bool unite(int x, int y) {
int px = find(x);
int py = find(y);
if (px == py) return false;
if (rank[px] < rank[py]) {
int temp = px;
px = py;
py = temp;
}
parent[py] = px;
if (rank[px] == rank[py]) rank[px]++;
return true;
}
bool connected(int x, int y) {
return find(x) == find(y);
}
}
void main() {
UnionFind uf = UnionFind(6);
uf.unite(0, 1);
uf.unite(1, 2);
uf.unite(3, 4);
print('0-2: ${uf.connected(0, 2)}');
print('0-3: ${uf.connected(0, 3)}');
uf.unite(2, 3);
print('0-4 after merge: ${uf.connected(0, 4)}');
}Sliding Window Maximum finds the maximum in each sliding window of size k using a deque for efficient processing.
- Deque Approach: O(n) time complexity
- Window Tracking: Maintain indices
- Remove Out-of-Window: Keep valid elements
- Applications: Streaming data, time-series
// Sliding Window Maximum in Dart
List<int> maxSlidingWindow(List<int> nums, int k) {
List<int> deque = [];
List<int> result = [];
for (int i = 0; i < nums.length; i++) {
// Remove out-of-window indices
while (deque.isNotEmpty && deque.first < i - k + 1) {
deque.removeAt(0);
}
// Remove smaller elements from rear
while (deque.isNotEmpty && nums[deque.last] < nums[i]) {
deque.removeLast();
}
deque.add(i);
if (i >= k - 1) {
result.add(nums[deque.first]);
}
}
return result;
}
void main() {
List<int> nums = [1, 3, -1, -3, 5, 3, 6, 7];
int k = 3;
List<int> res = maxSlidingWindow(nums, k);
print('Sliding window max: ${res.join(' ')}');
}KMP (Knuth-Morris-Pratt) is an efficient string matching algorithm using a partial match table to avoid unnecessary comparisons.
- LPS Array: Longest proper prefix suffix
- Pattern Preprocessing: Build LPS table
- Efficient Search: O(n) time complexity
- Applications: Pattern matching, text search
// KMP String Matching in Dart
List<int> buildLPS(String pattern) {
int m = pattern.length;
List<int> lps = List.filled(m, 0);
int len = 0;
int i = 1;
while (i < m) {
if (pattern[i] == pattern[len]) {
lps[i++] = ++len;
} else if (len > 0) {
len = lps[len - 1];
} else {
lps[i++] = 0;
}
}
return lps;
}
List<int> kmpSearch(String text, String pattern) {
List<int> positions = [];
List<int> lps = buildLPS(pattern);
int n = text.length;
int m = pattern.length;
int i = 0, j = 0;
while (i < n) {
if (text[i] == pattern[j]) {
i++;
j++;
}
if (j == m) {
positions.add(i - j);
j = lps[j - 1];
} else if (i < n && text[i] != pattern[j]) {
if (j > 0) {
j = lps[j - 1];
} else {
i++;
}
}
}
return positions;
}
void main() {
String text = 'AABAACAADAABAABA';
String pat = 'AABA';
List<int> pos = kmpSearch(text, pat);
print('Pattern found at: ${pos.join(' ')}');
}N-Queens is a classic backtracking problem that places N queens on an N×N chessboard where no two queens attack each other.
- Backtracking: Systematic trial and error
- Column Placement: One queen per column
- Safety Check: Row and diagonal conflicts
- Solution Counting: Total valid configurations
// N-Queens in Dart
class NQueens {
int n;
List<List<int>> board;
int solutions = 0;
NQueens(this.n) : board = List.generate(n, (_) => List.filled(n, 0));
bool isSafe(int row, int col) {
for (int j = 0; j < col; j++) {
if (board[row][j] == 1) return false;
}
for (int i = row, j = col; i >= 0 && j >= 0; i--, j--) {
if (board[i][j] == 1) return false;
}
for (int i = row, j = col; i < n && j >= 0; i++, j--) {
if (board[i][j] == 1) return false;
}
return true;
}
void solve(int col) {
if (col == n) {
solutions++;
if (solutions == 1) {
for (var row in board) {
print(row.map((x) => x == 1 ? 'Q' : '.').join(' '));
}
}
return;
}
for (int row = 0; row < n; row++) {
if (isSafe(row, col)) {
board[row][col] = 1;
solve(col + 1);
board[row][col] = 0;
}
}
}
void run() {
solve(0);
print('Total solutions: $solutions');
}
}
void main() {
NQueens q = NQueens(8);
q.run();
}LRU Cache (Least Recently Used) maintains a fixed-size cache, evicting the least recently used items when capacity is exceeded.
- LinkedHashMap: Maintains insertion order
- Get Operation: Access moves to most recent
- Put Operation: Update or add with eviction
- Cache Management: Capacity and eviction policy
// LRU Cache in Dart
class LRUCache<K, V> {
int capacity;
LinkedHashMap<K, V> cache;
LRUCache(this.capacity) : cache = LinkedHashMap<K, V>();
V? get(K key) {
if (!cache.containsKey(key)) return null;
V value = cache.remove(key)!;
cache[key] = value;
return value;
}
void put(K key, V value) {
if (cache.containsKey(key)) {
cache.remove(key);
}
if (cache.length == capacity) {
cache.remove(cache.keys.first);
}
cache[key] = value;
}
}
void main() {
LRUCache<int, int> lru = LRUCache<int, int>(2);
lru.put(1, 10);
lru.put(2, 20);
print(lru.get(1));
lru.put(3, 30);
print(lru.get(2));
print(lru.get(3));
}Topological Sort orders vertices in a directed acyclic graph (DAG) such that for every edge u→v, u comes before v.
- Kahn's Algorithm: Queue-based approach
- Indegree Tracking: Count incoming edges
- Applications: Task scheduling, dependency resolution
- Cycle Detection: DAG verification
// Graph - Topological Sort in Dart
List<int> topoSort(int v, List<List<int>> adj) {
List<int> inDegree = List.filled(v, 0);
for (int u = 0; u < v; u++) {
for (int node in adj[u]) {
inDegree[node]++;
}
}
List<int> queue = [];
for (int i = 0; i < v; i++) {
if (inDegree[i] == 0) queue.add(i);
}
List<int> order = [];
while (queue.isNotEmpty) {
int u = queue.removeAt(0);
order.add(u);
for (int node in adj[u]) {
if (--inDegree[node] == 0) {
queue.add(node);
}
}
}
return order.length == v ? order : [];
}
void main() {
int v = 6;
List<List<int>> adj = List.generate(v, (_) => []);
adj[5].add(2);
adj[5].add(0);
adj[4].add(0);
adj[4].add(1);
adj[2].add(3);
adj[3].add(1);
List<int> order = topoSort(v, adj);
print('Topological Order: ${order.join(' ')}');
}Bit Manipulation uses bitwise operations for efficient programming, including checking, setting, and toggling bits.
- Bitwise Operators: &, |, ^, ~, <<, >>
- Bit Checking: Test if bit is set
- Counting Bits: Efficient population count
- Power of 2: Check using bit magic
// Bit Manipulation in Dart
bool isBitSet(int n, int p) => (n >> p) & 1 == 1;
int setBit(int n, int p) => n | (1 << p);
int clearBit(int n, int p) => n & ~(1 << p);
int toggleBit(int n, int p) => n ^ (1 << p);
int countBits(int n) {
int count = 0;
while (n > 0) {
n &= n - 1;
count++;
}
return count;
}
bool isPowerOf2(int n) => n > 0 && (n & (n - 1)) == 0;
// Find unique (all others appear twice)
int findUnique(List<int> arr) {
int result = 0;
for (int x in arr) result ^= x;
return result;
}
void main() {
int n = 0b10110100;
print('Number: $n');
print('Bit 2 set? ${isBitSet(n, 2)}');
print('Set bit 0: ${setBit(n, 0)}');
print('Clear bit 4: ${clearBit(n, 4)}');
print('Toggle bit 7: ${toggleBit(n, 7)}');
print('Count bits: ${countBits(n)}');
print('isPow2(16): ${isPowerOf2(16)}');
List<int> arr = [2, 3, 5, 4, 5, 3, 4];
print('Unique: ${findUnique(arr)}');
}Number Theory includes algorithms for GCD, LCM, prime numbers, sieve, and modular exponentiation.
- GCD/LCM: Euclidean algorithm
- Prime Detection: Trial division, sieve
- Modular Arithmetic: Modular exponentiation
- Applications: Cryptography, number theory
// Number Theory in Dart
int gcd(int a, int b) => b == 0 ? a : gcd(b, a % b);
int lcm(int a, int b) => a ~/ gcd(a, b) * b;
bool isPrime(int n) {
if (n < 2) return false;
for (int i = 2; i * i <= n; i++) {
if (n % i == 0) return false;
}
return true;
}
List<int> sieve(int limit) {
List<bool> notPrime = List.filled(limit + 1, false);
if (limit >= 0) notPrime[0] = true;
if (limit >= 1) notPrime[1] = true;
for (int i = 2; i * i <= limit; i++) {
if (!notPrime[i]) {
for (int j = i * i; j <= limit; j += i) {
notPrime[j] = true;
}
}
}
List<int> primes = [];
for (int i = 2; i <= limit; i++) {
if (!notPrime[i]) primes.add(i);
}
return primes;
}
int modPow(int base, int exp, int mod) {
int result = 1;
base %= mod;
while (exp > 0) {
if (exp.isOdd) result = (result * base) % mod;
base = (base * base) % mod;
exp >>= 1;
}
return result;
}
void main() {
print('GCD(48,18)=${gcd(48, 18)}');
print('LCM(4,6)=${lcm(4, 6)}');
print('isPrime(17)=${isPrime(17)}');
List<int> primes = sieve(50);
print('Primes: ${primes.join(' ')}');
print('2^10 mod 1000 = ${modPow(2, 10, 1000)}');
}Records (tuples) provide a way to group multiple values into a single object with positional or named fields.
- Positional Records: Access by index
- Named Records: Access by field name
- Deconstruction: Pattern matching
- Return Values: Multiple return values
// Tuples in Dart (using Records)
void main() {
// Record (tuple)
var t1 = ('Alice', 25, 3.85);
print('${t1.$1} age=${t1.$2} gpa=${t1.$3}');
// Named record
var t2 = (name: 'Bob', age: 22, gpa: 3.62);
print('${t2.name} age=${t2.age} gpa=${t2.gpa}');
// Record deconstruction
var (n, a, g) = t1;
print('Deconstructed: $n, $a, $g');
// Named record in method return
(int min, int max) getMinMax(List<int> nums) {
int min = nums.reduce((a, b) => a < b ? a : b);
int max = nums.reduce((a, b) => a > b ? a : b);
return (min, max);
}
var result = getMinMax([5, 2, 8, 1, 9]);
print('Min=${result.min}, Max=${result.max}');
// Records in collections
List<(String, int)> students = [
('Alice', 90),
('Bob', 85),
('Carol', 92)
];
students.sort((a, b) => b.$2.compareTo(a.$2));
for (var (name, score) in students) {
print('$name: $score');
}
}Two Pointers technique efficiently solves array problems like container with most water and three-sum using two converging pointers.
- Container with Most Water: Maximize area
- Three-Sum: Find triplets summing to zero
- Pointer Movement: Conditional advancement
- Applications: Array problems, palindrome checking
// Two Pointers Technique in Dart
// Container with most water
int maxWater(List<int> height) {
int l = 0, r = height.length - 1, maxArea = 0;
while (l < r) {
int area = (height[l] < height[r] ? height[l] : height[r]) * (r - l);
if (area > maxArea) maxArea = area;
if (height[l] < height[r]) {
l++;
} else {
r--;
}
}
return maxArea;
}
// 3-sum
List<List<int>> threeSum(List<int> nums) {
nums.sort();
List<List<int>> result = [];
for (int i = 0; i < nums.length - 2; i++) {
if (i > 0 && nums[i] == nums[i - 1]) continue;
int l = i + 1, r = nums.length - 1;
while (l < r) {
int sum = nums[i] + nums[l] + nums[r];
if (sum == 0) {
result.add([nums[i], nums[l], nums[r]]);
while (l < r && nums[l] == nums[l + 1]) l++;
while (l < r && nums[r] == nums[r - 1]) r--;
l++;
r--;
} else if (sum < 0) {
l++;
} else {
r--;
}
}
}
return result;
}
void main() {
List<int> h = [1, 8, 6, 2, 5, 4, 8, 3, 7];
print('Max water: ${maxWater(h)}');
List<int> nums = [-1, 0, 1, 2, -1, -4];
for (var triplet in threeSum(nums)) {
print(triplet.join(' '));
}
}Backtracking is an algorithmic technique for solving problems by trying possibilities and undoing choices when they lead to dead ends.
- Subset Generation: Generate all subsets
- Permutations: All possible arrangements
- State Management: Track current state
- Applications: Combinatorial problems
// Backtracking in Dart
// Generate all subsets
List<List<int>> subsets(List<int> nums) {
List<List<int>> result = [];
_backtrackSubsets(nums, 0, [], result);
return result;
}
void _backtrackSubsets(List<int> nums, int idx, List<int> curr, List<List<int>> result) {
result.add(List.from(curr));
for (int i = idx; i < nums.length; i++) {
curr.add(nums[i]);
_backtrackSubsets(nums, i + 1, curr, result);
curr.removeLast();
}
}
// Generate permutations
List<List<int>> permute(List<int> nums) {
List<List<int>> result = [];
_permuteHelper(nums, 0, result);
return result;
}
void _permuteHelper(List<int> nums, int start, List<List<int>> result) {
if (start == nums.length) {
result.add(List.from(nums));
return;
}
for (int i = start; i < nums.length; i++) {
_swap(nums, start, i);
_permuteHelper(nums, start + 1, result);
_swap(nums, start, i);
}
}
void _swap(List<int> arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
void main() {
List<int> nums = [1, 2, 3];
var subsetsResult = subsets(nums);
print('Subsets (${subsetsResult.length}):');
for (var s in subsetsResult) {
print('[${s.join(' ')}]');
}
var perms = permute(nums);
print('Permutations (${perms.length}):');
for (var p in perms) {
print(p.join(' '));
}
}Greedy Algorithms make locally optimal choices at each step to find a global optimum for certain problems.
- Activity Selection: Maximize non-overlapping activities
- Fractional Knapsack: Fractional item selection
- Optimal Substructure: Greedy choice property
- Applications: Scheduling, resource allocation
// Greedy Algorithms in Dart
// Activity Selection
int activitySelection(List<(int, int)> activities) {
activities.sort((a, b) => a.$2.compareTo(b.$2));
int count = 1;
int lastEnd = activities[0].$2;
for (int i = 1; i < activities.length; i++) {
if (activities[i].$1 >= lastEnd) {
count++;
lastEnd = activities[i].$2;
}
}
return count;
}
// Fractional Knapsack
double fractionalKnapsack(List<(int, int)> items, int w) {
items.sort((a, b) => (b.$1 / b.$2).compareTo(a.$1 / a.$2));
double total = 0.0;
for (var (value, weight) in items) {
if (w >= weight) {
total += value;
w -= weight;
} else {
total += (value / weight) * w;
break;
}
}
return total;
}
void main() {
List<(int, int)> acts = [
(1, 3), (2, 5), (4, 6), (6, 8), (5, 7)
];
print('Max activities: ${activitySelection(acts)}');
List<(int, int)> items = [
(60, 10), (100, 20), (120, 30)
];
print('Max value (W=50): ${fractionalKnapsack(items, 50).toStringAsFixed(2)}');
}Events and Delegates implement the observer pattern, enabling loose coupling between components through event subscription and handling.
- Event Handlers: Function delegates
- Event Arguments: Carry event data
- Subscription Management: Add/remove handlers
- Observer Pattern: Push notifications
// Events and Delegates in Dart
// Delegate definition
typedef PriceChangedEventHandler = void Function(Stock sender, PriceChangedEventArgs e);
// Event args
class PriceChangedEventArgs {
final double oldPrice;
final double newPrice;
PriceChangedEventArgs(this.oldPrice, this.newPrice);
}
// Subject
class Stock {
String symbol;
double _price;
List<PriceChangedEventHandler> _handlers = [];
Stock(this.symbol, double price) : _price = price;
double get price => _price;
set price(double value) {
if (_price == value) return;
double old = _price;
_price = value;
_onPriceChanged(old, _price);
}
void addHandler(PriceChangedEventHandler handler) {
_handlers.add(handler);
}
void removeHandler(PriceChangedEventHandler handler) {
_handlers.remove(handler);
}
void _onPriceChanged(double oldPrice, double newPrice) {
var args = PriceChangedEventArgs(oldPrice, newPrice);
for (var handler in _handlers) {
handler(this, args);
}
}
}
// Observer
class Investor {
String name;
Investor(this.name);
void handlePriceChange(Stock sender, PriceChangedEventArgs e) {
print('$name notified: Price changed from $${e.oldPrice} to $${e.newPrice}');
}
}
void main() {
Stock apple = Stock('AAPL', 150);
Investor alice = Investor('Alice');
Investor bob = Investor('Bob');
apple.addHandler(alice.handlePriceChange);
apple.addHandler(bob.handlePriceChange);
apple.price = 155;
apple.price = 160;
}Disposable Pattern ensures proper cleanup of resources like file handles, database connections, and network sockets.
- Resource Acquisition: Open/initialize resources
- Resource Release: Close/cleanup properly
- Try-Finally: Guaranteed cleanup
- Using Pattern: RAII-style management
// Disposable and Resource Management
class FileHandler {
String filename;
FileHandler(this.filename) {
print('File opened: $filename');
}
void write(String data) {
print('Writing: $data');
}
void dispose() {
print('File closed: $filename');
}
}
void main() {
// Using try-finally for cleanup
FileHandler fh = FileHandler('test.txt');
try {
fh.write('Hello RAII!');
fh.write('Line 2');
} finally {
fh.dispose();
}
// Using a helper function
void withFile(String name, void Function(FileHandler) action) {
var handler = FileHandler(name);
try {
action(handler);
} finally {
handler.dispose();
}
}
withFile('test2.txt', (fh) {
fh.write('Data');
});
// Manual disposal
FileHandler fh3 = FileHandler('test3.txt');
try {
fh3.write('Data');
} finally {
fh3.dispose();
}
}Async and Parallel programming uses isolates for true parallelism and async/await for non-blocking operations.
- Isolates: Parallel execution units
- Async/Await: Non-blocking operations
- Task Parallelism: Parallel task execution
- Cancellation: Using Completer
// Async and Parallel Programming
import 'dart:async';
import 'dart:isolate';
void main() async {
// Parallel processing with isolates
List<int> numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
ReceivePort receivePort = ReceivePort();
await for (var message in receivePort) {
// Process results
}
// Async tasks
List<Future<int>> tasks = [];
for (int i = 0; i < 5; i++) {
int num = i;
tasks.add(Future.delayed(Duration(milliseconds: 100), () => num * num));
}
List<int> results = await Future.wait(tasks);
print('Async results: ${results.join(', ')}');
// Cancellation token (using Completer)
Completer<bool> cancelCompleter = Completer<bool>();
try {
await Future.delayed(Duration(milliseconds: 500), () {
if (!cancelCompleter.isCompleted) {
throw Exception('Cancelled!');
}
});
} catch (e) {
print('Cancelled!');
}
}Regular Expressions provide powerful pattern matching for text validation, search, and replacement operations.
- Pattern Matching: Validate input formats
- Search/Replace: Text manipulation
- Groups: Capture matched parts
- Named Groups: Readable pattern parts
// Regular Expressions in Dart
void main() {
// Email validation
RegExp emailRx = RegExp(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$');
List<String> emails = ['user@example.com', 'invalid-email', 'hello@world.org'];
for (String e in emails) {
print('$e: ${emailRx.hasMatch(e) ? 'Valid' : 'Invalid'}');
}
// Search and replace
String text = 'The quick brown fox jumps over the lazy dog';
String replaced = text.replaceAllMapped(RegExp(r'w{4}'), (match) => '****');
print('Replaced: $replaced');
// Find all matches
String data = 'Price: $100, Discount: $20, Total: $80';
RegExp numRx = RegExp(r'$(d+)');
Iterable<Match> matches = numRx.allMatches(data);
print('Numbers found: ');
for (var m in matches) {
print('${m.group(1)} ');
}
print('');
// Named groups
RegExp dateRx = RegExp(r'(?<year>d{4})-(?<month>d{2})-(?<day>d{2})');
Match? dateMatch = dateRx.firstMatch('2024-01-15');
if (dateMatch != null) {
print('Year: ${dateMatch.namedGroup('year')}');
print('Month: ${dateMatch.namedGroup('month')}');
print('Day: ${dateMatch.namedGroup('day')}');
}
// Capture groups
RegExp wordRx = RegExp(r'(w+)s+(w+)');
Match? match = wordRx.firstMatch('Hello World');
if (match != null) {
print('Word 1: ${match.group(1)}');
print('Word 2: ${match.group(2)}');
}
}Annotations add metadata to code. Reflection enables runtime type inspection and dynamic method invocation.
- Custom Annotations: Metadata on classes/methods
- Mirrors: Reflection API
- Runtime Type Info: Inspect at runtime
- Dynamic Invocation: Call methods by name
// Annotations and Reflection in Dart
import 'dart:mirrors';
class Author {
final String name;
final String version;
const Author(this.name, {this.version = '1.0'});
}
@Author('John Doe')
class Calculator {
@Author('Jane Smith', version: '2.0')
int add(int a, int b) => a + b;
int multiply(int a, int b) => a * b;
}
void main() {
// Get class annotations
ClassMirror classMirror = reflectClass(Calculator);
var classAnnotations = classMirror.metadata;
for (var annotation in classAnnotations) {
if (annotation.reflectee is Author) {
var author = annotation.reflectee as Author;
print('Class Author: ${author.name} (v${author.version})');
}
}
// Get method annotations
InstanceMirror calcInstance = reflect(Calculator());
var instanceMirror = calcInstance.type;
for (var method in instanceMirror.declarations.values) {
if (method is MethodMirror) {
for (var annotation in method.metadata) {
if (annotation.reflectee is Author) {
var author = annotation.reflectee as Author;
print('Method Author: ${author.name} (v${author.version})');
}
}
}
}
// Reflection - invoke methods
Calculator calc = Calculator();
var addMethod = calcInstance.type.declarations[#add] as MethodMirror;
var result = reflect(calc).invoke(#add, [5, 3]);
print('Add(5,3) = ${result.reflectee}');
// Get all methods
print('
All methods:');
for (var declaration in instanceMirror.declarations.values) {
if (declaration is MethodMirror) {
print(' ${declaration.simpleName}');
}
}
// Dynamic invocation
var multMethod = calcInstance.type.declarations[#multiply] as MethodMirror;
var multResult = reflect(calc).invoke(#multiply, [4, 5]);
print('Multiply(4,5) = ${multResult.reflectee}');
}Observer Pattern enables one-to-many notification. Command Pattern encapsulates requests as objects for queuing and undo.
- Observer: Subject-observer relationship
- Command: Action encapsulation with undo
- Loose Coupling: Components remain independent
- Undo/Redo: Command history support
// Design Patterns - Observer and Command
// Observer Pattern
abstract class Observer {
void update(String eventName, int data);
}
class Subject {
List<Observer> _observers = [];
int _data = 0;
void subscribe(Observer o) {
_observers.add(o);
}
void unsubscribe(Observer o) {
_observers.remove(o);
}
int get data => _data;
set data(int value) {
_data = value;
_notify();
}
void _notify() {
for (var o in _observers) {
o.update('Data Changed', _data);
}
}
}
class ConsoleObserver implements Observer {
String name;
ConsoleObserver(this.name);
@override
void update(String eventName, int data) {
print('$name notified: $eventName = $data');
}
}
// Command Pattern
abstract class Command {
void execute();
void undo();
}
class Counter {
int _value = 0;
void increment(int n) { _value += n; }
void decrement(int n) { _value -= n; }
int get value => _value;
}
class IncrementCommand implements Command {
Counter counter;
int amount;
IncrementCommand(this.counter, this.amount);
@override
void execute() {
counter.increment(amount);
}
@override
void undo() {
counter.decrement(amount);
}
}
void main() {
// Observer
Subject subject = Subject();
subject.subscribe(ConsoleObserver('Observer1'));
subject.subscribe(ConsoleObserver('Observer2'));
subject.data = 42;
subject.data = 100;
// Command
Counter counter = Counter();
List<Command> history = [];
history.add(IncrementCommand(counter, 10));
history.add(IncrementCommand(counter, 5));
for (var cmd in history) cmd.execute();
print('Counter: ${counter.value}');
history.last.undo();
print('After undo: ${counter.value}');
}Functional Programming uses pure functions, immutable data, and function composition for declarative code.
- Higher-order Functions: Functions as parameters
- Function Composition: Combine functions
- Memoization: Cache function results
- Immutability: Immutable data structures
// Functional Programming in Dart
// Higher-order functions
List<T> mapList<T>(List<T> list, T Function(T) fn) {
return list.map(fn).toList();
}
List<T> filterList<T>(List<T> list, bool Function(T) pred) {
return list.where(pred).toList();
}
R reduceList<T, R>(List<T> list, R init, R Function(R, T) fn) {
return list.fold(init, fn);
}
// Function composition
Function compose(Function f, Function g) {
return (x) => f(g(x));
}
// Memoization
Function memoize(Function fn) {
Map cache = {};
return (x) {
if (!cache.containsKey(x)) {
cache[x] = fn(x);
}
return cache[x];
};
}
void main() {
List<int> nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var doubled = mapList(nums, (x) => x * 2);
print('Doubled: ${doubled.join(' ')}');
var evens = filterList(nums, (x) => x.isEven);
print('Evens: ${evens.join(' ')}');
int sum = reduceList(nums, 0, (acc, x) => acc + x);
print('Sum: $sum');
int addOne(int x) => x + 1;
int doubleIt(int x) => x * 2;
var addThenDouble = compose(doubleIt, addOne);
print('Compose(double, +1)(5) = ${addThenDouble(5)}');
// Memoized Fibonacci
int fib(int n) => n <= 1 ? n : fib(n - 1) + fib(n - 2);
var memoFib = memoize(fib);
print('fib(30) = ${memoFib(30)}');
}Advanced Generics provide type safety with constraints, bounded types, and generic interfaces for flexible code.
- Type Constraints:
extendskeyword - Generic Classes: Type parameter classes
- Generic Methods: Type-safe methods
- Repository Pattern: Generic data access
// Advanced Generics and Constraints in Dart
// Generic class with constraints
class Repository<T> {
List<T> _items = [];
void add(T item) {
_items.add(item);
}
T create() {
// Dart doesn't support new T() directly
// This is a workaround
throw UnsupportedError('Cannot create instance of T');
}
List<T> getAll() => _items;
}
class Entity {
int id;
String name;
Entity({this.id = 0, this.name = ''});
}
abstract class IRepository<T> {
void add(T item);
T? get(int id);
}
class GenericRepository<T> implements IRepository<T> {
Map<int, T> _items = {};
int _nextId = 1;
@override
void add(T item) {
_items[_nextId++] = item;
}
@override
T? get(int id) {
return _items[id];
}
}
void main() {
Repository<Entity> repo = Repository<Entity>();
Entity e1 = Entity(id: 1, name: 'Alice');
repo.add(e1);
Entity e2 = Entity(id: 2, name: 'Bob');
repo.add(e2);
for (var e in repo.getAll()) {
print(e.name);
}
GenericRepository<Entity> genericRepo = GenericRepository<Entity>();
genericRepo.add(Entity(id: 3, name: 'Carol'));
Entity? found = genericRepo.get(3);
print('Found: ${found?.name}');
}Matrix Operations include multiplication, transposition, rotation, and other linear algebra operations.
- Matrix Multiplication: Complex operation
- Transposition: Flip rows/columns
- Rotation: 90-degree rotation
- Applications: Graphics, physics, ML
// Matrix Operations in Dart
List<List<int>> multiply(List<List<int>> a, List<List<int>> b) {
int r = a.length, c = b[0].length, k = b.length;
List<List<int>> result = List.generate(r, (_) => List.filled(c, 0));
for (int i = 0; i < r; i++) {
for (int j = 0; j < c; j++) {
for (int p = 0; p < k; p++) {
result[i][j] += a[i][p] * b[p][j];
}
}
}
return result;
}
List<List<int>> transpose(List<List<int>> a) {
int r = a.length, c = a[0].length;
List<List<int>> result = List.generate(c, (_) => List.filled(r, 0));
for (int i = 0; i < r; i++) {
for (int j = 0; j < c; j++) {
result[j][i] = a[i][j];
}
}
return result;
}
void rotate90(List<List<int>> m) {
int n = m.length;
// Transpose
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
int temp = m[i][j];
m[i][j] = m[j][i];
m[j][i] = temp;
}
}
// Reverse each row
for (int i = 0; i < n; i++) {
m[i] = m[i].reversed.toList();
}
}
void printMatrix(List<List<int>> m) {
for (var row in m) {
print(row.join(' '));
}
}
void main() {
List<List<int>> a = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
List<List<int>> b = [
[9, 8, 7],
[6, 5, 4],
[3, 2, 1]
];
print('A*B:');
printMatrix(multiply(a, b));
print('T(A):');
printMatrix(transpose(a));
rotate90(a);
print('A rotated 90CW:');
printMatrix(a);
}Trapping Rain Water calculates how much water can be trapped between bars using two-pointer technique.
- Two-Pointer: Efficient O(n) solution
- Left/Right Max: Track maximum heights
- Water Volume: Sum of trapped water
- Applications: Terrain analysis
// Trapping Rain Water in Dart
int trap(List<int> height) {
int l = 0, r = height.length - 1;
int leftMax = 0, rightMax = 0, water = 0;
while (l < r) {
if (height[l] < height[r]) {
if (height[l] >= leftMax) {
leftMax = height[l];
} else {
water += leftMax - height[l];
}
l++;
} else {
if (height[r] >= rightMax) {
rightMax = height[r];
} else {
water += rightMax - height[r];
}
r--;
}
}
return water;
}
void main() {
List<int> h1 = [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1];
print('Water trapped: ${trap(h1)}');
List<int> h2 = [4, 2, 0, 3, 2, 5];
print('Water trapped: ${trap(h2)}');
}LIS finds the longest subsequence where elements are in increasing order using DP or binary search.
- DP Solution: O(n²) time, O(n) space
- Binary Search: O(n log n) time
- Tail Tracking: Maintain increasing tails
- Applications: Sequencing, bioinformatics
// Longest Increasing Subsequence in Dart
// DP O(n^2)
int lisDP(List<int> arr) {
int n = arr.length;
List<int> dp = List.filled(n, 1);
for (int i = 1; i < n; i++) {
for (int j = 0; j < i; j++) {
if (arr[j] < arr[i] && dp[j] + 1 > dp[i]) {
dp[i] = dp[j] + 1;
}
}
}
return dp.reduce((a, b) => a > b ? a : b);
}
// Binary Search O(n log n)
int lisBS(List<int> arr) {
List<int> tails = [];
for (int x in arr) {
int idx = tails.lastIndexWhere((v) => v >= x) + 1;
if (idx == tails.length) {
tails.add(x);
} else {
tails[idx] = x;
}
}
return tails.length;
}
void main() {
List<int> arr = [10, 9, 2, 5, 3, 7, 101, 18];
print('LIS (DP): ${lisDP(arr)}');
print('LIS (BS): ${lisBS(arr)}');
}Bellman-Ford finds shortest paths in weighted graphs, handling negative edges and detecting negative cycles.
- Negative Edges: Handles negative weights
- Edge Relaxation: V-1 iterations
- Cycle Detection: Identify negative cycles
- Applications: Routing, network protocols
// Bellman-Ford in Dart
class Edge {
int u, v, w;
Edge(this.u, this.v, this.w);
}
void bellmanFord(List<Edge> edges, int v, int src) {
List<int> dist = List.filled(v, 9223372036854775807);
dist[src] = 0;
for (int i = 1; i < v; i++) {
for (var e in edges) {
if (dist[e.u] != 9223372036854775807 &&
dist[e.u] + e.w < dist[e.v]) {
dist[e.v] = dist[e.u] + e.w;
}
}
}
// Check negative cycle
for (var e in edges) {
if (dist[e.u] != 9223372036854775807 &&
dist[e.u] + e.w < dist[e.v]) {
print('Negative cycle detected!');
return;
}
}
print('Distances from $src:');
for (int i = 0; i < v; i++) {
print(' $i: ${dist[i] == 9223372036854775807 ? -1 : dist[i]}');
}
}
void main() {
int v = 5;
List<Edge> edges = [
Edge(0, 1, -1),
Edge(0, 2, 4),
Edge(1, 2, 3),
Edge(1, 3, 2),
Edge(1, 4, 2),
Edge(3, 2, 5),
Edge(3, 1, 1),
Edge(4, 3, -3)
];
bellmanFord(edges, v, 0);
}Floyd-Warshall finds all-pairs shortest paths in a weighted graph using dynamic programming in O(V³) time.
- All-Pairs Shortest Path: Between all vertices
- DP Approach: Intermediate vertex iteration
- Negative Edges: Handles without negative cycles
- Applications: Routing, transitive closure
// Floyd-Warshall in Dart
void floydWarshall(List<List<int>> dist) {
int v = dist.length;
for (int k = 0; k < v; k++) {
for (int i = 0; i < v; i++) {
for (int j = 0; j < v; j++) {
if (dist[i][k] != 9223372036854775807 &&
dist[k][j] != 9223372036854775807) {
int newDist = dist[i][k] + dist[k][j];
if (newDist < dist[i][j]) {
dist[i][j] = newDist;
}
}
}
}
}
print('All-Pairs Shortest Paths:');
for (int i = 0; i < v; i++) {
for (int j = 0; j < v; j++) {
print(dist[i][j] == 9223372036854775807 ? 'INF ' : '${dist[i][j]} ');
}
print('');
}
}
void main() {
const int INF = 9223372036854775807;
List<List<int>> graph = [
[0, 3, INF, 7],
[8, 0, 2, INF],
[5, INF, 0, 1],
[2, INF, INF, 0]
];
floydWarshall(graph);
}Kruskal's Algorithm finds a Minimum Spanning Tree by sorting edges and using union-find to add them without cycles.
- Edge Sorting: Sort by weight
- Union-Find: Cycle detection
- MST Construction: Build spanning tree
- Applications: Network design, clustering
// Kruskal's MST in Dart
class EdgeK {
int u, v, w;
EdgeK(this.u, this.v, this.w);
}
class DSU {
List<int> parent, rank;
DSU(int n)
: parent = List.generate(n, (i) => i),
rank = List.filled(n, 0);
int find(int x) {
if (parent[x] != x) {
parent[x] = find(parent[x]);
}
return parent[x];
}
bool unite(int x, int y) {
int px = find(x);
int py = find(y);
if (px == py) return false;
if (rank[px] < rank[py]) {
int temp = px;
px = py;
py = temp;
}
parent[py] = px;
if (rank[px] == rank[py]) rank[px]++;
return true;
}
}
void main() {
int v = 4;
List<EdgeK> edges = [
EdgeK(0, 1, 10),
EdgeK(0, 2, 6),
EdgeK(0, 3, 5),
EdgeK(1, 3, 15),
EdgeK(2, 3, 4)
];
edges.sort((a, b) => a.w.compareTo(b.w));
DSU dsu = DSU(v);
int cost = 0;
print('MST Edges:');
for (var e in edges) {
if (dsu.unite(e.u, e.v)) {
print('${e.u} -- ${e.v} (weight ${e.w})');
cost += e.w;
}
}
print('MST Cost: $cost');
}String Algorithms include longest palindrome, anagram checking, and grouping anagrams for text processing.
- Longest Palindrome: Expand around center
- Anagram Check: Frequency counting
- Group Anagrams: Sort strings as keys
- Applications: Text analysis, NLP
// String Algorithms in Dart
// Longest palindromic substring
String longestPalindrome(String s) {
int n = s.length, start = 0, maxLen = 1;
void expand(int l, int r) {
while (l >= 0 && r < n && s[l] == s[r]) {
l--;
r++;
}
if (r - l - 1 > maxLen) {
maxLen = r - l - 1;
start = l + 1;
}
}
for (int i = 0; i < n; i++) {
expand(i, i);
expand(i, i + 1);
}
return s.substring(start, start + maxLen);
}
// Check anagram
bool isAnagram(String s1, String s2) {
if (s1.length != s2.length) return false;
Map<String, int> freq = {};
for (String c in s1.split('')) {
freq[c] = (freq[c] ?? 0) + 1;
}
for (String c in s2.split('')) {
if (!freq.containsKey(c)) return false;
freq[c] = freq[c]! - 1;
if (freq[c]! < 0) return false;
}
return true;
}
// Group anagrams
List<List<String>> groupAnagrams(List<String> words) {
Map<String, List<String>> map = {};
for (String w in words) {
String key = w.split('')..sort()..join();
map.putIfAbsent(key, () => []).add(w);
}
return map.values.toList();
}
void main() {
print(longestPalindrome('babad'));
print(isAnagram('listen', 'silent'));
List<String> words = ['eat', 'tea', 'tan', 'ate', 'nat', 'bat'];
for (var group in groupAnagrams(words)) {
print(group.join(' '));
}
}Coin Change and Subset Sum are classic DP problems dealing with combinations and sums of values.
- Minimum Coins: Minimum coins for amount
- Count Ways: Number of combinations
- Subset Sum: Check if sum is possible
- Applications: Financial, resource allocation
// Coin Change and Subset Sum in Dart
// Minimum coins
int coinChange(List<int> coins, int amount) {
List<int> dp = List.filled(amount + 1, 9223372036854775807);
dp[0] = 0;
for (int i = 1; i <= amount; i++) {
for (int c in coins) {
if (c <= i && dp[i - c] != 9223372036854775807) {
dp[i] = dp[i] < dp[i - c] + 1 ? dp[i] : dp[i - c] + 1;
}
}
}
return dp[amount] == 9223372036854775807 ? -1 : dp[amount];
}
// Count ways
int countWays(List<int> coins, int amount) {
List<int> dp = List.filled(amount + 1, 0);
dp[0] = 1;
for (int c in coins) {
for (int i = c; i <= amount; i++) {
dp[i] += dp[i - c];
}
}
return dp[amount];
}
// Subset sum
bool subsetSum(List<int> arr, int target) {
int n = arr.length;
List<List<bool>> dp = List.generate(n + 1, (_) => List.filled(target + 1, false));
for (int i = 0; i <= n; i++) dp[i][0] = true;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= target; j++) {
dp[i][j] = dp[i - 1][j];
if (arr[i - 1] <= j) {
dp[i][j] = dp[i][j] || dp[i - 1][j - arr[i - 1]];
}
}
}
return dp[n][target];
}
void main() {
List<int> coins = [1, 5, 6, 9];
print('Min coins for 11: ${coinChange(coins, 11)}');
print('Ways for 10: ${countWays(coins, 10)}');
List<int> arr = [3, 34, 4, 12, 5, 2];
print('Subset sum 9: ${subsetSum(arr, 9)}');
print('Subset sum 30: ${subsetSum(arr, 30)}');
}Monotonic Stack maintains elements in sorted order for problems like next greater element and largest rectangle in histogram.
- Next Greater Element: Find next greater in array
- Largest Rectangle: Max area in histogram
- Stack Maintenance: Keep monotonic property
- Applications: Data analysis, pattern finding
// Monotonic Stack Problems
// Next Greater Element
List<int> nextGreater(List<int> arr) {
int n = arr.length;
List<int> result = List.filled(n, -1);
List<int> stack = [];
for (int i = 0; i < n; i++) {
while (stack.isNotEmpty && arr[stack.last] < arr[i]) {
result[stack.removeLast()] = arr[i];
}
stack.add(i);
}
return result;
}
// Largest Rectangle in Histogram
int largestRect(List<int> heights) {
List<int> stack = [];
int maxArea = 0;
List<int> h = [...heights, 0];
for (int i = 0; i < h.length; i++) {
while (stack.isNotEmpty && h[stack.last] > h[i]) {
int height = h[stack.removeLast()];
int width = stack.isEmpty ? i : i - stack.last - 1;
int area = height * width;
if (area > maxArea) maxArea = area;
}
stack.add(i);
}
return maxArea;
}
void main() {
List<int> arr = [4, 5, 2, 10, 8];
List<int> ng = nextGreater(arr);
print('Next Greater: ${ng.join(' ')}');
List<int> h = [2, 1, 5, 6, 2, 3];
print('Largest Rect: ${largestRect(h)}');
}Binary Search Variants handle rotated arrays, peak finding, and finding first/last occurrences of elements.
- Rotated Array Search: Binary search in rotated sorted
- Peak Finding: Find any peak element
- First/Last Position: Range queries
- Applications: Data structures, algorithms
// Binary Search Variants in Dart
// Search in rotated sorted array
int searchRotated(List<int> arr, int target) {
int l = 0, r = arr.length - 1;
while (l <= r) {
int mid = (l + r) ~/ 2;
if (arr[mid] == target) return mid;
if (arr[l] <= arr[mid]) {
if (target >= arr[l] && target < arr[mid]) {
r = mid - 1;
} else {
l = mid + 1;
}
} else {
if (target > arr[mid] && target <= arr[r]) {
l = mid + 1;
} else {
r = mid - 1;
}
}
}
return -1;
}
// Find peak element
int findPeak(List<int> arr) {
int l = 0, r = arr.length - 1;
while (l < r) {
int mid = (l + r) ~/ 2;
if (arr[mid] > arr[mid + 1]) {
r = mid;
} else {
l = mid + 1;
}
}
return l;
}
// First and last position
(int, int) firstLast(List<int> arr, int target) {
int first = arr.indexOf(target);
if (first == -1) return (-1, -1);
int last = arr.lastIndexOf(target);
return (first, last);
}
void main() {
List<int> rotated = [4, 5, 6, 7, 0, 1, 2];
print('Search 0: ${searchRotated(rotated, 0)}');
List<int> arr = [1, 2, 3, 1];
print('Peak index: ${findPeak(arr)}');
List<int> v = [5, 7, 7, 8, 8, 10];
var (f, l) = firstLast(v, 8);
print('First,Last of 8: $f,$l');
}Product of Array Except Self calculates the product of all elements except the current one in O(n) time.
- Left and Right Passes: Track products
- In-Place Solution: No extra space
- O(n) Time: Single pass each direction
- Applications: Array calculations
// Product of Array Except Self in Dart
List<int> productExceptSelf(List<int> nums) {
int n = nums.length;
List<int> result = List.filled(n, 1);
// Left pass
for (int i = 1; i < n; i++) {
result[i] = result[i - 1] * nums[i - 1];
}
// Right pass
int right = 1;
for (int i = n - 1; i >= 0; i--) {
result[i] *= right;
right *= nums[i];
}
return result;
}
void main() {
List<int> nums = [1, 2, 3, 4];
List<int> res = productExceptSelf(nums);
print('Output: ${res.join(' ')}');
}Flood Fill and Number of Islands are graph traversal problems using DFS/BFS for connected component detection.
- Flood Fill: Color replacement algorithm
- Number of Islands: Count connected land cells
- DFS Traversal: Depth-first on grid
- Applications: Image processing, maps
// Flood Fill and Number of Islands
// Flood Fill
void floodFill(List<List<int>> img, int r, int c, int oldColor, int newColor) {
if (r < 0 || r >= img.length || c < 0 || c >= img[0].length) return;
if (img[r][c] != oldColor || img[r][c] == newColor) return;
img[r][c] = newColor;
floodFill(img, r + 1, c, oldColor, newColor);
floodFill(img, r - 1, c, oldColor, newColor);
floodFill(img, r, c + 1, oldColor, newColor);
floodFill(img, r, c - 1, oldColor, newColor);
}
// Number of Islands
void dfs(List<List<String>> grid, int r, int c) {
if (r < 0 || r >= grid.length || c < 0 || c >= grid[0].length) return;
if (grid[r][c] == '0') return;
grid[r][c] = '0';
dfs(grid, r + 1, c);
dfs(grid, r - 1, c);
dfs(grid, r, c + 1);
dfs(grid, r, c - 1);
}
int numIslands(List<List<String>> grid) {
int count = 0;
for (int r = 0; r < grid.length; r++) {
for (int c = 0; c < grid[0].length; c++) {
if (grid[r][c] == '1') {
dfs(grid, r, c);
count++;
}
}
}
return count;
}
void main() {
List<List<String>> grid = [
['1', '1', '0', '0'],
['1', '1', '0', '0'],
['0', '0', '1', '0'],
['0', '0', '0', '1']
];
print('Islands: ${numIslands(grid)}');
}Word Search finds words in a 2D grid using DFS with backtracking, checking all directions from each cell.
- DFS Traversal: Explore adjacent cells
- Backtracking: Mark visited cells
- Efficient Search: Early termination
- Applications: Word games, puzzles
// Word Search in Grid
bool dfs(List<List<String>> board, String word, int r, int c, int idx) {
if (idx == word.length) return true;
if (r < 0 || r >= board.length || c < 0 || c >= board[0].length) return false;
if (board[r][c] != word[idx]) return false;
String tmp = board[r][c];
board[r][c] = '#';
bool found = dfs(board, word, r + 1, c, idx + 1) ||
dfs(board, word, r - 1, c, idx + 1) ||
dfs(board, word, r, c + 1, idx + 1) ||
dfs(board, word, r, c - 1, idx + 1);
board[r][c] = tmp;
return found;
}
bool wordSearch(List<List<String>> board, String word) {
for (int r = 0; r < board.length; r++) {
for (int c = 0; c < board[0].length; c++) {
if (dfs(board, word, r, c, 0)) return true;
}
}
return false;
}
void main() {
List<List<String>> board = [
['A', 'B', 'C', 'E'],
['S', 'F', 'C', 'S'],
['A', 'D', 'E', 'E']
];
print(wordSearch(board, 'ABCCED'));
print(wordSearch(board, 'SEE'));
print(wordSearch(board, 'ABCB'));
}Spiral Matrix traverses a 2D matrix in spiral order, using four boundaries that shrink as we progress.
- Boundary Tracking: Top, bottom, left, right
- Direction Changes: Moving clockwise
- Layer-by-Layer: Process outer to inner
- Applications: Matrix manipulation
// Spiral Matrix in Dart
List<int> spiralOrder(List<List<int>> matrix) {
List<int> result = [];
int top = 0, bottom = matrix.length - 1;
int left = 0, right = matrix[0].length - 1;
while (top <= bottom && left <= right) {
for (int i = left; i <= right; i++) result.add(matrix[top][i]);
top++;
for (int i = top; i <= bottom; i++) result.add(matrix[i][right]);
right--;
if (top <= bottom) {
for (int i = right; i >= left; i--) result.add(matrix[bottom][i]);
bottom--;
}
if (left <= right) {
for (int i = bottom; i >= top; i--) result.add(matrix[i][left]);
left++;
}
}
return result;
}
void main() {
List<List<int>> mat = [
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15, 16]
];
print('Spiral: ${spiralOrder(mat).join(' ')}');
}Sudoku Solver uses backtracking to fill empty cells with valid numbers, checking row, column, and box constraints.
- Constraint Checking: Row, column, box
- Backtracking: Try numbers recursively
- Efficient Search: Early validation
- Applications: Puzzle solving, CSP
// Sudoku Solver in Dart
bool isValid(List<List<String>> board, int r, int c, String num) {
for (int i = 0; i < 9; i++) {
if (board[r][i] == num) return false;
if (board[i][c] == num) return false;
int boxR = 3 * (r ~/ 3) + i ~/ 3;
int boxC = 3 * (c ~/ 3) + i % 3;
if (board[boxR][boxC] == num) return false;
}
return true;
}
bool solve(List<List<String>> board) {
for (int r = 0; r < 9; r++) {
for (int c = 0; c < 9; c++) {
if (board[r][c] == '.') {
for (String num in '123456789'.split('')) {
if (isValid(board, r, c, num)) {
board[r][c] = num;
if (solve(board)) return true;
board[r][c] = '.';
}
}
return false;
}
}
}
return true;
}
void main() {
List<List<String>> board = [
['5','3','.','.','7','.','.','.','.'],
['6','.','.','1','9','5','.','.','.'],
['.','9','8','.','.','.','.','6','.'],
['8','.','.','.','6','.','.','.','3'],
['4','.','.','8','.','3','.','.','1'],
['7','.','.','.','2','.','.','.','6'],
['.','6','.','.','.','.','2','8','.'],
['.','.','.','4','1','9','.','.','5'],
['.','.','.','.','8','.','.','7','9']
];
solve(board);
for (var row in board) {
print(row.join(' '));
}
}Priority Queue with Custom Comparator allows ordering elements based on custom criteria using Comparator interface.
- Custom Comparator: Define ordering logic
- Task Scheduling: Priority-based execution
- Multiple Criteria: Compare by multiple fields
- Applications: Scheduling, event handling
// Priority Queue Custom Comparator
import 'dart:collection';
class Task {
String name;
int priority;
int deadline;
Task(this.name, this.priority, this.deadline);
}
class TaskComparer implements Comparator<Task> {
@override
int compare(Task a, Task b) {
if (a.priority != b.priority) {
return b.priority.compareTo(a.priority);
}
return a.deadline.compareTo(b.deadline);
}
}
void main() {
PriorityQueue<Task> taskQueue = PriorityQueue<Task>(TaskComparer());
taskQueue.add(Task('Write Report', 3, 5));
taskQueue.add(Task('Fix Bug', 5, 2));
taskQueue.add(Task('Code Review', 4, 3));
taskQueue.add(Task('Deploy Feature', 5, 1));
taskQueue.add(Task('Write Tests', 3, 4));
print('Task execution order:');
while (taskQueue.isNotEmpty) {
Task t = taskQueue.removeFirst();
print(' [P=${t.priority},D=${t.deadline}] ${t.name}');
}
}Prim's Algorithm finds a Minimum Spanning Tree by growing the tree one edge at a time using a priority queue.
- Greedy Approach: Add minimum edge
- Priority Queue: Efficient edge selection
- Tree Growth: Expand from start vertex
- Applications: Network design, clustering
// Graph - Prim's MST in Dart
int primMST(List<List<(int, int)>> graph, int v) {
List<int> key = List.filled(v, 9223372036854775807);
List<bool> inMST = List.filled(v, false);
PriorityQueue<(int, int)> pq = PriorityQueue<(int, int)>(
(a, b) => a.$1.compareTo(b.$1)
);
key[0] = 0;
pq.add((0, 0));
int totalCost = 0;
while (pq.isNotEmpty) {
var (weight, u) = pq.removeFirst();
if (inMST[u]) continue;
inMST[u] = true;
totalCost += weight;
for (var (w, v) in graph[u]) {
if (!inMST[v] && w < key[v]) {
key[v] = w;
pq.add((w, v));
}
}
}
return totalCost;
}
void main() {
int v = 5;
List<List<(int, int)>> graph = List.generate(v, (_) => []);
void addEdge(int u, int v, int w) {
graph[u].add((w, v));
graph[v].add((w, u));
}
addEdge(0, 1, 2);
addEdge(0, 3, 6);
addEdge(1, 2, 3);
addEdge(1, 3, 8);
addEdge(1, 4, 5);
addEdge(2, 4, 7);
addEdge(3, 4, 9);
print('MST Cost (Prim's): ${primMST(graph, v)}');
}Custom Iterator Pattern enables creating custom iterable objects with specific iteration logic like ranges or sequences.
- Iterator Interface:
moveNext(),current - Iterable: Create custom sequences
- Lazy Evaluation: Generate on demand
- Applications: Custom collections, sequences
// Custom Iterator Pattern in Dart
// Custom Range iterator
class Range extends Iterable<int> {
final int start, end, step;
Range(this.start, this.end, [this.step = 1]);
@override
Iterator<int> get iterator => _RangeIterator(start, end, step);
}
class _RangeIterator implements Iterator<int> {
int _current;
final int end, step;
_RangeIterator(this._current, this.end, this.step);
@override
int get current => _current;
@override
bool moveNext() {
if (_current < end) {
_current += step;
return true;
}
return false;
}
}
// Custom Fibonacci generator
class Fibonacci extends Iterable<int> {
final int count;
Fibonacci(this.count);
@override
Iterator<int> get iterator => _FibonacciIterator(count);
}
class _FibonacciIterator implements Iterator<int> {
int _count, _index = 0;
int _current = 0;
int _a = 0, _b = 1;
_FibonacciIterator(this._count);
@override
int get current => _current;
@override
bool moveNext() {
if (_index >= _count) return false;
_current = _a;
int c = _a + _b;
_a = _b;
_b = c;
_index++;
return true;
}
}
void main() {
// Custom range
for (int x in Range(1, 11)) {
print('$x ');
}
print('');
for (int x in Range(0, 20, 2)) {
print('$x ');
}
print('');
// Custom fibonacci
for (int x in Fibonacci(10)) {
print('$x ');
}
print('');
// LINQ with custom iterator
var evens = Range(1, 21).where((x) => x.isEven);
print('Evens: ${evens.join(' ')}');
}Stack (LIFO) and Queue (FIFO) are fundamental data structures with custom implementations for specific needs.
- Stack Implementation: List-based push/pop
- Queue Implementation: ListQueue for efficiency
- Operations: Push, pop, enqueue, dequeue
- Applications: Various algorithms
// Stack and Queue Implementations
// Custom Stack
class MyStack<T> {
List<T> _items = [];
void push(T item) {
_items.add(item);
}
T pop() {
if (_items.isEmpty) throw StateError('Stack is empty');
return _items.removeLast();
}
T peek() {
if (_items.isEmpty) throw StateError('Stack is empty');
return _items.last;
}
bool get isEmpty => _items.isEmpty;
int get length => _items.length;
}
// Custom Queue
class MyQueue<T> {
ListQueue<T> _items = ListQueue<T>();
void enqueue(T item) {
_items.add(item);
}
T dequeue() {
if (_items.isEmpty) throw StateError('Queue is empty');
return _items.removeFirst();
}
T peek() {
if (_items.isEmpty) throw StateError('Queue is empty');
return _items.first;
}
bool get isEmpty => _items.isEmpty;
int get length => _items.length;
}
void main() {
// Custom Stack
MyStack<int> stack = MyStack<int>();
stack.push(10);
stack.push(20);
stack.push(30);
print('Stack top: ${stack.peek()}');
while (!stack.isEmpty) {
print('${stack.pop()} ');
}
print('');
// Custom Queue
MyQueue<int> queue = MyQueue<int>();
queue.enqueue(10);
queue.enqueue(20);
queue.enqueue(30);
print('Queue front: ${queue.peek()}');
while (!queue.isEmpty) {
print('${queue.dequeue()} ');
}
print('');
}Counting Sort and Radix Sort are non-comparison sorting algorithms efficient for specific input types.
- Counting Sort: O(n+k) for integers
- Radix Sort: Digit-by-digit sorting
- Linear Time: Efficient for certain data
- Applications: Integer sorting, strings
// Counting Sort and Radix Sort in Dart
void countingSort(List<int> arr) {
if (arr.isEmpty) return;
int maxVal = arr.reduce((a, b) => a > b ? a : b);
List<int> count = List.filled(maxVal + 1, 0);
for (int x in arr) count[x]++;
int idx = 0;
for (int i = 0; i <= maxVal; i++) {
while (count[i]-- > 0) {
arr[idx++] = i;
}
}
}
void countSortByDigit(List<int> arr, int exp) {
int n = arr.length;
List<int> output = List.filled(n, 0);
List<int> count = List.filled(10, 0);
for (int x in arr) {
count[(x ~/ exp) % 10]++;
}
for (int i = 1; i < 10; i++) {
count[i] += count[i - 1];
}
for (int i = n - 1; i >= 0; i--) {
int digit = (arr[i] ~/ exp) % 10;
output[count[digit] - 1] = arr[i];
count[digit]--;
}
for (int i = 0; i < n; i++) {
arr[i] = output[i];
}
}
void radixSort(List<int> arr) {
if (arr.isEmpty) return;
int maxVal = arr.reduce((a, b) => a > b ? a : b);
for (int exp = 1; maxVal ~/ exp > 0; exp *= 10) {
countSortByDigit(arr, exp);
}
}
void main() {
List<int> v1 = [4, 2, 2, 8, 3, 3, 1, 7, 5];
countingSort(v1);
print('Counting: ${v1.join(' ')}');
List<int> v2 = [170, 45, 75, 90, 802, 24, 2, 66];
radixSort(v2);
print('Radix: ${v2.join(' ')}');
}Cycle Detection identifies cycles in directed and undirected graphs using DFS recursion stack or union-find.
- Directed Graph: DFS with recursion stack
- Undirected Graph: Union-Find approach
- Back Edges: Indicate cycles
- Applications: Deadlock detection, validation
// Graph Cycle Detection in Dart
// Directed graph - DFS with recursion stack
bool dfsCycle(int v, List<List<int>> adj, List<bool> visited, List<bool> recStack) {
visited[v] = true;
recStack[v] = true;
for (int u in adj[v]) {
if (!visited[u] && dfsCycle(u, adj, visited, recStack)) {
return true;
} else if (recStack[u]) {
return true;
}
}
recStack[v] = false;
return false;
}
bool hasCycleDirected(int v, List<List<int>> adj) {
List<bool> visited = List.filled(v, false);
List<bool> recStack = List.filled(v, false);
for (int i = 0; i < v; i++) {
if (!visited[i] && dfsCycle(i, adj, visited, recStack)) {
return true;
}
}
return false;
}
// Undirected graph - Union Find
bool hasCycleUndirected(int v, List<(int, int)> edges) {
List<int> parent = List.generate(v, (i) => i);
int find(int x) {
if (parent[x] != x) {
parent[x] = find(parent[x]);
}
return parent[x];
}
for (var (u, v) in edges) {
int pu = find(u);
int pv = find(v);
if (pu == pv) return true;
parent[pu] = pv;
}
return false;
}
void main() {
int v = 4;
List<List<int>> adj = List.generate(v, (_) => []);
adj[0].add(1);
adj[1].add(2);
adj[2].add(3);
adj[3].add(1);
print('Directed cycle: ${hasCycleDirected(v, adj)}');
List<(int, int)> edges = [(0, 1), (1, 2), (2, 0)];
print('Undirected cycle: ${hasCycleUndirected(3, edges)}');
}Advanced Functional Programming includes group joins, select many, and complex aggregations with collections.
- Group Join: Left outer join operations
- Select Many: Flatten collections
- Group By: Group elements by key
- Aggregations: Complex data transformations
// Advanced Functional Programming
class Customer {
int id;
String name;
Customer(this.id, this.name);
}
class Order {
int customerId;
String product;
int quantity;
Order(this.customerId, this.product, this.quantity);
}
void main() {
List<Customer> customers = [
Customer(1, 'Alice'),
Customer(2, 'Bob'),
Customer(3, 'Carol')
];
List<Order> orders = [
Order(1, 'Laptop', 1),
Order(1, 'Mouse', 2),
Order(2, 'Keyboard', 1),
Order(2, 'Monitor', 3),
Order(2, 'Mouse', 1)
];
// GroupJoin (left outer join)
var customerOrders = customers.map((c) {
return (
name: c.name,
orders: orders.where((o) => o.customerId == c.id)
.map((o) => '${o.product} (x${o.quantity})')
.toList()
);
}).toList();
print('Customer Orders:');
for (var co in customerOrders) {
print('${co.name}: ${co.orders.join(', ')}');
}
// SelectMany (flatten)
var allOrderItems = orders.expand((o) => List.filled(o.quantity, o.product)).toList();
print('All items: ${allOrderItems.join(', ')}');
// Group by
var orderLookup = orders.fold<Map<int, List<Order>>>({}, (map, o) {
map.putIfAbsent(o.customerId, () => []).add(o);
return map;
});
orderLookup.forEach((id, orders) {
print('Customer $id has ${orders.length} orders');
});
// Aggregate
String allProducts = orders.map((o) => o.product).join(', ');
print('All products: $allProducts');
}Expression Evaluation uses stacks to evaluate RPN expressions and convert infix to postfix notation.
- RPN Evaluation: Postfix expression evaluation
- Infix to Postfix: Shunting-yard algorithm
- Operator Precedence: Handling precedence rules
- Applications: Calculators, compilers
// Expression Evaluation using Stack
// Evaluate Reverse Polish Notation
int evalRPN(List<String> tokens) {
List<int> stack = [];
for (String t in tokens) {
switch (t) {
case '+':
int b = stack.removeLast();
int a = stack.removeLast();
stack.add(a + b);
break;
case '-':
int b = stack.removeLast();
int a = stack.removeLast();
stack.add(a - b);
break;
case '*':
int b = stack.removeLast();
int a = stack.removeLast();
stack.add(a * b);
break;
case '/':
int b = stack.removeLast();
int a = stack.removeLast();
stack.add(a ~/ b);
break;
default:
stack.add(int.parse(t));
}
}
return stack.removeLast();
}
// Infix to Postfix
String infixToPostfix(String expr) {
List<String> ops = [];
List<String> result = [];
int precedence(String c) {
switch (c) {
case '+':
case '-':
return 1;
case '*':
case '/':
return 2;
default:
return 0;
}
}
for (String c in expr.split('')) {
if (c.contains(RegExp(r'[0-9]'))) {
result.add(c);
} else if (c == '(') {
ops.add(c);
} else if (c == ')') {
while (ops.last != '(') {
result.add(ops.removeLast());
}
ops.removeLast();
} else {
while (ops.isNotEmpty && precedence(ops.last) >= precedence(c)) {
result.add(ops.removeLast());
}
ops.add(c);
}
}
while (ops.isNotEmpty) {
result.add(ops.removeLast());
}
return result.join(' ');
}
void main() {
List<String> rpn = ['2', '1', '+', '3', '*'];
print('RPN eval: ${evalRPN(rpn)}');
print('Infix to Postfix: ${infixToPostfix('(2+3)*4')}');
}Strategy Pattern enables interchangeable algorithms. Template Method defines an algorithm skeleton with customizable steps.
- Strategy: Family of algorithms
- Template Method: Algorithm structure
- Flexibility: Dynamic algorithm selection
- Applications: Framework design, algorithms
// Design Patterns - Strategy and Template
// Strategy Pattern
abstract class SortStrategy {
void sort(List<int> data);
String get name;
}
class BubbleSortStrategy implements SortStrategy {
@override
String get name => 'Bubble Sort';
@override
void sort(List<int> data) {
int n = data.length;
for (int i = 0; i < n - 1; i++) {
bool swapped = false;
for (int j = 0; j < n - i - 1; j++) {
if (data[j] > data[j + 1]) {
int temp = data[j];
data[j] = data[j + 1];
data[j + 1] = temp;
swapped = true;
}
}
if (!swapped) break;
}
}
}
class BuiltInSortStrategy implements SortStrategy {
@override
String get name => 'Built-in Sort';
@override
void sort(List<int> data) {
data.sort();
}
}
class SortContext {
SortStrategy _strategy;
SortContext(this._strategy);
void setStrategy(SortStrategy strategy) {
_strategy = strategy;
}
void sort(List<int> data) {
print('Using: ${_strategy.name}');
_strategy.sort(data);
}
}
// Template Method Pattern
abstract class DataProcessor {
void process() {
loadData();
processData();
saveResult();
}
void loadData();
void processData();
void saveResult();
}
class CSVProcessor extends DataProcessor {
@override
void loadData() {
print('Loading CSV data...');
}
@override
void processData() {
print('Processing CSV data...');
}
@override
void saveResult() {
print('Saving CSV result...');
}
}
void main() {
List<int> data = [5, 3, 8, 1, 9, 2];
SortContext context = SortContext(BubbleSortStrategy());
context.sort(List.from(data));
print(data.join(' '));
context.setStrategy(BuiltInSortStrategy());
context.sort(List.from(data));
print(data.join(' '));
print('
Template Method:');
DataProcessor processor = CSVProcessor();
processor.process();
}Rabin-Karp uses rolling hashes for efficient string matching, ideal for multiple pattern search in text.
- Rolling Hash: Efficient hash updates
- Pattern Preprocessing: Compute pattern hash
- Average O(n): Efficient for long texts
- Applications: Plagiarism detection, DNA
// Rabin-Karp String Matching in Dart
List<int> rabinKarp(String text, String pattern) {
List<int> positions = [];
int n = text.length, m = pattern.length;
const int base = 31;
const int mod = 1000000009;
// Compute hash of pattern and first window
int patHash = 0, winHash = 0, power = 1;
for (int i = 0; i < m; i++) {
patHash = (patHash + (text.codeUnitAt(i) - 96) * power) % mod;
winHash = (winHash + (text.codeUnitAt(i) - 96) * power) % mod;
if (i < m - 1) power = (power * base) % mod;
}
for (int i = 0; i <= n - m; i++) {
if (patHash == winHash) {
if (text.substring(i, i + m) == pattern) {
positions.add(i);
}
}
if (i < n - m) {
winHash = (winHash - (text.codeUnitAt(i) - 96) + mod) % mod;
winHash = (winHash * (mod + 1 - base)) % mod;
winHash = (winHash + (text.codeUnitAt(i + m) - 96) * power) % mod;
}
}
return positions;
}
void main() {
String text = 'aabaacaadaabaaba';
String pattern = 'aaba';
List<int> pos = rabinKarp(text, pattern);
print('Rabin-Karp found at: ${pos.join(' ')}');
}Dynamic type in Dart allows runtime type flexibility, enabling dynamic objects and runtime type checking.
- Dynamic Type: Runtime type checking
- Dynamic Objects: Maps as dynamic structures
- Type Flexibility: Can hold any type
- Applications: Dynamic systems, JSON parsing
// Dynamic in Dart
void main() {
// dynamic type
dynamic value = 42;
print('Int: $value');
value = 'Hello, World!';
print('String: $value');
value = 3.14159;
print('Double: $value');
// Dynamic object with Map
Map<String, dynamic> person = {};
person['name'] = 'Alice';
person['age'] = 25;
print('Hello, I'm ${person['name']}');
// Dictionary to dynamic
Map<String, dynamic> dict = {
'Name': 'Bob',
'Age': 30
};
print('${dict['Name']} is ${dict['Age']} years old');
// Type checking
print('value is int: ${value is int}');
print('value is double: ${value is double}');
// Generic with type parameter
void printType<T>(T val) {
print('Type: ${T}, Value: $val');
}
printType<int>(42);
printType<String>('hello');
printType<double>(3.14);
}Mixins and Interfaces provide powerful code reuse and contract enforcement in Dart's type system.
- Mixins: Code reuse across hierarchies
- Interfaces: Contract definition
- Implementation: Multiple interface support
- Applications: Plugin architectures, frameworks
// Mixins and Interfaces
// Interface for printable
abstract class Printable {
void print();
}
// Interface for comparable
abstract class Comparable<T> {
int compareTo(T other);
}
// Mixin
mixin PrintableMixin {
void printWithHeader() {
print('=== Print Start ===');
print();
print('=== Print End ===');
}
}
// Implementation
class Point with PrintableMixin implements Printable, Comparable<Point> {
double x, y;
Point(this.x, this.y);
@override
void print() {
print('Point($x, $y)');
}
@override
int compareTo(Point other) {
double d1 = x * x + y * y;
double d2 = other.x * other.x + other.y * other.y;
return d1.compareTo(d2);
}
double get distance => x * x + y * y;
}
void main() {
Point p1 = Point(3, 4);
Point p2 = Point(1, 1);
Point p3 = Point(3, 4);
p1.print();
p1.printWithHeader();
print('p1 == p3: ${p1.compareTo(p3) == 0}');
print('p1 > p2: ${p1.compareTo(p2) > 0}');
print('p2 < p1: ${p2.compareTo(p1) < 0}');
// With functional methods
List<Point> points = [p1, p2, p3];
var sorted = points..sort((a, b) => a.distance.compareTo(b.distance));
print('Sorted by distance:');
for (var p in sorted) {
p.print();
}
}Producer-Consumer pattern coordinates multiple threads/tasks producing and consuming items from a bounded buffer.
- Bounded Buffer: Limited capacity queue
- Synchronization: Blocking when full/empty
- Thread Safety: Avoid race conditions
- Applications: Task queues, data pipelines
// Concurrency - Producer-Consumer
import 'dart:async';
class BoundedBuffer {
List<int> _buffer = [];
int _capacity;
Completer<void>? _notFull;
Completer<void>? _notEmpty;
BoundedBuffer(this._capacity) {
_notFull = Completer<void>()..complete();
_notEmpty = Completer<void>();
}
Future<void> produce(int item) async {
await _notFull!.future;
_notFull = Completer<void>();
_buffer.add(item);
print('Produced: $item | Buffer size: ${_buffer.length}');
_notEmpty!.complete();
}
Future<int> consume() async {
await _notEmpty!.future;
_notEmpty = Completer<void>();
int item = _buffer.removeAt(0);
print('Consumed: $item | Buffer size: ${_buffer.length}');
if (_buffer.length < _capacity) {
_notFull!.complete();
}
return item;
}
}
void main() async {
BoundedBuffer bb = BoundedBuffer(3);
// Producer
Future<void> producer() async {
for (int i = 1; i <= 6; i++) {
await bb.produce(i);
}
}
// Consumer
Future<void> consumer() async {
for (int i = 0; i < 6; i++) {
await bb.consume();
}
}
await Future.wait([producer(), consumer()]);
}Records provide lightweight data grouping. Pattern Matching enables powerful destructuring and type-based logic.
- Positional Records: Position-based access
- Named Records: Field-based access
- Pattern Matching: Switch statements
- Deconstruction: Extract record values
// Dart Features - Records and Pattern Matching
// Record
record Person(String name, int age);
// Record with methods
record Student(String name, int age, String major) {
void display() {
print('$name ($age) studies $major');
}
}
// Positional record with deconstruction
record Point(double x, double y) {
double get distance => Math.sqrt(x * x + y * y);
}
void main() {
// Record instantiation
Person p1 = Person('Alice', 25);
Person p2 = Person('Alice', 25);
Person p3 = Person(p1.name, p1.age + 1);
print('p1 == p2: ${p1 == p2}');
print('p1: Person(name: Alice, age: 25)');
print('p3: Person(name: Alice, age: 26)');
// Pattern matching
dynamic obj = 42;
if (obj is int && obj > 10) {
print('Medium int');
} else if (obj is int) {
print('Small int');
} else if (obj is String) {
print('String: $obj');
}
// Property pattern
if (p1 is Person && p1.name == 'Alice' && p1.age == 25) {
print('Matched Alice, age 25');
}
// Tuple pattern
var (x, y) = (10, 20);
switch ((x, y)) {
case (0, 0):
print('Origin');
case (10, 20):
print('Equal');
default:
print('Other');
}
}Advanced Dart Features for performance include typed data, slicing, pooling, and memory management techniques.
- Typed Data: Uint8List, ByteData
- Slicing: Efficient view of data
- Pooling: Object reuse patterns
- Memory Management: ReadOnly collections
// Advanced Dart - Performance and Memory
import 'dart:typed_data';
void main() {
// Uint8List (similar to Span)
Uint8List numbers = Uint8List.fromList([1, 2, 3, 4, 5]);
print('Numbers: ${numbers.join(', ')}');
// Slice
var slice = numbers.sublist(1, 4);
print('Slice: ${slice.join(', ')}');
// Modify slice affects original
slice[0] = 99;
print('After slice modification: ${numbers.join(', ')}');
// Memory
List<int> memory = [10, 20, 30, 40, 50];
var memorySlice = memory.sublist(1, 4);
print('Memory slice: ${memorySlice.join(', ')}');
// Pool (using List)
List<int> pooled = List.filled(10, 0);
for (int i = 0; i < 10; i++) {
pooled[i] = i * 2;
}
print('Pooled: ${pooled.join(', ')}');
// ByteData
ByteData bytes = ByteData(4);
bytes.setInt32(0, 0x01020304);
int intValue = bytes.getInt32(0);
print('Bytes as int: $intValue');
// String creation
String text = '**********';
print('Created: $text');
// ReadOnlyList
List<int> readOnly = List.unmodifiable([1, 2, 3, 4, 5]);
print('ReadOnly: ${readOnly.join(', ')}');
}Reflection in Dart provides runtime introspection of types, methods, and fields using the mirrors library.
- Class Mirrors: Reflect on classes
- Instance Mirrors: Reflect on instances
- Method Invocation: Dynamic method calls
- Access Private Members: Reflection capabilities
// Reflection in Dart
import 'dart:mirrors';
class Calculator {
int add(int a, int b) => a + b;
int multiply(int a, int b) => a * b;
String _secret = 'Hidden';
String getSecret() => _secret;
void printMessage(String message) => print(message);
}
void main() {
ClassMirror calcType = reflectClass(Calculator);
// Create instance
InstanceMirror calc = calcType.newInstance(const Symbol(''), []);
// Get and invoke method
MethodMirror addMethod = calcType.declarations[const Symbol('add')] as MethodMirror;
var result = calc.invoke(addMethod, [5, 3]);
print('Add(5,3) = ${result.reflectee}');
// Get all methods
print('\nAll methods:');
for (var declaration in calcType.declarations.values) {
if (declaration is MethodMirror) {
print(' ${declaration.simpleName} (${declaration.isPrivate ? 'private' : 'public'})');
}
}
// Access private field
VariableMirror secretField = calcType.declarations[const Symbol('_secret')] as VariableMirror;
String secret = calc.getField(secretField.simpleName).reflectee;
print('Private field: $secret');
// Invoke private method
MethodMirror getSecretMethod = calcType.declarations[const Symbol('getSecret')] as MethodMirror;
String secretValue = calc.invoke(getSecretMethod, []).reflectee;
print('Private method: $secretValue');
// Dynamic invocation with delegate
Function addDelegate = (int a, int b) => calc.reflectee.add(a, b);
print('Delegate: ${addDelegate(10, 20)}');
}Advanced Functional Queries enable building dynamic queries, sorting, projections, and grouping on collections.
- Dynamic Filtering: Runtime query building
- Dynamic Ordering: Field-based sorting
- Projections: Field selection
- Grouping: Dynamic group-by operations
// Advanced Functional Queries
class Person {
String name;
int age;
String city;
Person(this.name, this.age, this.city);
}
void main() {
List<Person> data = [
Person('Alice', 25, 'NYC'),
Person('Bob', 30, 'LA'),
Person('Carol', 22, 'NYC'),
Person('Dave', 35, 'Chicago')
];
// Dynamic query building
bool filter(Person p) => p.age > 25 && p.city == 'NYC';
var result = data.where(filter).toList();
print('Filter result: ${result.map((p) => p.name).join(', ')}');
// Dynamic ordering
String sortField = 'age';
var sorted = data.toList()
..sort((a, b) => a.age.compareTo(b.age));
print('Sorted by Age: ${sorted.map((p) => '${p.name}(${p.age})').join(', ')}');
// Dynamic select
List<String> fields = ['name', 'city'];
print('Projections:');
for (var p in data) {
var values = fields.map((f) {
switch (f) {
case 'name': return p.name;
case 'city': return p.city;
default: return null;
}
}).join(', ');
print(' $values');
}
// Group by dynamic
var grouped = data.fold<Map<String, List<Person>>>({}, (map, p) {
map.putIfAbsent(p.city, () => []).add(p);
return map;
});
grouped.forEach((city, people) {
print('City: $city (${people.length})');
});
}A Bank Account System demonstrates object-oriented programming concepts including encapsulation, transactions, and state management.
- Encapsulation: Private fields with public methods
- Transactions: Record all account operations
- Balance Management: Deposit and withdraw operations
- Statement Generation: Print transaction history
// Complete Bank Account System in Dart
class BankAccount {
static int _nextId = 1000;
String accountId;
String owner;
double balance;
List<Transaction> transactions = [];
BankAccount(this.owner, [double initialDeposit = 0.0]) {
_nextId++;
accountId = 'ACC$_nextId';
balance = 0.0;
if (initialDeposit > 0) deposit(initialDeposit, 'Initial deposit');
}
void deposit(double amount, [String description = 'Deposit']) {
if (amount <= 0) throw ArgumentError('Deposit amount must be positive');
balance += amount;
transactions.add(Transaction('DEPOSIT', amount, description, balance));
}
void withdraw(double amount, [String description = 'Withdrawal']) {
if (amount <= 0) throw ArgumentError('Withdrawal amount must be positive');
if (amount > balance) throw ArgumentError('Insufficient funds');
balance -= amount;
transactions.add(Transaction('WITHDRAWAL', amount, description, balance));
}
double getBalance() => balance;
void printStatement() {
print('=== Account Statement for $accountId ===');
print('Owner: $owner');
print('Balance: $${balance.toStringAsFixed(2)}');
print('Transactions:');
for (var t in transactions) {
print(' ${t.toString()}');
}
}
}
class Transaction {
String type;
double amount;
String description;
double balance;
Transaction(this.type, this.amount, this.description, this.balance);
@override
String toString() {
return '$type: $${amount.toStringAsFixed(2)} - $description (Balance: $${balance.toStringAsFixed(2)})';
}
}
void main() {
BankAccount acc = BankAccount('Alice', 1000);
acc.deposit(500, 'Salary');
acc.withdraw(200, 'Groceries');
acc.deposit(100, 'Bonus');
acc.withdraw(50, 'Coffee');
acc.printStatement();
}Event-Driven Programming uses events to trigger behavior. Dart's EventEmitter pattern enables loose coupling between components.
- Event Emitter: Central event management
- Subscribers: Listen for specific events
- Event Handling: Respond to events with callbacks
- Memory Management: Proper cleanup of event listeners
// Event-Driven Programming in Dart
class EventEmitter {
Map<String, List<Function>> _listeners = {};
void on(String event, Function handler) {
_listeners.putIfAbsent(event, () => []).add(handler);
}
void emit(String event, [dynamic data]) {
if (_listeners.containsKey(event)) {
for (var handler in _listeners[event]!) {
handler(data);
}
}
}
void off(String event, [Function? handler]) {
if (handler == null) {
_listeners.remove(event);
} else if (_listeners.containsKey(event)) {
_listeners[event]!.removeWhere((h) => h == handler);
}
}
}
void main() {
EventEmitter emitter = EventEmitter();
// Subscribe to events
emitter.on('userLogin', (data) {
print('User logged in: $data');
});
emitter.on('userLogout', (data) {
print('User logged out: $data');
});
emitter.on('dataUpdate', (data) {
print('Data updated: ${data['message']}');
});
// Emit events
emitter.emit('userLogin', 'Alice');
emitter.emit('dataUpdate', {'message': 'Profile updated', 'timestamp': DateTime.now()});
emitter.emit('userLogout', 'Alice');
// Remove specific handler
void handler(data) => print('Special handler: $data');
emitter.on('special', handler);
emitter.emit('special', 'Hello');
emitter.off('special', handler);
emitter.emit('special', 'This won't print');
}Streams provide asynchronous data flow. StreamControllers allow creating and controlling custom streams with full lifecycle management.
- Stream: Asynchronous data sequence
- StreamController: Create and manage streams
- Subscription: Listen to stream events
- Broadcast Streams: Multiple subscribers
// Custom Stream and StreamController in Dart
import 'dart:async';
class NumberStream {
StreamController<int> _controller = StreamController<int>();
Stream<int> get stream => _controller.stream;
void generateNumbers(int count) async {
for (int i = 1; i <= count; i++) {
await Future.delayed(Duration(milliseconds: 500));
_controller.add(i);
}
_controller.close();
}
void dispose() {
_controller.close();
}
}
void main() async {
NumberStream ns = NumberStream();
// Subscribe to stream
StreamSubscription sub = ns.stream.listen(
(data) => print('Received: $data'),
onError: (error) => print('Error: $error'),
onDone: () => print('Stream completed'),
cancelOnError: true
);
// Start generating
ns.generateNumbers(5);
// Cancel after 2 seconds
await Future.delayed(Duration(seconds: 2));
await sub.cancel();
print('Subscription cancelled');
// Transform stream
Stream<int> evenStream = ns.stream.where((n) => n.isEven);
evenStream.listen((data) => print('Even: $data'));
// Multiple subscribers
Stream<int> shared = ns.stream.asBroadcastStream();
shared.listen((data) => print('Sub1: $data'));
shared.listen((data) => print('Sub2: $data'));
ns.dispose();
}HTTP Requests and Web Scraping are essential for data retrieval. Dart provides HttpClient for making HTTP requests and parsing responses.
- GET Requests: Fetch data from APIs
- POST Requests: Send data to servers
- JSON Parsing: Convert JSON to Dart objects
- Error Handling: Handle HTTP errors gracefully
// Web Scraping and HTTP Requests in Dart
import 'dart:convert';
import 'dart:io';
class HttpClient {
static Future<String> get(String url) async {
var client = HttpClient();
try {
var request = await client.getUrl(Uri.parse(url));
var response = await request.close();
if (response.statusCode == 200) {
return await response.transform(utf8.decoder).join();
} else {
throw Exception('HTTP Error: ${response.statusCode}');
}
} finally {
client.close();
}
}
static Future<String> post(String url, Map<String, dynamic> data) async {
var client = HttpClient();
try {
var request = await client.postUrl(Uri.parse(url));
request.headers.set('Content-Type', 'application/json');
request.add(utf8.encode(jsonEncode(data)));
var response = await request.close();
if (response.statusCode == 200 || response.statusCode == 201) {
return await response.transform(utf8.decoder).join();
} else {
throw Exception('HTTP Error: ${response.statusCode}');
}
} finally {
client.close();
}
}
}
void main() async {
try {
// GET request
print('Fetching data...');
String response = await HttpClient.get('https://jsonplaceholder.typicode.com/posts/1');
Map<String, dynamic> post = jsonDecode(response);
print('Post: ${post['title']}');
print('Body: ${post['body']}');
// POST request
Map<String, dynamic> newPost = {
'title': 'My New Post',
'body': 'This is the content of my new post',
'userId': 1
};
String result = await HttpClient.post(
'https://jsonplaceholder.typicode.com/posts',
newPost
);
print('Created: $result');
} catch (e) {
print('Error: $e');
}
}Database Operations with SQLite in Dart provide persistent storage. The sqflite package offers a comprehensive SQL API for CRUD operations.
- SQLite: Embedded database engine
- CRUD Operations: Create, Read, Update, Delete
- Transactions: Atomic database operations
- Migration: Schema evolution and versioning
// Database Operations with SQLite in Dart
import 'dart:async';
import 'dart:io';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
class User {
int? id;
String name;
String email;
int age;
User({this.id, required this.name, required this.email, required this.age});
Map<String, dynamic> toMap() {
return {
'id': id,
'name': name,
'email': email,
'age': age,
};
}
factory User.fromMap(Map<String, dynamic> map) {
return User(
id: map['id'],
name: map['name'],
email: map['email'],
age: map['age'],
);
}
}
class DatabaseHelper {
static final DatabaseHelper _instance = DatabaseHelper._internal();
factory DatabaseHelper() => _instance;
DatabaseHelper._internal();
Database? _database;
Future<Database> get database async {
if (_database != null) return _database!;
_database = await _initDatabase();
return _database!;
}
Future<Database> _initDatabase() async {
String path = join(await getDatabasesPath(), 'users.db');
return await openDatabase(
path,
version: 1,
onCreate: _onCreate,
);
}
Future<void> _onCreate(Database db, int version) async {
await db.execute('''
CREATE TABLE users(
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL,
age INTEGER
)
''');
print('Database created');
}
Future<int> insertUser(User user) async {
Database db = await database;
return await db.insert('users', user.toMap());
}
Future<List<User>> getUsers() async {
Database db = await database;
final List<Map<String, dynamic>> maps = await db.query('users');
return List.generate(maps.length, (i) => User.fromMap(maps[i]));
}
Future<int> updateUser(User user) async {
Database db = await database;
return await db.update(
'users',
user.toMap(),
where: 'id = ?',
whereArgs: [user.id],
);
}
Future<int> deleteUser(int id) async {
Database db = await database;
return await db.delete(
'users',
where: 'id = ?',
whereArgs: [id],
);
}
Future<void> close() async {
if (_database != null) {
await _database!.close();
_database = null;
}
}
}
void main() async {
DatabaseHelper db = DatabaseHelper();
try {
// Insert users
User user1 = User(name: 'Alice', email: 'alice@email.com', age: 25);
User user2 = User(name: 'Bob', email: 'bob@email.com', age: 30);
int id1 = await db.insertUser(user1);
int id2 = await db.insertUser(user2);
print('Inserted users with IDs: $id1, $id2');
// Query users
List<User> users = await db.getUsers();
print('All users:');
for (var u in users) {
print(' ${u.id}. ${u.name} (${u.email}) - Age: ${u.age}');
}
// Update user
User userToUpdate = users.first;
userToUpdate.age = 26;
await db.updateUser(userToUpdate);
print('Updated user ${userToUpdate.name}');
// Delete user
await db.deleteUser(users.last.id!);
print('Deleted last user');
// Show final list
users = await db.getUsers();
print('Final users:');
for (var u in users) {
print(' ${u.name} (${u.email})');
}
} catch (e) {
print('Error: $e');
} finally {
await db.close();
}
}Unit Testing ensures code quality and correctness. Dart provides a built-in testing framework with assertions, test groups, and async support.
- Assertions: Verify expected results
- Test Cases: Individual test scenarios
- Setup/Teardown: Test lifecycle management
- Async Testing: Test asynchronous code
// Unit Testing in Dart
import 'dart:async';
class Calculator {
int add(int a, int b) => a + b;
int subtract(int a, int b) => a - b;
int multiply(int a, int b) => a * b;
double divide(int a, int b) {
if (b == 0) throw ArgumentError('Division by zero');
return a / b;
}
Future<int> asyncAdd(int a, int b) async {
await Future.delayed(Duration(milliseconds: 100));
return a + b;
}
}
// Test file would normally be in a separate file
void runTests() {
Calculator calc = Calculator();
// Test 1: Addition
assert(calc.add(2, 3) == 5, 'Add test failed');
print('✓ Add test passed');
// Test 2: Subtraction
assert(calc.subtract(5, 3) == 2, 'Subtract test failed');
print('✓ Subtract test passed');
// Test 3: Multiplication
assert(calc.multiply(4, 3) == 12, 'Multiply test failed');
print('✓ Multiply test passed');
// Test 4: Division
assert(calc.divide(10, 2) == 5.0, 'Divide test failed');
print('✓ Divide test passed');
// Test 5: Division by zero
try {
calc.divide(5, 0);
assert(false, 'Division by zero should throw');
} catch (e) {
assert(e is ArgumentError, 'Wrong exception type');
print('✓ Division by zero test passed');
}
// Test 6: Async addition
calc.asyncAdd(5, 3).then((result) {
assert(result == 8, 'Async add test failed');
print('✓ Async add test passed');
});
print('All tests passed!');
}
void main() {
runTests();
print('
=== Test Results ===');
print('All tests completed successfully');
}Factory and Abstract Factory are creational design patterns that provide interfaces for creating families of related objects without specifying concrete classes.
- Factory Method: Single product creation
- Abstract Factory: Family of related products
- Decoupling: Separate creation from usage
- Product Families: Consistent product variants
// Design Patterns - Factory and Abstract Factory
// Product interfaces
abstract class Button {
void render();
void onClick();
}
abstract class Checkbox {
void render();
void onCheck();
}
// Concrete products for Windows
class WindowsButton implements Button {
@override
void render() => print('Rendering Windows button');
@override
void onClick() => print('Windows button clicked');
}
class WindowsCheckbox implements Checkbox {
@override
void render() => print('Rendering Windows checkbox');
@override
void onCheck() => print('Windows checkbox checked');
}
// Concrete products for Mac
class MacButton implements Button {
@override
void render() => print('Rendering Mac button');
@override
void onClick() => print('Mac button clicked');
}
class MacCheckbox implements Checkbox {
@override
void render() => print('Rendering Mac checkbox');
@override
void onCheck() => print('Mac checkbox checked');
}
// Abstract Factory
abstract class GUIFactory {
Button createButton();
Checkbox createCheckbox();
}
// Concrete factories
class WindowsFactory implements GUIFactory {
@override
Button createButton() => WindowsButton();
@override
Checkbox createCheckbox() => WindowsCheckbox();
}
class MacFactory implements GUIFactory {
@override
Button createButton() => MacButton();
@override
Checkbox createCheckbox() => MacCheckbox();
}
// Application
class Application {
final GUIFactory factory;
Application(this.factory);
void renderUI() {
Button button = factory.createButton();
Checkbox checkbox = factory.createCheckbox();
button.render();
checkbox.render();
}
}
void main() {
// Windows app
Application windowsApp = Application(WindowsFactory());
print('=== Windows Application ===');
windowsApp.renderUI();
// Mac app
Application macApp = Application(MacFactory());
print('
=== Mac Application ===');
macApp.renderUI();
}State Management is crucial for reactive applications. This example demonstrates a Flutter-like state management pattern with reactive updates and widget hierarchy.
- State Class: Managed state with listeners
- Widgets: UI components that react to state
- Notification System: Update UI on state changes
- Cleanup: Proper resource disposal
// Flutter-like State Management in Dart
import 'dart:async';
// State class
class State<T> {
T value;
List<Function> _listeners = [];
State(this.value);
T get state => value;
void setState(T newValue) {
value = newValue;
_notifyListeners();
}
void addListener(Function listener) {
_listeners.add(listener);
}
void removeListener(Function listener) {
_listeners.remove(listener);
}
void _notifyListeners() {
for (var listener in _listeners) {
listener();
}
}
void dispose() {
_listeners.clear();
}
}
// Counter App
class CounterApp {
State<int> counter = State<int>(0);
List<Widget> widgets = [];
CounterApp() {
// Create widgets
widgets.add(TextWidget('Counter: ${counter.state}'));
widgets.add(ButtonWidget('Increment', () => counter.setState(counter.state + 1)));
widgets.add(ButtonWidget('Decrement', () => counter.setState(counter.state - 1)));
widgets.add(ButtonWidget('Reset', () => counter.setState(0)));
// Listen to state changes
counter.addListener(() {
print('Counter updated: ${counter.state}');
updateUI();
});
}
void updateUI() {
print('=== UI Update ===');
print('Counter: ${counter.state}');
}
void run() {
print('Counter App Started');
updateUI();
// Simulate user interactions
Future.delayed(Duration(seconds: 1), () {
print('User clicks Increment');
counter.setState(counter.state + 1);
});
Future.delayed(Duration(seconds: 2), () {
print('User clicks Increment');
counter.setState(counter.state + 1);
});
Future.delayed(Duration(seconds: 3), () {
print('User clicks Decrement');
counter.setState(counter.state - 1);
});
Future.delayed(Duration(seconds: 4), () {
print('User clicks Reset');
counter.setState(0);
});
}
void dispose() {
counter.dispose();
}
}
// Simple Widgets
class TextWidget {
String text;
TextWidget(this.text) {
print('Text: $text');
}
}
class ButtonWidget {
String label;
Function onPressed;
ButtonWidget(this.label, this.onPressed) {
print('Button: $label');
}
void click() {
onPressed();
}
}
void main() {
CounterApp app = CounterApp();
app.run();
// Keep app running for a while
Future.delayed(Duration(seconds: 6), () {
print('
App closing...');
app.dispose();
});
}