Intro
- The Dart language itself is implemented in C++ and Dart.
- The Dart VM (Virtual Machine) is written in C++.
- Strongly Typed Language:
- Static Type Checking: Types are checked at compile-time, reducing runtime errors and improving code quality.
- Sound Null Safety: Helps eliminate null reference errors by distinguishing between nullable and non-nullable types. Null safety is enforced at compile time.
- Everything in dart is object
Dart Compilation
Compilation Process:
- Ahead-of-Time (AOT) Compilation: For optimized, fast-running, native machine code, mainly used for mobile apps. Produces efficient code but requires a longer compilation time.
- Just-in-Time (JIT) Compilation: For faster development cycles, especially during development. Provides hot reload capabilities, allowing for immediate feedback.
- Transpilation to JavaScript: When targeting web platforms, Dart code is compiled to JavaScript using the
dart2jscompiler.
Tools:
- Dart SDK: Includes tools like
dart2js,dartanalyzer, anddartdevc(Dart Development Compiler). - Flutter: Uses Dart and provides a framework for building natively compiled applications for mobile, web, and desktop from a single codebase.
How Dart Works Internally
Dart Virtual Machine (Dart VM):
- Executes Dart code in a virtualized environment.
- Supports JIT compilation for fast development cycles and AOT compilation for optimized performance.
Garbage Collection:
- Dart uses a generational garbage collector.
- Objects are allocated in the young generation and promoted to the old generation if they survive garbage collection cycles.
Isolates:
- Dart’s concurrency model uses isolates, which are independent workers with their own memory and event loop.
- Communication between isolates is done through message passing.
import 'dart:isolate';
void entryPoint(SendPort sendPort) {
sendPort.send('Hello from isolate!');
}
void main() async {
ReceivePort receivePort = ReceivePort();
await Isolate.spawn(entryPoint, receivePort.sendPort);
receivePort.listen((message) {
print(message); // Output: Hello from isolate!
});
}Package Manager (pub):
- Dart uses
pubfor package management, allowing for easy dependency management and package distribution.
Cross-Platform Development:
- With Flutter, Dart enables the creation of apps that run on iOS, Android, web, and desktop with a single codebase.
Performance Optimization
Tree Shaking:
- Removes unused code during the compilation process, resulting in smaller and faster binaries.
Ahead-of-Time Compilation:
- Produces highly optimized native code for better performance on mobile and desktop platforms.
Hot Reload:
- Provides quick iterations by injecting updated code into the running app without restarting the entire application, mainly used in Flutter development.
dart run hello.dart
dart create my_project
Dart Basics
main function, which is the entry point of every Dart application. The main function is where the execution of the program begins.
void main() {
print('Hello, Dart!');
}
//`main` Function with Command-Line Arguments:
void main(List<String> arguments) {
if (arguments.isNotEmpty) {
print('Hello, ${arguments[0]}!');
} else {
print('Hello, Dart!');
}
}
//dart run main.dart AliceVariables:
var name = 'Dart';
String language = 'Dart';
int number = 42;
double pi = 3.14;
bool isCool = true;Control Flow:
if (age > 18) {
print('Adult');
} else {
print('Minor');
}
for (var i = 0; i < 5; i++) {
print(i);
}
while (isRunning) {
run();
}Collections:
List<int> numbers = [1, 2, 3];
Set<String> names = {'Alice', 'Bob'};
Map<String, int> ages = {'Alice': 30, 'Bob': 25};Functions
void printMessage(String message) {
print(message);
}
int add(int a, int b) {
return a + b;
}
//Arrow function
String greet(String name) => 'Hello, $name!';Calling Functions:
void main() {
printMessage('Hello, Dart!');
int sum = add(2, 3);
print(sum); // Output: 5
print(greet('Alice')); // Output: Hello, Alice!
}Optional Positional Parameters
Defining Functions with Optional Positional Parameters:
void describe(String name, [int age, String city]) {
print('Name: $name');
if (age != null) print('Age: $age');
if (city != null) print('City: $city');
}
void main() {
describe('Alice'); // Output: Name: Alice
describe('Bob', 30); // Output: Name: Bob Age: 30
describe('Charlie', 25, 'New York'); // Output: Name: Charlie Age: 25 City: New York
}Named Parameters
Defining Functions with Named Parameters:
void describe({String name, int age, String city}) {
print('Name: $name');
if (age != null) print('Age: $age');
if (city != null) print('City: $city');
}
void main() {
describe(name: 'Alice'); // Output: Name: Alice
describe(name: 'Bob', age: 30); // Output: Name: Bob Age: 30
describe(name: 'Charlie', age: 25, city: 'New York'); // Output: Name: Charlie Age: 25 City: New York
}Required Named Parameters:
void describe({required String name, int age = 0, String city = 'Unknown'}) {
print('Name: $name');
print('Age: $age');
print('City: $city');
}
void main() {
describe(name: 'Alice'); // Output: Name: Alice Age: 0 City: Unknown
describe(name: 'Bob', age: 30); // Output: Name: Bob Age: 30 City: Unknown
describe(name: 'Charlie', age: 25, city: 'New York'); // Output: Name: Charlie Age: 25 City: New York
}Optional and Named Parameters Combined
Combining Optional Positional and Named Parameters:
void describe(String name, {int age, String city}) {
print('Name: $name');
if (age != null) print('Age: $age');
if (city != null) print('City: $city');
}
void main() {
describe('Alice'); // Output: Name: Alice
describe('Bob', age: 30); // Output: Name: Bob Age: 30
describe('Charlie', age: 25, city: 'New York'); // Output: Name: Charlie Age: 25 City: New York
}Default Parameter Values
Using Default Parameter Values:
void describe({String name = 'Unknown', int age = 0, String city = 'Unknown'}) {
print('Name: $name');
print('Age: $age');
print('City: $city');
}
void main() {
describe(); // Output: Name: Unknown Age: 0 City: Unknown
describe(name: 'Alice'); // Output: Name: Alice Age: 0 City: Unknown
describe(name: 'Bob', age: 30); // Output: Name: Bob Age: 30 City: Unknown
describe(name: 'Charlie', age: 25, city: 'New York'); // Output: Name: Charlie Age: 25 City: New York
}Anonymous Functions
Defining and Using Anonymous Functions:
void main() {
var list = ['apples', 'bananas', 'oranges'];
list.forEach((item) {
print('${list.indexOf(item)}: $item');
});
list.forEach((item) => print('${list.indexOf(item)}: $item'));
}Closures
Using Closures:
Function makeAdder(int addBy) {
return (int i) => addBy + i;
}
void main() {
var add2 = makeAdder(2);
var add4 = makeAdder(4);
print(add2(3)); // Output: 5
print(add4(3)); // Output: 7
}Object-Oriented Programming
Classes:
class Person {
String name;
int age;
//Dart will assgin vaule passed to this.name and this.age
Person(this.name, this.age);
void greet() {
print('Hello, my name is $name.');
}
}
void main() {
var person = Person('Alice', 30);
person.greet();
}Inheritance:
class Employee extends Person {
int employeeId;
Employee(String name, int age, this.employeeId) : super(name, age);
@override
void greet() {
print('Hello, my name is $name and my ID is $employeeId.');
}
}Classes and Objects
Defining a Class:
class Animal {
String name;
int age;
Animal(this.name, this.age); // we dont want to call this.nane=name etc dart will handle it
void makeSound() {
print('$name makes a sound.');
}
}Creating an Object:
void main() {
Animal cat = Animal('Whiskers', 2);
cat.makeSound(); // Output: Whiskers makes a sound.
}Constructors
Default Constructor:
class Dog {
String name;
Dog(this.name);
}
void main() {
Dog dog = Dog('Buddy');
print(dog.name); // Output: Buddy
}Named Constructors:
class Dog {
String name;
int age;
Dog(this.name, this.age);
Dog.anyname(String name) {
this.name = name;
this.age = 0;
}
}
void main() {
Dog puppy = Dog.named('Buddy');
print('${puppy.name} is ${puppy.age} years old.'); // Output: Buddy is 0 years old.
}Factory Constructors:
class Square {
double side;
Square(this.side);
factory Square.fromArea(double area) {
return Square(Math.sqrt(area));
}
}
void main() {
Square square = Square.fromArea(16);
print(square.side); // Output: 4.0
}Inheritance
Extending a Class:
class Animal {
String name;
Animal(this.name);
void makeSound() {
print('$name makes a sound.');
}
}
class Dog extends Animal {
Dog(String name) : super(name);
@override
void makeSound() {
print('$name barks.');
}
}
void main() {
Dog dog = Dog('Buddy');
dog.makeSound(); // Output: Buddy barks.
}Abstract Classes
Defining an Abstract Class:
abstract class Shape {
void draw();
}
class Circle extends Shape {
@override
void draw() {
print('Drawing a circle');
}
}
void main() {
Circle circle = Circle();
circle.draw(); // Output: Drawing a circle
}Interfaces
Implementing Interfaces:
class Flyer {
void fly() {
print('Flying');
}
}
class Bird implements Flyer {
@override
void fly() {
print('Bird is flying');
}
}
void main() {
Bird bird = Bird();
bird.fly(); // Output: Bird is flying
}Mixins
Using Mixins:
mixin Swimmer {
void swim() {
print('Swimming');
}
}
class Animal {
String name;
Animal(this.name);
}
class Fish extends Animal with Swimmer {
Fish(String name) : super(name);
}
void main() {
Fish fish = Fish('Goldfish');
fish.swim(); // Output: Swimming
}Encapsulation
Getters and Setters:
class Person {
String _name;
String get name => _name;
set name(String newName) {
if (newName.length > 3) {
_name = newName;
} else {
print('Name is too short.');
}
}
}
void main() {
Person person = Person();
person.name = 'John';
print(person.name); // Output: John
person.name = 'Jo'; // Output: Name is too short.
}Polymorphism
Method Overriding:
class Animal {
void sound() {
print('Animal makes a sound');
}
}
class Cat extends Animal {
@override
void sound() {
print('Cat meows');
}
}
void main() {
Animal myCat = Cat();
myCat.sound(); // Output: Cat meows
}Static Members
Static Variables and Methods:
class MathUtils {
static const double pi = 3.14159;
static double square(double number) {
return number * number;
}
}
void main() {
print(MathUtils.pi); // Output: 3.14159
print(MathUtils.square(4)); // Output: 16
}Asynchronous Programming
Async/Await:
Future<void> fetchData() async {
var data = await fetchFromServer();
print(data);
}
Future<String> fetchFromServer() {
return Future.delayed(Duration(seconds: 2), () => 'Data from server');
}Streams:
Stream<int> countStream(int max) async* {
for (int i = 1; i <= max; i++) {
yield i;
}
}
void main() async {
await for (var value in countStream(5)) {
print(value);
}
}Error Handling
Try/Catch:
try {
int result = 10 ~/ 0;
} catch (e) {
print('Error: $e');
}Packages
Core Libraries
dart:core
Basic Types:
intdoubleStringbool
Collections:
List<T>Set<T>Map<K, V>
Common Methods:
String Methods:
String text = 'Hello, Dart!';
text.length; // 12
text.toLowerCase(); // 'hello, dart!'
text.toUpperCase(); // 'HELLO, DART!'
text.contains('Dart'); // true
text.replaceAll('Dart', 'World'); // 'Hello, World!'
text.split(', '); // ['Hello', 'Dart!']List Methods:
List<int> numbers = [1, 2, 3];
numbers.add(4); // [1, 2, 3, 4]
numbers.remove(2); // [1, 3, 4]
numbers.length; // 3
numbers.contains(3); // true
numbers.sort(); // [1, 3, 4]Map Methods:
Map<String, int> ages = {'Alice': 30, 'Bob': 25};
ages['Charlie'] = 20; // {'Alice': 30, 'Bob': 25, 'Charlie': 20}
ages.remove('Bob'); // {'Alice': 30, 'Charlie': 20}
ages.keys; // ('Alice', 'Charlie')
ages.values; // (30, 20)dart:async
Future:
Future<int> fetchData() async {
return 42;
}
void main() async {
int data = await fetchData();
print(data); // 42
}Stream:
Stream<int> countStream(int max) async* {
for (int i = 1; i <= max; i++) {
yield i;
}
}
void main() async {
await for (int i in countStream(3)) {
print(i); // 1, 2, 3
}
}dart:collection
Queue:
import 'dart:collection';
Queue<int> queue = Queue();
queue.addAll([1, 2, 3]);
queue.removeFirst(); // 1
queue.removeLast(); // 3LinkedHashMap:
LinkedHashMap<String, int> map = LinkedHashMap();
map['one'] = 1;
map['two'] = 2;dart:convert
JSON Encoding/Decoding:
import 'dart:convert';
String jsonString = '{"name": "Alice", "age": 30}';
Map<String, dynamic> user = jsonDecode(jsonString);
print(user['name']); // Alice
String jsonString = jsonEncode({'name': 'Alice', 'age': 30});
print(jsonString); // {"name":"Alice","age":30}dart:io
File Operations:
import 'dart:io';
void main() async {
String path = 'example.txt';
// Write to a file
File file = File(path);
await file.writeAsString('Hello, Dart!');
// Read from a file
String contents = await file.readAsString();
print(contents); // Hello, Dart!
}HttpClient:
import 'dart:io';
void main() async {
HttpClient client = HttpClient();
HttpClientRequest request = await client.getUrl(Uri.parse('https://example.com'));
HttpClientResponse response = await request.close();
String contents = await response.transform(utf8.decoder).join();
print(contents);
}dart:math
Math Functions:
import 'dart:math';
void main() {
print(pi); // 3.141592653589793
print(sqrt(16)); // 4.0
print(pow(2, 3)); // 8
print(max(5, 10)); // 10
print(min(5, 10)); // 5
}Random Numbers:
import 'dart:math';
void main() {
Random random = Random();
print(random.nextInt(100)); // Random integer between 0 and 99
print(random.nextDouble()); // Random double between 0.0 and 1.0
print(random.nextBool()); // Random boolean value
}dart:typed_data
Typed Data:
import 'dart:typed_data';
void main() {
Uint8List bytes = Uint8List(4);
bytes[0] = 1;
bytes[1] = 2;
bytes[2] = 3;
bytes[3] = 4;
print(bytes); // [1, 2, 3, 4]
}dart:developer
Debugging:
import 'dart:developer';
void main() {
debugger();
log('This is a log message.');
}dart:isolate
Isolates:
import 'dart:isolate';
void entryPoint(SendPort sendPort) {
sendPort.send('Hello from isolate!');
}
void main() async {
ReceivePort receivePort = ReceivePort();
await Isolate.spawn(entryPoint, receivePort.sendPort);
receivePort.listen((message) {
print(message); // Hello from isolate!
});
}Using Packages:
-
Add dependency in
pubspec.yaml:dependencies: http: ^0.13.3 -
Import and use:
import 'package:http/http.dart' as http; void main() async { var response = await http.get(Uri.parse('https://example.com')); print(response.body); }
Data types
| Type | Example | Notes |
|---|---|---|
int | int age = 30; | Whole numbers |
double | double pi = 3.14; | Floating-point numbers |
num | num x = 3; x = 3.14; | Superclass of both int and double |
String | String name = 'Boopathi'; | A sequence of UTF-16 characters |
bool | bool isOn = true; | true or false only |
Dynamic var
| Type | Example | Notes |
|---|---|---|
var | var x = 10; | Type inferred, can’t change later |
dynamic | dynamic x = 10; x = 'hello'; | Type checked at runtime only |
Object | Object x = 42; | Base class of all types (type-safe unlike dynamic) |
Never | Never foo() => throw Error(); | A function that never returns (useful for exceptions) |
void | void greet() => print("hi"); | Function returns no value |
Null | Null x = null; | Can only be null (used with null safety) |
Final vs const
- FInal: You can assign a value once, but the value can be computed at runtime.
- const: Value must be known at compile-time.
final time = DateTime.now(); // OK — runtime value
time = DateTime.now(); // ❌ Error: can't reassign
const pi = 3.14159; // OK
const time = DateTime.now(); // ❌ Error: not compile-time constant
Type casting
| Feature | Syntax / Example | Description | Throws Error? |
|---|---|---|---|
| Type Check (Safe) | if (x is String) | Checks if x is of type String | ❌ No |
| Negated Type Check | if (x is! int) | Checks if x is not of type int | ❌ No |
| Type Promotion | if (x is String) x.length | Promotes x to String in the block | ❌ No |
| Explicit Casting | String name = obj as String | Casts obj to String (runtime check) | ✅ TypeError if wrong |
| Downcasting (Inheritance) | (animal as Dog).bark() | Casts base class reference to subclass type | ✅ if not a Dog |
| Custom Conversion (parse) | int.parse('123') | Converts String to int | ✅ FormatException |
| Custom Conversion (toString) | 123.toString() | Converts any type to String | ❌ No |
| Generic Casting (Collections) | List<num> → List<int> | Not allowed directly, must map: list.map((e) => e as int).toList() | ✅ TypeError if wrong |
| Dynamic Assignment | dynamic x = 5; String y = x as String; | Casts dynamic to specific type | ✅ if wrong type |
| Const Type Context | const x = 10; int y = x; | Dart infers type at compile-time | ❌ No |
Classes
const constructor
- when we use const it will act as singleton but const must need to have final var
class Point {
final int x, y;
const Point(this.x, this.y);
}
void main() {
const p1 = Point(1, 2); // Compile-time constant
const p2 = Point(1, 2); // Same as p1 (canonicalized)
print(identical(p1, p2)); // ✅ true (single object reused)
}