Dart Flutter Article Tips

Taming the Lonely Singleton in Dart

Explore the world of singletons in Dart & Flutter with this comprehensive guide.
Plague Fox 4 min read
Taming the Lonely Singleton in Dart

Ah, the singleton, the lonely pattern of the object-oriented world. Much like the last slice of pizza, it's often misunderstood and underappreciated. But fear not, intrepid Dart developers! We're here to give you the lowdown on singletons in Dart, including their uses, tips, and hilarious examples that'll make you chuckle while you code. So, grab your favorite beverage and buckle up – we're about to embark on a singleton safari in the wild world of Dart!


Singletons: What Are They Good For?

For the uninitiated, a singleton is a design pattern that ensures a class has only one instance while providing a global point of access to that instance. It's perfect for when you need a single source of truth – like a configuration manager, a database connection, or even a universal remote for your smart home. (Just kidding, we're not quite there yet.)

💡
Singletons Between Multiple Isolates
In Dart, isolates are independent units of computation that run concurrently and don't share memory. Each isolate has its own heap, which means that objects created in one isolate are not accessible to other isolates. This poses a challenge when using the singleton pattern, as each isolate will have its own instance of the singleton, rather than sharing a single instance across all isolates.

Here's a simple example of a singleton in Dart:

class MySingleton {
  static final MySingleton _internalSingleton = MySingleton._internal();
  factory MySingleton() => _internalSingleton;
  MySingleton._internal();
}

or

class MySingleton {
  static final MySingleton _internalSingleton = MySingleton._internal();
  static MySingleton get instance => _internalSingleton;
  MySingleton._internal();
}

or

class MySingleton {
  static MySingleton? _internalSingleton;
  factory MySingleton() => _internalSingleton ??= MySingleton._internal();
  MySingleton._internal();
}

Now, let's dive into some tips, tricks, and examples that'll make your singleton experience smoother than a sea of whipped cream!

⚠️
Beware: Examples Not for Production Usage
The examples provided in this article are for educational purposes only and should not be used in production environments without proper modifications, testing, and optimization.

Example: Logger

Let's say you're developing a web application and you want to implement a logger that writes messages to a log file. Using a singleton for your logger ensures there's only one instance controlling the log file, preventing any unexpected behavior.

Here's a simple Logger singleton:

import 'dart:developer' as dev;
import 'dart:io' as io;

class Logger {
  static final Logger _instance = Logger._internal();
  factory Logger() => _instance;
  Logger._internal();

  final io.File _logFile = io.File('application.log');

  void log(String message) {
    DateTime now = DateTime.now();
    String logMessage = '$now: $message';
    _logFile.writeAsStringSync(logMessage, mode: io.FileMode.append);
    dev.log(logMessage);
  }
}

And here's how to use the Logger singleton in your application:

void main() {
  // Get the Logger singleton instance
  Logger logger = Logger();

  // Log a message
  logger.log('Application started');

  // Perform some operations
  // ...

  // Log another message
  logger.log('Application finished');
}

In this example, the Logger singleton handles writing messages to the log file, ensuring that only one instance is interacting with the file at any given time. This prevents any issues with concurrency or file access, providing a consistent and reliable logging experience.


Example: AppConfig

Let's say you're developing an application that requires a centralized configuration manager to store and retrieve app settings, such as API keys, base URLs, and feature toggles. A singleton is a great choice for this purpose, ensuring that all parts of your application have a consistent view of the settings.

Here's a simple AppConfig singleton:

class AppConfig {
  static final AppConfig _instance = AppConfig._internal();
  factory AppConfig() => _instance;
  AppConfig._internal();

  final Map<String, String> _$settings = {
    'api_key': 'your_api_key_here',
    'base_url': 'https://api.domain.tld',
    'theme': 'dark',
  };

  String? getSetting(String key) => _$settings[key];

  void setSetting(String key, String value) => _$settings[key] = value;
}

And here's how to use the AppConfig singleton in your application:

void main() {
  // Get the AppConfig singleton instance
  AppConfig config = AppConfig();

  // Update a setting
  config.setSetting('api_key', '123:abc');

  // Retrieve a setting
  String apiKey = config.getSetting('api_key')!;
  print('API Key: $apiKey');
}

In this example, the AppConfig singleton acts as a centralized configuration manager, providing a single source of truth for app settings. This ensures that all parts of your application have access to the same settings and that updates are consistently reflected across the entire application.


VS Code Snippet for Singletons

Visual Studio Code snippets are a convenient way to quickly generate code templates for common patterns, such as singletons. You can create a custom snippet for creating singletons in Dart with just a few steps. Follow the instructions below to set up a singleton snippet in VS Code:

  1. Open Visual Studio Code.
  2. Click on the gear icon in the lower left corner to open the settings menu.
  3. Select "User Snippets" from the menu.
  4. In the "User Snippets" search bar, type "dart" and select "Dart" from the list of options.
  5. This will open the "dart.json" file, where you can define your custom snippets for Dart. Add the following snippet to the file in your JSON array:
[
  /* Other snippets here */
  "Singleton": {
    "scope": "flutter, dart",
    "prefix": "singleton",
    "description": "Create a singleton class in Dart",
    "body": [
      "/// ${1} singleton class",
      "class ${1:${TM_FILENAME_BASE/(^[a-zA-Z]{1})(.*)/${1:/upcase}${2:/camelcase}/g}} {",
      "  static final ${1} _internalSingleton = ${1}._internal();",
      "  factory ${1}() => _internalSingleton;",
      "  ${1}._internal();\n",
      "  ${0}",
      "}"
    ]
  }
]
  1. Save the "dart.json" file and close it.
  2. Now, when you type "singleton" in a Dart file, VS Code will suggest the singleton snippet. Press Enter or Tab to insert the snippet.
  3. The snippet will generate a basic singleton template, and you can start typing the desired class name.

With this custom VS Code snippet, you can quickly create singletons in Dart and streamline your development process.

Share
Comments
More from Plague Fox
Linter, Analyzer & Dart Code Metrics
Dart Flutter Podcast Russian

Linter, Analyzer & Dart Code Metrics

Join us in this stream where we will take a detailed look at Dart and Flutter tools such as linter, analyzer, and Dart Code Metrics. We will discuss how these tools help improve code quality and enhance its readability, as well as how they can make your development more efficient.
Plague Fox

Plague Fox

Engineer by day, fox by night. Talks about Flutter & Dart.

Great! You’ve successfully signed up.

Welcome back! You've successfully signed in.

You've successfully subscribed to Plague Fox.

Success! Check your email for magic link to sign-in.

Success! Your billing info has been updated.

Your billing was not updated.