Skip to content
· 8 min read · 0 views

Dart Deep Dive: Understanding the Programming Language for Flutter

A comprehensive guide to Dart programming for Flutter development, covering variables, data types, control flow, OOP, collections, null safety, and async programming.

// table of contents (30 sections)

Why Dart?

Before Flutter, you need to understand Dart. It’s the language Flutter is built on, and for good reason:

  • Fast compilation with JIT for development and AOT for production
  • Null safety built into the type system
  • Familiar syntax if you come from Java, JavaScript, or Kotlin
  • Single codebase for mobile, web, and desktop

Dart was created by Google in 2011 and redesigned in 2018 to be the foundation of Flutter. It compiles to native ARM code for mobile and JavaScript for web.


Variables & Data Types

Dart is a statically typed language, but it supports type inference so you don’t always need explicit type annotations.

// Explicit types
String name = 'Abdu Ar Rahman';
int age = 28;
double height = 1.75;
bool isDeveloper = true;

// Type inference with var
var city = 'Jakarta';      // String
var year = 2026;            // int
var isActive = true;        // bool

// Constants
const pi = 3.14159;         // Compile-time constant
final now = DateTime.now(); // Runtime constant (can't be reassigned)

Key Difference: const vs final

KeywordWhen SetMutableUse Case
constCompile timeNoHardcoded values
finalRuntimeNoValues set once (e.g. from API)
varRuntimeYesGeneral variables

Numbers

Dart has two number types:

int count = 42;           // 64-bit integer
double price = 29.99;     // 64-bit double (IEEE 754)
num value = 10;           // Can be int or double

Strings

Strings are UTF-16 and support interpolation:

var name = 'Flutter';
var greeting = 'Hello, $name!';
var expression = '2 + 2 = ${2 + 2}';

// Multi-line strings
var description = '''
Dart is a modern language
optimized for UI development.
''';

// Raw strings (no escape processing)
var path = r'C:\Users\dev\project';

Operators

Dart supports all the standard operators you’d expect:

// Arithmetic
int a = 10 + 3;    // 13
int b = 10 - 3;    // 7
int c = 10 * 3;    // 30
double d = 10 / 3; // 3.333...
int e = 10 ~/ 3;   // 3 (integer division)
int f = 10 % 3;    // 1 (modulo)

// Comparison
==  !=  >  <  >=  <=

// Logical
&&  ||  !

// Null-aware
var result = nullableValue ?? 'default';

The ?? operator is a lifesaver for null safety. It returns the left side if non-null, otherwise the right side.


Control Flow

If / Else

var score = 85;

if (score >= 90) {
  print('Grade A');
} else if (score >= 80) {
  print('Grade B');
} else {
  print('Keep trying!');
}

Switch

var day = 'Monday';

switch (day) {
  case 'Monday':
    print('Start of the week');
    break;
  case 'Friday':
    print('Almost weekend');
    break;
  default:
    print('Regular day');
}

Loops

// For loop
for (var i = 0; i < 5; i++) {
  print('Iteration $i');
}

// While loop
var count = 0;
while (count < 5) {
  count++;
}

// Do-while (runs at least once)
do {
  count--;
} while (count > 0);

// For-in (for iterables)
var languages = ['Dart', 'Flutter', 'Kotlin'];
for (var lang in languages) {
  print(lang);
}

Functions

Dart functions are first-class objects. You can pass them as arguments, return them, and assign them to variables.

// Basic function
int add(int a, int b) {
  return a + b;
}

// Arrow syntax for single expressions
int multiply(int a, int b) => a * b;

// Optional positional parameters
void greet(String name, [String? title]) {
  print('Hello, ${title ?? ''} $name');
}

// Named parameters (recommended for clarity)
void createUser({required String name, required int age, String role = 'user'}) {
  print('Creating $name, age $age, role $role');
}

// Calling with named params
createUser(name: 'Abdu', age: 28, role: 'admin');

Anonymous Functions & Closures

// Anonymous function (lambda)
var numbers = [1, 2, 3, 4, 5];
var doubled = numbers.map((n) => n * 2).toList(); // [2, 4, 6, 8, 10]

// Closure — captures variables from outer scope
Function makeAdder(int addBy) {
  return (int i) => addBy + i;
}

var add2 = makeAdder(2);
print(add2(3)); // 5

Object-Oriented Programming

Dart is a fully object-oriented language. Everything is an object, even numbers and null.

Classes

class Developer {
  String name;
  String specialty;
  int yearsOfExperience;

  // Constructor
  Developer(this.name, this.specialty, this.yearsOfExperience);

  // Named constructor
  Developer.junior(this.name)
      : specialty = 'General',
        yearsOfExperience = 0;

  // Method
  void introduce() {
    print('Hi, I\'m $name, a $specialty developer with $yearsOfExperience years of experience.');
  }

  // Getter
  bool get isSenior => yearsOfExperience >= 5;

  // Setter
  set promote(int years) {
    yearsOfExperience = years;
    print('$name now has $years years of experience!');
  }
}

Inheritance

class FlutterDeveloper extends Developer {
  List<String> projects;

