Lesson 12 min

Dart Program Structure

00:00 / 00:00

Dart Program Structure

Every programming language has a structure — a set of rules about how files are organized and how different pieces fit together. Understanding Dart's program structure from the start will prevent confusion later and help you read and write code with confidence.

In this lesson, we'll dissect the anatomy of a Dart file from top to bottom.


The Big Picture

A complete Dart file can contain these elements, generally in this order:

1. Library declaration (optional)
2. Import statements
3. Top-level variables
4. Top-level functions
5. Classes / Enums / Mixins / Extensions
6. main() function (entry point)

You don't need all of these in every file. The only requirement for a runnable program is a main() function. Let's explore each section.


Section 1: Import Statements

Imports bring in code from other libraries — either Dart's built-in libraries, packages from pub.dev, or other files in your project.

Dart Core Library Imports

dart
import 'dart:math'; // Math functions: sin, cos, Random, etc. import 'dart:io'; // File I/O, stdin, stdout import 'dart:convert'; // JSON encoding/decoding import 'dart:async'; // Future, Stream, async utilities import 'dart:collection'; // Queue, LinkedHashMap, etc.

Important: dart:core is the only library that is auto-imported. It provides print(), String, int, List, Map, Object, Exception, and many other fundamental types. You never need to write import 'dart:core';.

Package Imports (from pub.dev)

After running dart pub get, import packages like:

dart
import 'package:http/http.dart' as http; import 'package:path/path.dart';

Relative File Imports

Import other Dart files in your own project:

dart
import 'utils/string_helpers.dart'; import '../models/user.dart';

Import with Aliases (as)

Prevent naming conflicts using as:

