Dart UnmodifiableMapBase

Dart UnmodifiableMapBase tutorial shows how to work with immutable map collections in Dart using UnmodifiableMapBase class.

Dart UnmodifiableMapBase

Dart UnmodifiableMapBase

last modified April 4, 2025

In Dart, UnmodifiableMapBase is an abstract base class for creating unmodifiable map views. It provides read-only access to map data while preventing modifications.

UnmodifiableMapBase implements the Map interface but throws UnsupportedError for all mutating operations. It’s useful for exposing map data safely without allowing changes.

Basic UnmodifiableMapBase Usage

Here’s how to create a simple unmodifiable map by extending UnmodifiableMapBase.

main.dart

import ‘dart:collection’;

class UnmodifiableMapView<K, V> extends UnmodifiableMapBase<K, V> { final Map<K, V> _source;

UnmodifiableMapView(this._source);

@override V? operator [](Object? key) => _source[key];

@override Iterable<K> get keys => _source.keys; }

void main() {

final source = {‘a’: 1, ‘b’: 2, ‘c’: 3}; final unmodifiable = UnmodifiableMapView(source);

print(unmodifiable[‘a’]); // 1 print(unmodifiable.length); // 3

try { unmodifiable[’d’] = 4; // Throws } catch (e) { print(‘Error: $e’); } }

We create an UnmodifiableMapView class that wraps a source map. The [] operator and keys getter must be implemented. Attempts to modify throw exceptions.

$ dart main.dart 1 3 Error: Unsupported operation: Cannot modify unmodifiable map

Creating an Unmodifiable Map with Factory

A more practical approach uses a factory method to create unmodifiable maps.

main.dart

import ‘dart:collection’;

class UnmodifiableMapFactory { static Map<K, V> create<K, V>(Map<K, V> source) { return _UnmodifiableMapWrapper(source); } }

class _UnmodifiableMapWrapper<K, V> extends UnmodifiableMapBase<K, V> { final Map<K, V> _source;

_UnmodifiableMapWrapper(this._source);

@override V? operator [](Object? key) => _source[key];

@override Iterable<K> get keys => _source.keys; }

void main() {

final source = {‘x’: 10, ‘y’: 20, ‘z’: 30}; final unmodifiable = UnmodifiableMapFactory.create(source);

print(unmodifiable[‘y’]); // 20 print(unmodifiable.containsKey(‘x’)); // true

try { unmodifiable.clear(); // Throws } catch (e) { print(‘Error: $e’); } }

This example shows a factory pattern for creating unmodifiable maps. The private _UnmodifiableMapWrapper class implements the unmodifiable behavior.

$ dart main.dart 20 true Error: Unsupported operation: Cannot modify unmodifiable map

Unmodifiable Map with Custom Key-Value Types

UnmodifiableMapBase works with custom types while maintaining immutability.

main.dart

import ‘dart:collection’;

class Product { final String id; final String name;

Product(this.id, this.name);

@override String toString() => ‘Product($id, $name)’; }

class UnmodifiableProductMap extends UnmodifiableMapBase<String, Product> { final Map<String, Product> _products;

UnmodifiableProductMap(this._products);

@override Product? operator [](Object? key) => _products[key];

@override Iterable<String> get keys => _products.keys; }

void main() {

final products = { ‘p1’: Product(‘p1’, ‘Laptop’), ‘p2’: Product(‘p2’, ‘Phone’), ‘p3’: Product(‘p3’, ‘Tablet’) };

final unmodifiable = UnmodifiableProductMap(products);

print(unmodifiable[‘p2’]); // Product(p2, Phone) print(unmodifiable.values.where((p) => p.name.length > 5).toList());

try { unmodifiable[‘p4’] = Product(‘p4’, ‘Monitor’); // Throws } catch (e) { print(‘Modification error: $e’); } }

We create a type-safe unmodifiable map for Product objects. The map provides read-only access while preventing any modifications to the product collection.

$ dart main.dart Product(p2, Phone) [Product(p1, Laptop), Product(p3, Tablet)] Modification error: Unsupported operation: Cannot modify unmodifiable map

Combining UnmodifiableMapBase with Other Collections

UnmodifiableMapBase can be combined with other collection types for complex immutable data structures.

main.dart

import ‘dart:collection’;

class ImmutableConfig extends UnmodifiableMapBase<String, dynamic> { final Map<String, dynamic> _config; final List<String> _log;

ImmutableConfig(this._config, this._log);

@override dynamic operator [](Object? key) { _log.add(‘Accessed: $key’); return _config[key]; }

@override Iterable<String> get keys => _config.keys; }

void main() {

final config = { ’timeout’: 30, ‘retries’: 3, ‘debug’: false };

final accessLog = <String>[]; final immutableConfig = ImmutableConfig(config, accessLog);

print(immutableConfig[’timeout’]); // 30 print(immutableConfig[‘debug’]); // false

try { immutableConfig[’timeout’] = 60; // Throws } catch (e) { print(‘Error: $e’); }

print(‘Access log: $accessLog’); }

This example combines an unmodifiable map with a logging list. The map remains immutable while tracking access attempts. The log list can be accessed separately.

$ dart main.dart 30 false Error: Unsupported operation: Cannot modify unmodifiable map Access log: [Accessed: timeout, Accessed: debug]

Advanced UnmodifiableMapBase with Lazy Loading

We can implement lazy loading in an unmodifiable map by overriding the accessors.

main.dart

import ‘dart:collection’;

class LazyUnmodifiableMap extends UnmodifiableMapBase<String, String> { final Map<String, String> _source; final Map<String, String> _cache = {};

LazyUnmodifiableMap(this._source);

@override String? operator [](Object? key) { if (key is! String) return null;

if (!_cache.containsKey(key)) {
  print('Loading $key from source...');
  _cache[key] = _source[key] ?? 'DEFAULT';
}

return _cache[key];

}

@override Iterable<String> get keys => _source.keys; }

void main() {

final source = { ‘config1’: ‘Value1’, ‘config2’: ‘Value2’, ‘config3’: ‘Value3’ };

final lazyMap = LazyUnmodifiableMap(source);

print(‘First access:’); print(lazyMap[‘config2’]); // Loads and caches

print(’\nSecond access:’); print(lazyMap[‘config2’]); // Uses cache

print(’\nAll keys:’); print(lazyMap.keys.join(’, ‘));

try { lazyMap[’new’] = ‘value’; // Throws } catch (e) { print(’\nError: $e’); } }

This advanced example implements lazy loading with caching. Values are loaded on first access and cached for subsequent requests. The map remains unmodifiable.

$ dart main.dart First access: Loading config2 from source… Value2

Second access: Value2

All keys: config1, config2, config3

Error: Unsupported operation: Cannot modify unmodifiable map

Best Practices

  • Immutable Design: Use for exposing internal state safely.

  • Performance: Consider caching for expensive lookups.

  • Type Safety: Create specific subclasses for strong typing.

  • Documentation: Clearly document immutability.

  • Composition: Combine with other patterns like factory.

Source

Dart UnmodifiableMapBase Documentation

This tutorial covered Dart’s UnmodifiableMapBase with practical examples demonstrating immutable map implementations and patterns.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Dart tutorials.

ad ad