Dart Fundamentals

Dart is an object-oriented, statically typed, class-based programming language developed by Google. It's the primary language used for building mobile, web, and desktop apps with Flutter. Dart’s syntax is similar to JavaScript, Java, and C# but includes features like a rich set of libraries, garbage collection, and a strong type system.
Dart is statically typed because its variables' types are checked at compile-time, and Dart's type system is more similar to that of languages like Java or C#, even though its syntax allows for flexible and concise variable declarations.
1. Variables and Data Types
Declaring Variables: Dart allows you to declare variables using either explicit or implicit types. For implicit typing, use
var
and Dart will infer the type.var name = "Alice"; // Implicit typing int age = 25; // Explicit typing
Basic Data Types: Dart supports several basic data types such as:
int
: Integer numbersdouble
: Floating-point numbersString
: Textbool
: Boolean (true/false)
int age = 30;
double price = 99.99;
String greeting = "Hello, Dart!";
bool isActive = true;
Const Variables: Dart allows defining constant variables using the
const
keyword. These variables are compile-time constants.const int maxUsers = 100;
Final Variables: Dart also supports
final
variables, which are variables that can only be assigned once.final String apiKey = "API12345";
Collections:
Lists (arrays in other languages): Ordered collections of items.
List<int> numbers = [1, 2, 3, 4];
Maps (key-value pairs):
Map<String, String> person = { 'name': 'Alice', 'city': 'Wonderland' };
Sets: Unordered collections of unique items.
Set<String> fruits = {'apple', 'banana'};
2. Operators
Dart offers a wide range of operators, including:
Arithmetic Operators: Used for basic calculations (
+
, &&, || ,/
,~/
for integer division).int sum = 10 + 5; int div = 10 ~/ 2; // Integer division, result is 5
Comparison Operators: Used for comparison (
==
,!=
,>
,<
,>=
,<=
).bool isEqual = (10 == 5); // false bool isGreater = (10 > 5); // true
Logical Operators: Used for boolean logic (
&&
,||
,!
).bool and = true && false; // false bool or = true || false; // true
3. Control Flow
Dart uses common control flow structures to control the execution of code:
If-Else Statements:
if (age > 18) { print("Adult"); } else { print("Minor"); }
Switch-Case:
switch (fruit) { case 'Apple': print("It's an apple"); break; case 'Banana': print("It's a banana"); break; default: print("Unknown fruit");
Loops:
For Loop: Iterates a fixed number of times.
for (int i = 0; i < 5; i++) { print(i); // Prints 0 to 4 }
While Loop: Continues as long as a condition is true.
int count = 0; while (count < 3) { print(count); // Prints 0 to 2 count++; }
4. Functions
Functions are first-class objects in Dart, meaning they can be passed as arguments, returned from other functions, or stored in variables.
Function Declaration: Dart functions are declared with a return type, function name, and parameters.
int add(int a, int b) { return a + b; }
Arrow Functions: Dart supports shorthand syntax for single-line functions.
int multiply(int a, int b) => a * b;
5. Object-Oriented Programming (OOP) Concepts
Dart is an object-oriented language that supports key OOP principles like encapsulation, inheritance, polymorphism, and abstraction.
i) Encapsulation: Hides internal state and provides controlled access.
ii) Inheritance: Allows a class to inherit properties and methods from another.
iii) Polymorphism: Allows different types to be treated as instances of a common type.
iv) Abstraction: Hides complex implementation details and only exposes necessary parts.
Classes: Dart uses classes to define objects and their behaviors.
class Person { String name; int age; Person(this.name, this.age); void greet() { print("Hello, I am $name, $age years old."); } }
Inheritance: Dart allows a class to inherit properties and methods from another class using
extends
.class Animal { void speak() { print("Animal sound"); } } class Dog extends Animal { @override void speak() { print("Bark"); } }
Abstract Classes: Abstract classes can't be instantiated directly but can define method signatures to be implemented by subclasses.
abstract class Shape { void draw(); } class Circle extends Shape { void draw() { print("Drawing a Circle"); } }
Interfaces: Any class can act as an interface. Dart uses the
implements
keyword to implement interfaces.class Printer { void printDocument(); } class LaserPrinter implements Printer { void printDocument() { print("Laser printer printing..."); } }
6. Exception Handling
Dart provides mechanisms to handle errors and exceptions using try
, catch
, and finally
.
Try-Catch:
try { int result = 10 ~/ 0; // Throws exception } catch (e) { print("Error: $e"); // Catches and handles the error }
Finally: Executes code regardless of whether an exception occurred.
try { print("Executing try block"); } finally { print("Executing finally block"); }
7. Asynchronous Programming
Dart is highly used for asynchronous programming, especially in Flutter for tasks like handling network requests or delayed operations. Futures, Steams, Async-Await.
Difference between Futures and Steams ?
Futures represent a single value that will be available in the future (e.g., the result of a network request). Where as Streams represent a sequence of asynchronous events (e.g., multiple responses from a server, user interactions, or continuous data).
Feature | Future | Stream |
Purpose | Represents a single asynchronous value | Represents a sequence of asynchronous values |
Completion | Completed once with a value or an error | Can emit multiple values over time and eventually close |
Data | One result at a time | Multiple results, one after another |
Use Case | When you want the result of a one-time operation | When you need to listen for multiple events over time |
Handling Data | .then() , await | .listen() , await for |
State | Can be completed with a value or error | Can emit multiple values and then be closed |
Examples | - Fetching data from a server (single response) | - Real-time updates (e.g., WebSocket data) |
- Performing a calculation | - User input events | |
- Reading a file | - Sensor data updates | |
Completion Behavior | Resolves once with a single result | Can continuously emit values until explicitly closed |
Asynchronous Operation | Best suited for operations that return one result | Best suited for operations that return multiple events |
Futures: Represent a value that will be available in the future (e.g., after a network call).
Future<void> fetchData() async { await Future.delayed(Duration(seconds: 2)); print("Data fetched"); }
Async-Await: The
async
keyword marks a function as asynchronous, andawait
is used to pause execution until the result is available.
Future<int> fetchData() async {
await Future.delayed(Duration(seconds: 2));
return 42;
}
void main() async {
int result = await fetchData();
print(result); // Prints 42
}
- A Stream is a sequence of asynchronous events or data that are delivered over time. Streams are widely used in Dart for handling data like network requests, user input, or data from databases.
We create a Stream
using Stream.periodic
that emits a new number every second. The .take(5)
method ensures that the stream stops after 5 items.
import 'dart:async';
void main() {
// Creating a stream of numbers
Stream<int> countStream = Stream<int>.periodic(Duration(seconds: 1), (x) => x).take(5);
// Listen to the stream
countStream.listen((data) {
print(data); // Prints 0, 1, 2, 3, 4
});
}
- A StreamController allows you to manually add events to a stream with
controller.add()
and listen to it withcontroller.stream
.listen()
, giving you control over the flow of data.listen()
is used to handle incoming data. This can be used to react to various events (like user interactions, incoming network responses, etc.).
import 'dart:async';
void main() {
final controller = StreamController<int>();
// Adding data to the stream
controller.add(1);
controller.add(2);
// Listening to the stream
controller.stream.listen((data) {
print("Received: $data"); // Output: Received: 1, Received: 2
});
// Close the stream after use
controller.close();
}
8. Collections and Advanced Topics
Lists: Ordered collections of items.
List<int> numbers = [1, 2, 3, 4];
Sets: Unordered collections of unique items.
Set<String> fruits = {'apple', 'banana'};
Maps: Store key-value pairs.
Map<String, int> age = {'Alice': 30, 'Bob': 25};
Enums: Represent a fixed set of constants.
enum Days { Monday, Tuesday, Wednesday }
Null Safety: Dart 2.12 introduced null safety, preventing null reference errors.
int? nullableValue = null; int nonNullableValue = 10; // Cannot be null
Mixins: Allow classes to reuse code without using inheritance.
mixin Walker { void walk() { print("Walking"); } } class Person with Walker { void speak() { print("Hello"); } }
Pattern Matching: Dart supports pattern matching to simplify working with complex data structures.
Records are an anonymous, immutable, aggregate type. Records are real values; you can store them in variables, nest them, pass them to and from functions, and store them in data structures such as lists, maps, and sets.
var record = ('first', a: 2, b: true, 'last');
Extensions: Allows you to add new functionality to existing libraries, classes, or types without modifying their source code. They're super useful when you want to "extend" a class with new methods, getters, or setters—especially for classes you don't own (like built-in types or third-party libraries). Extensions don't override existing methods—they add new ones. You can use extensions without importing the actual file, as long as it's in scope. You can also define generic extensions and even add static methods (but static methods are accessed through the extension name, not the instance).
//Example
extension ExtensionName on Type {
// Add methods, getters, or setters
ReturnType methodName(Type arg) {
// logic
}
ReturnType get customGetter => ...;
set customSetter(ValueType value) {
// logic
}
}
//Adding a capitalize method to String
extension StringExtension on String {
String capitalize() {
if (this.isEmpty) return this;
return this[0].toUpperCase() + substring(1).toLowerCase();
}
}
void main() {
String name = 'dart';
print(name.capitalize()); // Output: Dart
}
//Adding a squared getter to int
extension IntMath on int {
int get squared => this * this;
}
void main() {
int num = 5;
print(num.squared); // Output: 25
}
Subscribe to my newsletter
Read articles from Kishore Mongar directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Kishore Mongar
Kishore Mongar
I am passionate about creating seamless web experiences with modern technologies. My unwavering commitment to staying at the forefront of the industry is fueled by a goal for continuous learning and professional development.