dart
import 'dart:math' as math; void main() { print(math.pi); // 3.141592653589793 print(math.sqrt(16)); // 4.0 }

Import with Show / Hide

Control exactly what gets imported:

dart
// Only import Random from dart:math import 'dart:math' show Random; // Import everything EXCEPT pi import 'dart:math' hide pi;

Section 2: Top-Level Variables

Variables declared outside of any class or function are called top-level variables. They exist at the file scope and are accessible everywhere in the file.

dart
// Top-level constant — known at compile time const String appName = 'MyApp'; // Top-level final — set once at runtime final String version = '1.0.0'; // Top-level mutable variable int launchCount = 0;

When to Use Top-Level Variables

  • Constants that apply to the whole file or program
  • Configuration values that don't change
  • Avoid mutable top-level variables in large programs (prefer encapsulation in classes)
dart
// Good use of top-level constants const double pi = 3.14159265358979; const int maxRetries = 3; const String baseUrl = 'https://api.example.com';

Section 3: Top-Level Functions

Functions defined outside of any class are top-level functions. main() is the most important one, but you can define as many as you need.

dart
// Top-level utility function String greet(String name) { return 'Hello, $name!'; } // Top-level calculation function int square(int n) => n * n; // Arrow function shorthand (single expression) bool isEven(int n) => n % 2 == 0; void main() { print(greet('Alice')); // Hello, Alice! print(square(5)); // 25 print(isEven(4)); // true }

Arrow functions (=>) are shorthand for single-expression functions. int square(int n) => n * n; is identical to writing a full return statement inside curly braces.


Section 4: Classes

Classes are blueprints for objects — they bundle data (fields) and behavior (methods) together. Dart is fully object-oriented.

dart
// Class definition class Person { // Fields (instance variables) String name; int age; // Constructor Person(this.name, this.age); // Named constructor Person.anonymous() : name = 'Unknown', age = 0; // Instance method void introduce() { print('Hi, I\'m $name and I\'m $age years old.'); } // Getter bool get isAdult => age >= 18; // Override toString String toString() => 'Person($name, $age)'; }

Enums

Enums define a fixed set of named values:

dart
enum Direction { north, south, east, west } enum Status { active, inactive, pending; // Enums can have methods in Dart 3 String get label => name.toUpperCase(); }

Section 5: The main() Function

As covered in the previous lesson, main() is the entry point — where Dart begins execution. It must be a top-level function.

dart
void main() { // Program starts here } // OR with command-line arguments: void main(List<String> args) { print('Arguments: $args'); }

There is exactly one main() per runnable Dart program.


Section 6: Comments

Comments are notes for humans — the Dart compiler ignores them completely. Good comments explain why something is done, not what is done.

Single-Line Comments

dart
// This is a single-line comment int x = 42; // Comments can appear after code too

Multi-Line Comments

dart
/* This is a multi-line comment. It can span as many lines as needed. Useful for temporarily disabling blocks of code. */ int y = 10;

Documentation Comments (///)

Triple-slash comments generate API documentation with dart doc:

dart
/// Calculates the area of a circle. /// /// Takes the [radius] as input and returns the area as a [double]. /// Throws an [ArgumentError] if [radius] is negative. /// /// Example: /// ```dart /// double area = circleArea(5.0); /// print(area); // 78.53981633974483 /// ``` double circleArea(double radius) { if (radius < 0) throw ArgumentError('Radius cannot be negative'); return 3.14159265358979 * radius * radius; }

When you hover over circleArea() in VS Code, you'll see this documentation rendered beautifully.

Convention: Use /// for public APIs (functions, classes, properties that others will use). Use // for internal implementation notes.


File Naming Conventions

Dart has clear, community-enforced naming rules for files:

ConventionExample
snake_case for file namesmy_helper.dart, string_utils.dart
PascalCase for class namesclass MyHelper {}
camelCase for variables/functionsmyVariable, calculateTotal()
SCREAMING_SNAKE_CASE for constantsconst MAX_SIZE = 100; (or camelCase const)
lowercase for packagespackage:my_package
✅  user_profile.dart
✅  http_client.dart
✅  main.dart

❌  UserProfile.dart
❌  httpClient.dart
❌  Main.Dart

Single-File vs Multi-File Programs

Single-File Program

Suitable for scripts, exercises, and simple tools:

dart
// everything in one file: main.dart const String appVersion = '1.0.0'; class Calculator { int add(int a, int b) => a + b; int subtract(int a, int b) => a - b; } void printBanner() { print('=== Calculator v$appVersion ==='); } void main() { printBanner(); var calc = Calculator(); print(calc.add(10, 5)); // 15 print(calc.subtract(10, 5)); // 5 }

Multi-File Program

For larger projects, split code across files:

lib/
  models/
    user.dart
    product.dart
  services/
    auth_service.dart
    api_service.dart
  utils/
    string_utils.dart
    date_utils.dart
bin/
  main.dart

bin/main.dart:

dart
import '../lib/models/user.dart'; import '../lib/services/auth_service.dart'; import '../lib/utils/string_utils.dart'; void main() { var user = User(name: 'Alice', email: 'alice@example.com'); print(user); }

Putting It All Together — Full Example Program

Here is a complete Dart file showcasing every structural element we discussed:

dart
// ============================================================ // FILE: life_calculator.dart // DESCRIPTION: Demonstrates full Dart program structure // ============================================================ // --- IMPORTS --- import 'dart:math' show Random; // --- TOP-LEVEL CONSTANT --- const String appName = 'Life Calculator'; const int currentYear = 2024; // --- ENUM --- enum LifeStage { child, teenager, adult, senior; String get label { switch (this) { case LifeStage.child: return 'Child (0-12)'; case LifeStage.teenager: return 'Teenager (13-17)'; case LifeStage.adult: return 'Adult (18-64)'; case LifeStage.senior: return 'Senior (65+)'; } } } // --- CLASS --- /// Represents a person and provides life-related calculations. class Person { /// The person's full name. final String name; /// The person's birth year. final int birthYear; /// Creates a [Person] with the given [name] and [birthYear]. Person(this.name, this.birthYear); /// The person's current age. int get age => currentYear - birthYear; /// The person's current life stage. LifeStage get lifeStage { if (age <= 12) return LifeStage.child; if (age <= 17) return LifeStage.teenager; if (age <= 64) return LifeStage.adult; return LifeStage.senior; } String toString() => '$name (born $birthYear, age $age)'; } // --- TOP-LEVEL FUNCTIONS --- /// Generates a random birth year between [minAge] and [maxAge]. int randomBirthYear(int minAge, int maxAge) { final random = Random(); final age = minAge + random.nextInt(maxAge - minAge); return currentYear - age; } /// Prints a formatted summary for a [Person]. void printPersonSummary(Person person) { print('Name: ${person.name}'); print('Birth Year: ${person.birthYear}'); print('Age: ${person.age}'); print('Life Stage: ${person.lifeStage.label}'); print('---'); } // --- MAIN FUNCTION (Entry Point) --- void main() { print('=== $appName ===\n'); /* Create a list of people with different ages */ final people = [ Person('Alice', 1990), Person('Bob', 2010), Person('Charlie', 1955), Person('Diana', randomBirthYear(13, 17)), ]; // Print summary for each person for (final person in people) { printPersonSummary(person); } print('Total people: ${people.length}'); }

Output:

=== Life Calculator ===

Name:       Alice
Birth Year: 1990
Age:        34
Life Stage: Adult (18-64)
---
Name:       Bob
Birth Year: 2010
Age:        14
Life Stage: Teenager (13-17)
---
Name:       Charlie
Birth Year: 1955
Age:        69
Life Stage: Senior (65+)
---
Name:       Diana
Birth Year: (random)
Age:        (random)
Life Stage: Teenager (13-17)
---
Total people: 4

Dart Program Structure — Cheat Sheet

┌──────────────────────────────────────────────┐
│              DART FILE STRUCTURE             │
├──────────────────────────────────────────────┤
│  // library declaration (optional)           │
│  library my_library;                         │
├──────────────────────────────────────────────┤
│  // imports                                  │
│  import 'dart:math';                         │
│  import 'package:http/http.dart';            │
│  import 'other_file.dart';                   │
├──────────────────────────────────────────────┤
│  // top-level variables                      │
│  const String APP_NAME = 'App';              │
│  int counter = 0;                            │
├──────────────────────────────────────────────┤
│  // enums, classes, mixins, extensions       │
│  enum Color { red, green, blue }             │
│  class MyClass { ... }                       │
├──────────────────────────────────────────────┤
│  // top-level functions                      │
│  void helper() { ... }                       │
│  int calculate(int x) => x * 2;              │
├──────────────────────────────────────────────┤
│  // entry point                              │
│  void main() { ... }                         │
└──────────────────────────────────────────────┘

Summary

In this lesson, you learned the complete anatomy of a Dart file:

ElementPurpose
ImportsBring in external libraries (dart:, package:, relative)
dart:coreAuto-imported — provides String, int, print(), etc.
Top-level variablesFile-scoped constants and shared state
Top-level functionsReusable logic outside of any class
Classes / EnumsBlueprints for objects; named sets of values
main()The entry point — execution begins here
//Single-line comment
/* */Multi-line comment
///Documentation comment (generates API docs)
File namingsnake_case for files, PascalCase for classes

Next Up: Now that you understand how a Dart program is structured, we'll dive into variables and data types — the building blocks of every Dart program.

WhatsApp