Lesson 16 min

Static Members & Enums

00:00 / 00:00

Static Members & Enums

In this lesson, we'll explore two powerful features that let you organize class-level data and represent fixed sets of values in a clean, type-safe way.


Part 1: Static Members

So far, every variable and method we've defined belongs to an instance of a class — you create an object, and that object owns its data. But sometimes, data or behavior belongs to the class itself, not to any particular object.

That's where static members come in.


Static Variables (Class-Level State)

A static variable is shared across all instances of a class. There's only one copy, regardless of how many objects you create.

dart
class Counter { static int _count = 0; // Shared by all instances String label; Counter(this.label) { _count++; print('Created counter "$label". Total counters: $_count'); } static int get count => _count; static void reset() { _count = 0; } } void main() { var c1 = Counter('Alpha'); // Total counters: 1 var c2 = Counter('Beta'); // Total counters: 2 var c3 = Counter('Gamma'); // Total counters: 3 print(Counter.count); // 3 — accessed on class, not instance Counter.reset(); print(Counter.count); // 0 }

[!NOTE] Static variables are accessed using the class name (Counter.count), not via an instance (c1.count would be a warning in strict mode and is bad practice).


Static Methods

A static method belongs to the class, not to an instance. It cannot access instance variables or this.

dart
class MathUtils { // Pure utility — no instance state needed static double square(double x) => x * x; static double cube(double x) => x * x * x; static int clamp(int value, int min, int max) { if (value < min) return min; if (value > max) return max; return value; } static bool isEven(int n) => n % 2 == 0; static List<int> range(int start, int end) { return List.generate(end - start, (i) => start + i); } } void main() { print(MathUtils.square(4)); // 16.0 print(MathUtils.cube(3)); // 27.0 print(MathUtils.clamp(150, 0, 100)); // 100 print(MathUtils.isEven(7)); // false print(MathUtils.range(1, 6)); // [1, 2, 3, 4, 5] }

Static Constants

Static constants are a very common pattern in Dart — they define fixed values associated with a class:

dart
class AppConfig { static const String appName = 'DartShark'; static const String version = '2.1.0'; static const int maxRetries = 3; static const Duration timeout = Duration(seconds: 30); static const String baseUrl = 'https://api.dartshark.dev'; } void main() { print(AppConfig.appName); // DartShark print(AppConfig.version); // 2.1.0 print(AppConfig.maxRetries);// 3 print(AppConfig.baseUrl); // https://api.dartshark.dev }

[!TIP] Use static const for compile-time constants (values known at compile time). Use static final for runtime constants (computed once at startup, like static final DateTime startTime = DateTime.now()).


When to Use Static Members

Use CaseExample
Utility/helper methodsMathUtils.clamp(), StringUtils.capitalize()
Counters / trackingUser._instanceCount++ in constructor
Configuration constantsAppConfig.baseUrl
Singleton patternDatabase._instance
Factory cacheCaching expensive objects

Cannot Access this in Static Context

Static methods don't belong to any instance, so this is not available:

dart
class Example { String name = 'Dart'; static void badMethod() { // print(this.name); // ❌ Compile error: 'this' is not available in static context // print(name); // ❌ Same error } void goodMethod() { print(this.name); // ✅ Instance method can access instance members } }

Singleton Pattern with Static

A common real-world use of static is the Singleton — ensuring only one instance of a class ever exists:

dart
class DatabaseService { static DatabaseService? _instance; // Private constructor DatabaseService._internal(); // Factory constructor returns the single instance factory DatabaseService() { _instance ??= DatabaseService._internal(); return _instance!; } void query(String sql) { print('Executing: \$sql'); } } void main() { var db1 = DatabaseService(); var db2 = DatabaseService(); print(identical(db1, db2)); // true — same instance! db1.query('SELECT * FROM users'); }

Part 2: Enums

An enum (enumeration) is a special type that represents a fixed set of named constant values. Enums make your code safer and more readable than using raw strings or integers.


Basic Enum

dart
enum Color { red, green, blue } enum Direction { north, south, east, west } enum Status { pending, active, inactive, suspended }

Using an enum:

dart
void main() { Color favoriteColor = Color.blue; print(favoriteColor); // Color.blue // .name — the string name of the value (Dart 2.15+) print(favoriteColor.name); // blue // .index — the zero-based position print(favoriteColor.index); // 2 // All values print(Color.values); // [Color.red, Color.green, Color.blue] }

Switch with Enum

Enums shine when used with switch — Dart can warn you if you forget a case:

dart
enum AppStatus { loading, success, error, empty } void handleStatus(AppStatus status) { switch (status) { case AppStatus.loading: print('⏳ Loading data...'); break; case AppStatus.success: print('✅ Data loaded successfully!'); break; case AppStatus.error: print('❌ Something went wrong.'); break; case AppStatus.empty: print('📭 No data found.'); break; } } void main() { handleStatus(AppStatus.success); // ✅ Data loaded successfully! handleStatus(AppStatus.error); // ❌ Something went wrong. }

[!TIP] Using enums with switch is better than if/else if chains on strings. If you add a new enum value later, Dart will warn you about unhandled cases.


Enhanced Enums (Dart 2.17+)

Dart 2.17 introduced enhanced enums — enums can now have fields, constructors, and methods!

dart
enum UserRole { admin(label: 'Administrator', canDelete: true, level: 3), editor(label: 'Editor', canDelete: false, level: 2), viewer(label: 'Viewer', canDelete: false, level: 1); // Fields final String label; final bool canDelete; final int level; // Constructor const UserRole({ required this.label, required this.canDelete, required this.level, }); // Method bool hasHigherPrivilegeThan(UserRole other) => level > other.level; // Getter bool get isAdmin => this == UserRole.admin; } void main() { var role = UserRole.editor; print(role.label); // Editor print(role.canDelete); // false print(role.level); // 2 print(role.isAdmin); // false print(role.hasHigherPrivilegeThan(UserRole.viewer)); // true print(role.hasHigherPrivilegeThan(UserRole.admin)); // false }

Real Example: DayOfWeek Enum

dart
enum DayOfWeek { monday('Mon', false), tuesday('Tue', false), wednesday('Wed', false), thursday('Thu', false), friday('Fri', false), saturday('Sat', true), sunday('Sun', true); final String abbreviation; final bool isWeekend; const DayOfWeek(this.abbreviation, this.isWeekend); bool get isWeekday => !isWeekend; DayOfWeek get nextDay { final values = DayOfWeek.values; return values[(index + 1) % values.length]; } } void main() { var today = DayOfWeek.friday; print('Today: \${today.name}'); // Today: friday print('Abbreviation: \${today.abbreviation}'); // Abbreviation: Fri print('Is weekend? \${today.isWeekend}'); // Is weekend? false print('Tomorrow: \${today.nextDay.name}'); // Tomorrow: saturday // Find all weekdays var weekdays = DayOfWeek.values.where((d) => d.isWeekday); print('Weekdays: \${weekdays.map((d) => d.abbreviation).join(', ')}'); // Weekdays: Mon, Tue, Wed, Thu, Fri }

Real Example: AppStatus with Display Info

dart
enum AppStatus { loading('Loading...', '⏳'), success('Success!', '✅'), error('An error occurred', '❌'), offline('No internet connection', '📴'); final String message; final String icon; const AppStatus(this.message, this.icon); String get display => '\$icon \$message'; } void showStatus(AppStatus status) { print(status.display); } void main() { showStatus(AppStatus.loading); // ⏳ Loading... showStatus(AppStatus.success); // ✅ Success! showStatus(AppStatus.error); // ❌ An error occurred showStatus(AppStatus.offline); // 📴 No internet connection }

Summary

ConceptKey Idea
static variableShared across all instances; belongs to the class
static methodClass-level behavior; no access to this
static constCompile-time class constant
static finalRuntime class constant (computed once)
this in static❌ Not available — static context has no instance
Basic enumFixed set of named constants
.nameString name of the enum value
.indexZero-based integer position
.valuesList of all enum values
Enhanced enumEnum with fields, constructors, and methods (Dart 2.17+)

[!IMPORTANT] Prefer enums over string constants or integer codes whenever you have a fixed set of options. Enums are type-safe, self-documenting, and work beautifully with switch statements.

In the next lesson, we'll unlock Generics — writing type-safe, reusable code that works with any data type.

WhatsApp