BindingBase class Null safety

Base class for mixins that provide singleton services.

The Flutter engine (dart:ui) exposes some low-level services, but these are typically not suitable for direct use, for example because they only provide a single callback which an application may wish to multiplex to allow multiple listeners.

Bindings provide the glue between these low-level APIs and the higher-level framework APIs. They bind the two together, whence the name.

Implementing a binding mixin

A library would typically create a new binding mixin to expose a feature in dart:ui. This is rare in general, but it is something that an alternative framework would do, e.g. if a framework were to replace the widgets library with an alternative API but still wished to leverage the services and foundation libraries.

To create a binding mixin, declare a mixin on the BindingBase class and whatever other bindings the concrete binding must implement for this binding mixin to be useful.

The mixin is guaranteed to only be constructed once in the lifetime of the app; this is handled by initInstances.

A binding mixin must at a minimum implement the following features:

  • The initInstances method, which must call super.initInstances and set an _instance static field to this.
  • An instance static getter, which must return that field using checkInstance.

In addition, it should implement whatever singleton features the library needs.

As a general rule, the less can be placed in the binding, the better. Prefer having APIs that takes objects rather than having them refer to global singletons. Bindings are best limited to exposing features that literally only exist once, for example, the APIs in dart:ui.

Here is a basic example of a binding that implements these features. It relies on another fictional binding called BarBinding.
mixin FooBinding on BindingBase, BarBinding {
  @override
  void initInstances() {
    super.initInstances();
    _instance = this;
    // ...binding initialization...
  }

  static FooBinding get instance => BindingBase.checkInstance(_instance);
  static FooBinding? _instance;

  // ...binding features...
}

Implementing a binding class

The top-most layer used to write the application (e.g. the Flutter widgets library) will have a concrete class that inherits from BindingBase and uses all the various BindingBase mixins (such as ServicesBinding). The widgets library in Flutter introduces a binding called WidgetsFlutterBinding.

A binding class should mix in the relevant bindings from each layer that it wishes to expose, and should have an ensureInitialized method that constructs the class if that layer's mixin's _instance field is null. This allows the binding to be overriden by developers who have more specific needs, while still allowing other code to call ensureInitialized when a binding is needed.

A typical binding class is shown below. The ensureInitialized method's return type is the library's binding mixin, rather than the concrete class.
class FooLibraryBinding extends BindingBase with BarBinding, FooBinding {
  static FooBinding ensureInitialized() {
    if (FooBinding._instance == null) {
      FooLibraryBinding();
    }
    return FooBinding.instance;
  }
}

Implementers

Constructors

BindingBase()
Default abstract constructor for bindings.

Properties

hashCode int
The hash code for this object.
read-onlyinherited
locked bool
Whether lockEvents is currently locking events.
protected">@protectedread-only
platformDispatcher PlatformDispatcher
The ui.PlatformDispatcher to which this binding is bound.
read-only
runtimeType Type
A representation of the runtime type of the object.
read-onlyinherited
window SingletonFlutterWindow
The main window to which this binding is bound.
read-only

Methods

initInstances() → void
The initialization method. Subclasses override this method to hook into the platform and otherwise configure their services. Subclasses must call "super.initInstances()".
mustCallSuper">@mustCallSuperprotected">@protected
initServiceExtensions() → void
Called when the binding is initialized, to register service extensions.
mustCallSuper">@mustCallSuperprotected">@protected
lockEvents(Future<void> callback()) Future<void>
Locks the dispatching of asynchronous events and callbacks until the callback's future completes.
protected">@protected
noSuchMethod(Invocation invocation) → dynamic
Invoked when a non-existent method or property is accessed.
inherited
performReassemble() Future<void>
This method is called by reassembleApplication to actually cause the application to reassemble, e.g. after a hot reload.
mustCallSuper">@mustCallSuperprotected">@protected
postEvent(String eventKind, Map<String, dynamic> eventData) → void
All events dispatched by a BindingBase use this method instead of calling developer.postEvent directly so that tests for BindingBase can track which events were dispatched by overriding this method.
protected">@protected
reassembleApplication() Future<void>
Cause the entire application to redraw, e.g. after a hot reload.
registerBoolServiceExtension({required String name, required AsyncValueGetter<bool> getter, required AsyncValueSetter<bool> setter}) → void
Registers a service extension method with the given name (full name "ext.flutter.name"), which takes a single argument "enabled" which can have the value "true" or the value "false" or can be omitted to read the current value. (Any value other than "true" is considered equivalent to "false". Other arguments are ignored.)
protected">@protected
registerNumericServiceExtension({required String name, required AsyncValueGetter<double> getter, required AsyncValueSetter<double> setter}) → void
Registers a service extension method with the given name (full name "ext.flutter.name"), which takes a single argument with the same name as the method which, if present, must have a value that can be parsed by double.parse, and can be omitted to read the current value. (Other arguments are ignored.)
protected">@protected
registerServiceExtension({required String name, required ServiceExtensionCallback callback}) → void
Registers a service extension method with the given name (full name "ext.flutter.name").
protected">@protected
registerSignalServiceExtension({required String name, required AsyncCallback callback}) → void
Registers a service extension method with the given name (full name "ext.flutter.name"), which takes no arguments and returns no value.
protected">@protected
registerStringServiceExtension({required String name, required AsyncValueGetter<String> getter, required AsyncValueSetter<String> setter}) → void
Registers a service extension method with the given name (full name "ext.flutter.name"), which optionally takes a single argument with the name "value". If the argument is omitted, the value is to be read, otherwise it is to be set. Returns the current value.
protected">@protected
toString() String
A string representation of this object.
override
unlocked() → void
Called by lockEvents when events get unlocked.
mustCallSuper">@mustCallSuperprotected">@protected

Operators

operator ==(Object other) bool
The equality operator.
inherited

Static Properties

debugReassembleConfig DebugReassembleConfig?
Additional configuration used by the framework during hot reload.
read / write

Static Methods

checkInstance<T extends BindingBase>(T? instance) → T
A method that shows a useful error message if the given binding instance is not initialized.
protected">@protected
debugBindingType() Type?
In debug builds, the type of the current binding, if any, or else null.