Use case
If a developer wants to call runApp within a custom Zone with zoneValues and the zoneValues rely on the underlying platform, they must call WidgetsFlutterBinding.ensureInitialized() like:
final token = Object(); void main() async { WidgetsFlutterBinding.ensureInitialized(); final value = await _getPlatformValue(); runZoned( () => runApp(MyApp()), zoneValues: {token: value}, ); }
This appears to work as expected, however, because WidgetsFlutterBinding also initializes other bindings like GestureBinding the zoneValue is not accessible within onPressed callbacks:
class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { print(Zone.current[token]); // OK return MaterialApp( home: Scaffold( body: Center( child: ElevatedButton( onPressed: () { print(Zone.current[token]); // NULL }, child: Text('Click Me'), ), ), ), ); } }
Proposal
It would be great to have a way to initialize the ServicesBinding and SchedulerBinding independently of the remaining bindings in order to support accessing data from the underlying platform before runApp but without initializing the WidgetsBinding.instance within the root zone.
final token = Object(); void main() async { FlutterServicesBinding.ensureInitialized(); final value = await _getPlatformValue(); runZoned( () => runApp(MyApp()), zoneValues: {token: value}, ); }
class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { print(Zone.current[token]); // OK return MaterialApp( home: Scaffold( body: Center( child: ElevatedButton( onPressed: () { print(Zone.current[token]); // OK }, child: Text('Click Me'), ), ), ), ); } }
Where FlutterServicesBinding could look something like:
import 'dart:ui' as ui show SingletonFlutterWindow, PlatformDispatcher, window; import 'package:flutter/foundation.dart'; import 'package:flutter/scheduler.dart'; import 'package:flutter/services.dart'; class FlutterServicesBinding extends _StandaloneBindingBase with SchedulerBinding, ServicesBinding { FlutterServicesBinding._(); static FlutterServicesBinding? _instance; static FlutterServicesBinding ensureInitialized() { return _instance ??= FlutterServicesBinding._(); } } abstract class _StandaloneBindingBase implements BindingBase { _StandaloneBindingBase() { initInstances(); window.onReportTimings = null; } @override ui.SingletonFlutterWindow get window => ui.window; @override ui.PlatformDispatcher get platformDispatcher { return ui.PlatformDispatcher.instance; } @override @protected @mustCallSuper void initInstances() {} @override @protected @mustCallSuper void initServiceExtensions() {} @override @protected bool get locked => false; @override @protected Future<void> lockEvents(Future<void> Function() callback) async {} @override @protected @mustCallSuper void unlocked() {} @override Future<void> reassembleApplication() async {} @override @mustCallSuper @protected Future<void> performReassemble() async {} @override @protected void registerSignalServiceExtension({ required String name, required AsyncCallback callback, }) {} @override @protected void registerBoolServiceExtension({ required String name, required AsyncValueGetter<bool> getter, required AsyncValueSetter<bool> setter, }) {} @override @protected void registerNumericServiceExtension({ required String name, required AsyncValueGetter<double> getter, required AsyncValueSetter<double> setter, }) {} @override @protected void postEvent(String eventKind, Map<String, dynamic> eventData) {} @override @protected void registerStringServiceExtension({ required String name, required AsyncValueGetter<String> getter, required AsyncValueSetter<String> setter, }) {} @override @protected void registerServiceExtension({ required String name, required ServiceExtensionCallback callback, }) {} @override String toString() => '<${objectRuntimeType(this, 'BindingBase')}>'; }
DartPad Demos
-
DartPad illustrating the limitation when using
WidgetsFlutterBinding.ensureInitialized(); -
DartPad illustrating an example of how this can be improved via
FlutterServicesBinding.ensureInitialized();
Additional Info
Published https://pub.dev/packages/flutter_services_binding as a workaround in the meantime.