  FlutterDeveloper(String name, int years, this.projects)
      : super(name, 'Flutter', years);

  @override
  void introduce() {
    super.introduce();
    print('Projects: ${projects.join(', ')}');
  }
}

Abstract Classes & Interfaces

abstract class Repository {
  Future<List<String>> getAll();
  Future<String> getById(int id);
}

class ApiRepository implements Repository {
  @override
  Future<List<String>> getAll() async {
    // Fetch from API
    return ['item1', 'item2'];
  }

  @override
  Future<String> getById(int id) async {
    return 'item$id';
  }
}

Mixins

Mixins let you share code between classes without inheritance:

mixin Loggable {
  void log(String message) {
    print('[${DateTime.now()}] $message');
  }
}

mixin Serializable {
  Map<String, dynamic> toJson();
}

class UserModel with Loggable, Serializable {
  final String name;
  final String email;

  UserModel(this.name, this.email);

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

  void save() {
    log('Saving user: $name');
    // Save to database
  }
}

Collections

List

// Fixed-length vs growable
var fruits = ['Apple', 'Banana', 'Cherry']; // Growable
var fixed = List<String>.filled(3, '');       // Fixed length

// Common operations
fruits.add('Date');
fruits.addAll(['Elderberry', 'Fig']);
fruits.remove('Banana');
fruits.sort();

// Higher-order methods
var upperFruits = fruits.map((f) => f.toUpperCase()).toList();
var longFruits = fruits.where((f) => f.length > 5).toList();
var hasCherry = fruits.any((f) => f == 'Cherry');

Map

var scores = {
  'Alice': 95,
  'Bob': 87,
  'Charlie': 92,
};

// Add/update
scores['David'] = 88;

// Access
var aliceScore = scores['Alice'] ?? 0;

// Iterate
scores.forEach((name, score) {
  print('$name scored $score');
});

Set

var uniqueTags = <String>{'dart', 'flutter', 'mobile'};
uniqueTags.add('dart'); // Won't duplicate
uniqueTags.contains('dart'); // true

// Set operations
var setA = {1, 2, 3};
var setB = {3, 4, 5};
var union = setA.union(setB);        // {1, 2, 3, 4, 5}
var intersection = setA.intersection(setB); // {3}

Null Safety

Dart’s sound null safety means the compiler guarantees that non-nullable types never contain null.

// Non-nullable (default)
String name = 'Abdu'; // Can NEVER be null
// name = null; // Compile error!

// Nullable (add ?)
String? nickname; // CAN be null

// Null-aware operators
var result = nickname?.length;    // null if nickname is null
var fallback = nickname ?? 'N/A'; // 'N/A' if nickname is null
nickname ??= 'Default';           // Assign only if null

// Null assertion (use only when you're certain)
int length = nickname!.length; // Throws if null!

Late Keyword

Use late when a variable will be initialized later but not at declaration:

class ProfilePage extends StatefulWidget {
  @override
  State<ProfilePage> createState() => _ProfilePageState();
}

class _ProfilePageState extends State<ProfilePage> {
  late String userName; // Will be set in initState()

  @override
  void initState() {
    super.initState();
    userName = fetchUserName();
  }
}

Async Programming

Dart uses the Event Loop model with Future and async/await.

Future

Future<String> fetchUserName() {
  return Future.delayed(
    Duration(seconds: 2),
    () => 'Abdu Ar Rahman',
  );
}

Async / Await

Future<void> loadProfile() async {
  print('Loading...');

  try {
    var name = await fetchUserName();
    print('Welcome, $name!');
  } catch (e) {
    print('Error: $e');
  } finally {
    print('Done loading.');
  }
}

Stream

For multiple values over time (like WebSocket data):

Stream<int> countDown() async* {
  for (var i = 5; i > 0; i--) {
    await Future.delayed(Duration(seconds: 1));
    yield i;
  }
}

// Listening
countDown().listen(
  (value) => print('$value...'),
  onDone: () => print('Go!'),
);

Exception Handling

try {
  var result = 10 ~/ 0; // Throws IntegerDivisionByZeroException
} on IntegerDivisionByZeroException {
  print('Cannot divide by zero');
} on FormatException catch (e) {
  print('Format error: $e');
} catch (e, stackTrace) {
  print('Unexpected error: $e');
  print('Stack trace: $stackTrace');
} finally {
  // Always runs
  print('Cleanup done');
}

Quick Reference

ConceptSyntaxExample
Variablevar name = valuevar count = 0
NullableType? nameString? name
Constantconst name = valueconst pi = 3.14
FunctionReturnType name(params)int add(int a, int b)
Arrow fn=> expression(x) => x * 2
Classclass Name {}class User {}
Asyncasync/awaitawait fetch()
Streamasync*/yieldyield value

What’s Next?

Now that you understand Dart fundamentals, you’re ready to build Flutter apps. The next step is learning about Flutter widgets, state management, and networking.

Bismillah, happy coding! 🚀

You might also like

Enjoyed This Post?

Want to discuss the topic, have questions, or looking to collaborate on something similar? Drop a comment below or reach out directly.

Discussion