Clean up the bindings APIs (#86438)

This commit is contained in:
Ian Hickson 2021-07-14 14:41:24 -07:00 committed by GitHub
parent 666185c027
commit d056500bfe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
249 changed files with 1637 additions and 1271 deletions

View File

@ -32,7 +32,7 @@ Future<void> main() async {
child: ComplexLayoutApp(), child: ComplexLayoutApp(),
), ),
)); ));
await SchedulerBinding.instance?.endOfFrame; await SchedulerBinding.instance.endOfFrame;
/// Wait 50ms to allow the raster thread to actually put up the frame. (The /// Wait 50ms to allow the raster thread to actually put up the frame. (The
/// endOfFrame future ends when we send the data to the engine, before /// endOfFrame future ends when we send the data to the engine, before
@ -50,9 +50,9 @@ Future<void> main() async {
child: ComplexLayoutApp(), child: ComplexLayoutApp(),
), ),
)); ));
await SchedulerBinding.instance?.endOfFrame; await SchedulerBinding.instance.endOfFrame;
final WidgetController controller = LiveWidgetController(WidgetsBinding.instance!); final WidgetController controller = LiveWidgetController(WidgetsBinding.instance);
// Scroll down // Scroll down
for (int iteration = 0; iteration < maxIterations; iteration += 1) { for (int iteration = 0; iteration < maxIterations; iteration += 1) {

View File

@ -41,7 +41,7 @@ class _FilteredChildAnimationPageState extends State<FilteredChildAnimationPage>
_filterType = widget.initialFilterType; _filterType = widget.initialFilterType;
_complexChild = widget.initialComplexChild; _complexChild = widget.initialComplexChild;
_useRepaintBoundary = widget.initialUseRepaintBoundary; _useRepaintBoundary = widget.initialUseRepaintBoundary;
WidgetsBinding.instance!.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
final RenderBox childBox = _childKey.currentContext!.findRenderObject()! as RenderBox; final RenderBox childBox = _childKey.currentContext!.findRenderObject()! as RenderBox;
_childCenter = childBox.paintBounds.center; _childCenter = childBox.paintBounds.center;
}); });

View File

@ -11,7 +11,7 @@ class LargeImagesPage extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final ImageCache imageCache = PaintingBinding.instance!.imageCache!; final ImageCache imageCache = PaintingBinding.instance.imageCache;
imageCache.maximumSize = 30; imageCache.maximumSize = 30;
imageCache.maximumSizeBytes = 50 << 20; imageCache.maximumSizeBytes = 50 << 20;
return GridView.builder( return GridView.builder(

View File

@ -66,7 +66,7 @@ class BenchMouseRegionGridHover extends WidgetRecorder {
void frameDidDraw() { void frameDidDraw() {
if (!started) { if (!started) {
started = true; started = true;
SchedulerBinding.instance!.addPostFrameCallback((Duration timeStamp) async { SchedulerBinding.instance.addPostFrameCallback((Duration timeStamp) async {
_tester.start(); _tester.start();
registerDidStop(_tester.stop); registerDidStop(_tester.stop);
}); });
@ -124,7 +124,7 @@ class _UntilNextFrame {
static Future<void> wait() { static Future<void> wait() {
if (_UntilNextFrame._completer == null) { if (_UntilNextFrame._completer == null) {
_UntilNextFrame._completer = Completer<void>(); _UntilNextFrame._completer = Completer<void>();
SchedulerBinding.instance!.addPostFrameCallback((_) { SchedulerBinding.instance.addPostFrameCallback((_) {
_UntilNextFrame._completer!.complete(null); _UntilNextFrame._completer!.complete(null);
_UntilNextFrame._completer = null; _UntilNextFrame._completer = null;
}); });
@ -145,7 +145,7 @@ class _Tester {
TestGesture get gesture { TestGesture get gesture {
return _gesture ??= TestGesture( return _gesture ??= TestGesture(
dispatcher: (PointerEvent event) async { dispatcher: (PointerEvent event) async {
RendererBinding.instance!.handlePointerEvent(event); RendererBinding.instance.handlePointerEvent(event);
}, },
kind: PointerDeviceKind.mouse, kind: PointerDeviceKind.mouse,
); );

View File

@ -41,7 +41,7 @@ class BenchMouseRegionGridScroll extends WidgetRecorder {
void frameDidDraw() { void frameDidDraw() {
if (!started) { if (!started) {
started = true; started = true;
SchedulerBinding.instance!.addPostFrameCallback((Duration timeStamp) async { SchedulerBinding.instance.addPostFrameCallback((Duration timeStamp) async {
_tester.start(); _tester.start();
registerDidStop(_tester.stop); registerDidStop(_tester.stop);
}); });
@ -96,7 +96,7 @@ class _UntilNextFrame {
static Future<void> wait() { static Future<void> wait() {
if (_UntilNextFrame._completer == null) { if (_UntilNextFrame._completer == null) {
_UntilNextFrame._completer = Completer<void>(); _UntilNextFrame._completer = Completer<void>();
SchedulerBinding.instance!.addPostFrameCallback((_) { SchedulerBinding.instance.addPostFrameCallback((_) {
_UntilNextFrame._completer!.complete(null); _UntilNextFrame._completer!.complete(null);
_UntilNextFrame._completer = null; _UntilNextFrame._completer = null;
}); });
@ -117,7 +117,7 @@ class _Tester {
TestGesture get gesture { TestGesture get gesture {
return _gesture ??= TestGesture( return _gesture ??= TestGesture(
dispatcher: (PointerEvent event) async { dispatcher: (PointerEvent event) async {
RendererBinding.instance!.handlePointerEvent(event); RendererBinding.instance.handlePointerEvent(event);
}, },
kind: PointerDeviceKind.mouse, kind: PointerDeviceKind.mouse,
); );

View File

@ -85,7 +85,7 @@ class BenchMouseRegionMixedGridHover extends WidgetRecorder {
void frameDidDraw() { void frameDidDraw() {
if (!started) { if (!started) {
started = true; started = true;
SchedulerBinding.instance!.addPostFrameCallback((Duration timeStamp) async { SchedulerBinding.instance.addPostFrameCallback((Duration timeStamp) async {
_tester.start(); _tester.start();
registerDidStop(_tester.stop); registerDidStop(_tester.stop);
}); });
@ -146,7 +146,7 @@ class _UntilNextFrame {
static Future<void> wait() { static Future<void> wait() {
if (_UntilNextFrame._completer == null) { if (_UntilNextFrame._completer == null) {
_UntilNextFrame._completer = Completer<void>(); _UntilNextFrame._completer = Completer<void>();
SchedulerBinding.instance!.addPostFrameCallback((_) { SchedulerBinding.instance.addPostFrameCallback((_) {
_UntilNextFrame._completer!.complete(null); _UntilNextFrame._completer!.complete(null);
_UntilNextFrame._completer = null; _UntilNextFrame._completer = null;
}); });
@ -167,7 +167,7 @@ class _Tester {
TestGesture get gesture { TestGesture get gesture {
return _gesture ??= TestGesture( return _gesture ??= TestGesture(
dispatcher: (PointerEvent event) async { dispatcher: (PointerEvent event) async {
RendererBinding.instance!.handlePointerEvent(event); RendererBinding.instance.handlePointerEvent(event);
}, },
kind: PointerDeviceKind.mouse, kind: PointerDeviceKind.mouse,
); );

View File

@ -935,7 +935,7 @@ double _computeAverage(String label, Iterable<double> values) {
/// ///
/// See also: /// See also:
/// ///
/// * https://en.wikipedia.org/wiki/Standard_deviation /// * <https://en.wikipedia.org/wiki/Standard_deviation>
double _computeStandardDeviationForPopulation(String label, Iterable<double> population) { double _computeStandardDeviationForPopulation(String label, Iterable<double> population) {
if (population.isEmpty) { if (population.isEmpty) {
throw StateError('$label: attempted to compute the standard deviation of empty population.'); throw StateError('$label: attempted to compute the standard deviation of empty population.');
@ -987,12 +987,32 @@ class _RecordingWidgetsBinding extends BindingBase
SemanticsBinding, SemanticsBinding,
RendererBinding, RendererBinding,
WidgetsBinding { WidgetsBinding {
/// Makes an instance of [_RecordingWidgetsBinding] the current binding.
@override
void initInstances() {
super.initInstances();
_instance = this;
}
/// The singleton instance of this object.
///
/// Provides access to the features exposed by this class. The binding must
/// be initialized before using this getter; this is typically done by calling
/// [_RecordingWidgetsBinding.ensureInitialized].
static _RecordingWidgetsBinding get instance => BindingBase.checkInstance(_instance);
static _RecordingWidgetsBinding? _instance;
/// Returns an instance of the [_RecordingWidgetsBinding], creating and
/// initializing it if necessary.
///
/// See also:
///
/// * [WidgetsFlutterBinding.ensureInitialized], the equivalent in the widgets framework.
static _RecordingWidgetsBinding ensureInitialized() { static _RecordingWidgetsBinding ensureInitialized() {
if (WidgetsBinding.instance == null) { if (_instance == null) {
_RecordingWidgetsBinding(); _RecordingWidgetsBinding();
} }
return WidgetsBinding.instance! as _RecordingWidgetsBinding; return instance;
} }
FrameRecorder? _recorder; FrameRecorder? _recorder;

View File

@ -10,8 +10,7 @@ import 'package:integration_test/integration_test.dart';
import 'package:macrobenchmarks/src/simple_scroll.dart'; import 'package:macrobenchmarks/src/simple_scroll.dart';
void main() { void main() {
final IntegrationTestWidgetsFlutterBinding binding = final IntegrationTestWidgetsFlutterBinding binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized();
IntegrationTestWidgetsFlutterBinding.ensureInitialized() as IntegrationTestWidgetsFlutterBinding;
testWidgets( testWidgets(
'Frame Counter and Input Delay for benchmarkLive', 'Frame Counter and Input Delay for benchmarkLive',
(WidgetTester tester) async { (WidgetTester tester) async {

View File

@ -10,8 +10,8 @@ import 'package:macrobenchmarks/main.dart';
Future<void> endOfAnimation() async { Future<void> endOfAnimation() async {
do { do {
await SchedulerBinding.instance!.endOfFrame; await SchedulerBinding.instance.endOfFrame;
} while (SchedulerBinding.instance!.hasScheduledFrame); } while (SchedulerBinding.instance.hasScheduledFrame);
} }
Future<void> main() async { Future<void> main() async {

View File

@ -10,8 +10,8 @@ import 'package:macrobenchmarks/main.dart';
Future<void> endOfAnimation() async { Future<void> endOfAnimation() async {
do { do {
await SchedulerBinding.instance!.endOfFrame; await SchedulerBinding.instance.endOfFrame;
} while (SchedulerBinding.instance!.hasScheduledFrame); } while (SchedulerBinding.instance.hasScheduledFrame);
} }
Future<void> main() async { Future<void> main() async {

View File

@ -44,24 +44,24 @@ Future<void> main() async {
// Wait for frame rendering to stabilize. // Wait for frame rendering to stabilize.
for (int i = 0; i < 5; i++) { for (int i = 0; i < 5; i++) {
await SchedulerBinding.instance?.endOfFrame; await SchedulerBinding.instance.endOfFrame;
} }
final Stopwatch watch = Stopwatch(); final Stopwatch watch = Stopwatch();
print('flutter_test allElements benchmark... (${WidgetsBinding.instance?.renderViewElement})'); print('flutter_test allElements benchmark... (${WidgetsBinding.instance.renderViewElement})');
// Make sure we get enough elements to process for consistent benchmark runs // Make sure we get enough elements to process for consistent benchmark runs
int elementCount = collectAllElementsFrom(WidgetsBinding.instance!.renderViewElement!, skipOffstage: false).length; int elementCount = collectAllElementsFrom(WidgetsBinding.instance.renderViewElement!, skipOffstage: false).length;
while (elementCount < 2458) { while (elementCount < 2458) {
await Future<void>.delayed(Duration.zero); await Future<void>.delayed(Duration.zero);
elementCount = collectAllElementsFrom(WidgetsBinding.instance!.renderViewElement!, skipOffstage: false).length; elementCount = collectAllElementsFrom(WidgetsBinding.instance.renderViewElement!, skipOffstage: false).length;
} }
print('element count: $elementCount'); print('element count: $elementCount');
watch.start(); watch.start();
for (int i = 0; i < _kNumIters; i += 1) { for (int i = 0; i < _kNumIters; i += 1) {
final List<Element> allElements = collectAllElementsFrom( final List<Element> allElements = collectAllElementsFrom(
WidgetsBinding.instance!.renderViewElement!, WidgetsBinding.instance.renderViewElement!,
skipOffstage: false, skipOffstage: false,
).toList(); ).toList();
allElements.clear(); allElements.clear();

View File

@ -68,7 +68,7 @@ Future<void> main() async {
// Time how long each frame takes // Time how long each frame takes
cpuWatch.reset(); cpuWatch.reset();
while (SchedulerBinding.instance!.hasScheduledFrame) { while (SchedulerBinding.instance.hasScheduledFrame) {
await tester.pump(); await tester.pump();
totalSubsequentFramesIterationCount += 1; totalSubsequentFramesIterationCount += 1;
} }

View File

@ -33,18 +33,18 @@ Future<void> main() async {
final TestViewConfiguration big = TestViewConfiguration( final TestViewConfiguration big = TestViewConfiguration(
size: const Size(360.0, 640.0), size: const Size(360.0, 640.0),
window: RendererBinding.instance?.window, window: RendererBinding.instance.window,
); );
final TestViewConfiguration small = TestViewConfiguration( final TestViewConfiguration small = TestViewConfiguration(
size: const Size(355.0, 635.0), size: const Size(355.0, 635.0),
window: RendererBinding.instance?.window, window: RendererBinding.instance.window,
); );
final RenderView? renderView = WidgetsBinding.instance?.renderView; final RenderView renderView = WidgetsBinding.instance.renderView;
binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.benchmark; binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.benchmark;
watch.start(); watch.start();
while (watch.elapsed < kBenchmarkTime) { while (watch.elapsed < kBenchmarkTime) {
renderView?.configuration = iterations.isEven ? big : small; renderView.configuration = iterations.isEven ? big : small;
await tester.pumpBenchmark(Duration(milliseconds: iterations * 16)); await tester.pumpBenchmark(Duration(milliseconds: iterations * 16));
iterations += 1; iterations += 1;
} }

View File

@ -42,7 +42,7 @@ class _HomePage extends State<HomePage> {
// Trigger the second route. // Trigger the second route.
// https://github.com/flutter/flutter/issues/40126 // https://github.com/flutter/flutter/issues/40126
WidgetsBinding.instance?.addPostFrameCallback((_) async { WidgetsBinding.instance.addPostFrameCallback((_) async {
Navigator.of(context).push( Navigator.of(context).push(
MaterialPageRoute<void>(builder: (_) => const SecondPage())); MaterialPageRoute<void>(builder: (_) => const SecondPage()));
}); });

View File

@ -7,7 +7,7 @@ import 'package:flutter_gallery/demo/calculator_demo.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
void main() { void main() {
final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized() as TestWidgetsFlutterBinding; final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized();
if (binding is LiveTestWidgetsFlutterBinding) if (binding is LiveTestWidgetsFlutterBinding)
binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive; binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive;

View File

@ -8,7 +8,7 @@ import 'package:flutter_gallery/gallery/app.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
void main() { void main() {
final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized() as TestWidgetsFlutterBinding; final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized();
if (binding is LiveTestWidgetsFlutterBinding) if (binding is LiveTestWidgetsFlutterBinding)
binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive; binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive;

View File

@ -7,7 +7,7 @@ import 'package:flutter_gallery/gallery/app.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
void main() { void main() {
final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized() as TestWidgetsFlutterBinding; final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized();
if (binding is LiveTestWidgetsFlutterBinding) if (binding is LiveTestWidgetsFlutterBinding)
binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive; binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive;

View File

@ -58,7 +58,7 @@ Future<void> main() async {
print('Starting app...'); print('Starting app...');
runApp(const GalleryApp(testMode: true)); runApp(const GalleryApp(testMode: true));
final _LiveWidgetController controller = _LiveWidgetController(WidgetsBinding.instance!); final _LiveWidgetController controller = _LiveWidgetController(WidgetsBinding.instance);
for (final GalleryDemoCategory category in kAllGalleryDemoCategories) { for (final GalleryDemoCategory category in kAllGalleryDemoCategories) {
print('Tapping "${category.name}" section...'); print('Tapping "${category.name}" section...');
await controller.tap(find.text(category.name)); await controller.tap(find.text(category.name));
@ -111,7 +111,7 @@ class _LiveWidgetController extends LiveWidgetController {
Future<void> _waitUntilFrame(bool Function() condition, [Completer<void>? completer]) { Future<void> _waitUntilFrame(bool Function() condition, [Completer<void>? completer]) {
completer ??= Completer<void>(); completer ??= Completer<void>();
if (!condition()) { if (!condition()) {
SchedulerBinding.instance!.addPostFrameCallback((Duration timestamp) { SchedulerBinding.instance.addPostFrameCallback((Duration timestamp) {
_waitUntilFrame(condition, completer); _waitUntilFrame(condition, completer);
}); });
} else { } else {

View File

@ -7,7 +7,7 @@ import 'package:flutter_gallery/gallery/app.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
void main() { void main() {
final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized() as TestWidgetsFlutterBinding; final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized();
if (binding is LiveTestWidgetsFlutterBinding) if (binding is LiveTestWidgetsFlutterBinding)
binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive; binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive;

View File

@ -7,7 +7,7 @@ import 'package:flutter_gallery/gallery/app.dart' show GalleryApp;
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
void main() { void main() {
final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized() as TestWidgetsFlutterBinding; final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized();
if (binding is LiveTestWidgetsFlutterBinding) if (binding is LiveTestWidgetsFlutterBinding)
binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive; binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive;

View File

@ -77,9 +77,9 @@ Future<void> smokeDemo(WidgetTester tester, GalleryDemo demo) async {
// Verify that the dumps are pretty. // Verify that the dumps are pretty.
final String routeName = demo.routeName; final String routeName = demo.routeName;
verifyToStringOutput('debugDumpApp', routeName, WidgetsBinding.instance!.renderViewElement!.toStringDeep()); verifyToStringOutput('debugDumpApp', routeName, WidgetsBinding.instance.renderViewElement!.toStringDeep());
verifyToStringOutput('debugDumpRenderTree', routeName, RendererBinding.instance?.renderView.toStringDeep() ?? ''); verifyToStringOutput('debugDumpRenderTree', routeName, RendererBinding.instance.renderView.toStringDeep());
verifyToStringOutput('debugDumpLayerTree', routeName, RendererBinding.instance?.renderView.debugLayer?.toStringDeep() ?? ''); verifyToStringOutput('debugDumpLayerTree', routeName, RendererBinding.instance.renderView.debugLayer?.toStringDeep() ?? '');
// Scroll the demo around a bit more. // Scroll the demo around a bit more.
await tester.flingFrom(const Offset(400.0, 300.0), const Offset(0.0, 400.0), 1000.0); await tester.flingFrom(const Offset(400.0, 300.0), const Offset(0.0, 400.0), 1000.0);
@ -179,8 +179,8 @@ void main() {
testWidgets('Flutter Gallery app smoke test', smokeGallery); testWidgets('Flutter Gallery app smoke test', smokeGallery);
testWidgets('Flutter Gallery app smoke test with semantics', (WidgetTester tester) async { testWidgets('Flutter Gallery app smoke test with semantics', (WidgetTester tester) async {
RendererBinding.instance!.setSemanticsEnabled(true); RendererBinding.instance.setSemanticsEnabled(true);
await smokeGallery(tester); await smokeGallery(tester);
RendererBinding.instance!.setSemanticsEnabled(false); RendererBinding.instance.setSemanticsEnabled(false);
}); });
} }

View File

@ -11,7 +11,7 @@ Future<String> mockUpdateUrlFetcher() {
} }
void main() { void main() {
final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized() as TestWidgetsFlutterBinding; final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized();
if (binding is LiveTestWidgetsFlutterBinding) if (binding is LiveTestWidgetsFlutterBinding)
binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive; binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive;

View File

@ -30,12 +30,12 @@ class _MessageHandler {
case 'demoNames': case 'demoNames':
return const JsonEncoder.withIndent(' ').convert(_allDemos); return const JsonEncoder.withIndent(' ').convert(_allDemos);
case 'profileDemos': case 'profileDemos':
controller ??= LiveWidgetController(WidgetsBinding.instance!); controller ??= LiveWidgetController(WidgetsBinding.instance);
await runDemos(kProfiledDemos, controller!); await runDemos(kProfiledDemos, controller!);
_unTestedDemos.removeAll(kProfiledDemos); _unTestedDemos.removeAll(kProfiledDemos);
return const JsonEncoder.withIndent(' ').convert(kProfiledDemos); return const JsonEncoder.withIndent(' ').convert(kProfiledDemos);
case 'restDemos': case 'restDemos':
controller ??= LiveWidgetController(WidgetsBinding.instance!); controller ??= LiveWidgetController(WidgetsBinding.instance);
final List<String> restDemos = _unTestedDemos.toList(); final List<String> restDemos = _unTestedDemos.toList();
await runDemos(restDemos, controller!); await runDemos(restDemos, controller!);
return const JsonEncoder.withIndent(' ').convert(restDemos); return const JsonEncoder.withIndent(' ').convert(restDemos);

View File

@ -22,8 +22,7 @@ List<String> _allDemos = kAllGalleryDemos.map(
void main([List<String> args = const <String>[]]) { void main([List<String> args = const <String>[]]) {
final bool withSemantics = args.contains('--with_semantics'); final bool withSemantics = args.contains('--with_semantics');
final IntegrationTestWidgetsFlutterBinding binding = final IntegrationTestWidgetsFlutterBinding binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized();
IntegrationTestWidgetsFlutterBinding.ensureInitialized() as IntegrationTestWidgetsFlutterBinding;
binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive; binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive;
group('flutter gallery transitions on e2e', () { group('flutter gallery transitions on e2e', () {
testWidgets('find.bySemanticsLabel', (WidgetTester tester) async { testWidgets('find.bySemanticsLabel', (WidgetTester tester) async {

View File

@ -11,8 +11,8 @@ import 'package:flutter_test/flutter_test.dart';
Future<void> endOfAnimation() async { Future<void> endOfAnimation() async {
do { do {
await SchedulerBinding.instance!.endOfFrame; await SchedulerBinding.instance.endOfFrame;
} while (SchedulerBinding.instance!.hasScheduledFrame); } while (SchedulerBinding.instance.hasScheduledFrame);
} }
int iteration = 0; int iteration = 0;
@ -30,5 +30,5 @@ Future<void> main() async {
await endOfAnimation(); await endOfAnimation();
await Future<void>.delayed(const Duration(milliseconds: 50)); await Future<void>.delayed(const Duration(milliseconds: 50));
debugPrint('==== MEMORY BENCHMARK ==== READY ===='); debugPrint('==== MEMORY BENCHMARK ==== READY ====');
WidgetsBinding.instance!.addObserver(LifecycleObserver()); WidgetsBinding.instance.addObserver(LifecycleObserver());
} }

View File

@ -38,7 +38,7 @@ Future<void> main() async {
), ),
)); ));
await SchedulerBinding.instance!.endOfFrame; await SchedulerBinding.instance.endOfFrame;
// We are waiting for the GPU to rasterize a frame here. This makes this // We are waiting for the GPU to rasterize a frame here. This makes this
// flaky, we can rely on a more deterministic source such as // flaky, we can rely on a more deterministic source such as
@ -48,7 +48,7 @@ Future<void> main() async {
debugPrint('==== MEMORY BENCHMARK ==== READY ===='); debugPrint('==== MEMORY BENCHMARK ==== READY ====');
final WidgetController controller = final WidgetController controller =
LiveWidgetController(WidgetsBinding.instance!); LiveWidgetController(WidgetsBinding.instance);
debugPrint('Scrolling...'); debugPrint('Scrolling...');
final Finder list = find.byKey(const Key('ImageList')); final Finder list = find.byKey(const Key('ImageList'));

View File

@ -13,8 +13,8 @@ import 'package:flutter_test/flutter_test.dart';
Future<void> endOfAnimation() async { Future<void> endOfAnimation() async {
do { do {
await SchedulerBinding.instance!.endOfFrame; await SchedulerBinding.instance.endOfFrame;
} while (SchedulerBinding.instance!.hasScheduledFrame); } while (SchedulerBinding.instance.hasScheduledFrame);
} }
Rect boundsFor(WidgetController controller, Finder item) { Rect boundsFor(WidgetController controller, Finder item) {
@ -35,7 +35,7 @@ Future<void> main() async {
child: GalleryApp(testMode: true), child: GalleryApp(testMode: true),
), ),
)); ));
await SchedulerBinding.instance!.endOfFrame; await SchedulerBinding.instance.endOfFrame;
await Future<void>.delayed(const Duration(milliseconds: 50)); await Future<void>.delayed(const Duration(milliseconds: 50));
debugPrint('==== MEMORY BENCHMARK ==== READY ===='); debugPrint('==== MEMORY BENCHMARK ==== READY ====');
@ -49,9 +49,9 @@ Future<void> main() async {
child: GalleryApp(testMode: true), child: GalleryApp(testMode: true),
), ),
)); ));
await SchedulerBinding.instance!.endOfFrame; await SchedulerBinding.instance.endOfFrame;
final WidgetController controller = LiveWidgetController(WidgetsBinding.instance!); final WidgetController controller = LiveWidgetController(WidgetsBinding.instance);
debugPrint('Navigating...'); debugPrint('Navigating...');
await controller.tap(find.text('Material')); await controller.tap(find.text('Material'));

View File

@ -16,7 +16,7 @@ void main() {
// Disconnects semantics listener for testing purposes. // Disconnects semantics listener for testing purposes.
originalSemanticsListener = ui.window.onSemanticsEnabledChanged; originalSemanticsListener = ui.window.onSemanticsEnabledChanged;
ui.window.onSemanticsEnabledChanged = null; ui.window.onSemanticsEnabledChanged = null;
RendererBinding.instance?.setSemanticsEnabled(false); RendererBinding.instance.setSemanticsEnabled(false);
// If the test passes, LifeCycleSpy will rewire the semantics listener back. // If the test passes, LifeCycleSpy will rewire the semantics listener back.
runApp(const LifeCycleSpy()); runApp(const LifeCycleSpy());
} }
@ -46,15 +46,15 @@ class _LifeCycleSpyState extends State<LifeCycleSpy> with WidgetsBindingObserver
@override @override
void initState() { void initState() {
super.initState(); super.initState();
WidgetsBinding.instance?.addObserver(this); WidgetsBinding.instance.addObserver(this);
_actualLifeCycleSequence = <AppLifecycleState?>[ _actualLifeCycleSequence = <AppLifecycleState?>[
ServicesBinding.instance?.lifecycleState ServicesBinding.instance.lifecycleState
]; ];
} }
@override @override
void dispose() { void dispose() {
WidgetsBinding.instance?.removeObserver(this); WidgetsBinding.instance.removeObserver(this);
super.dispose(); super.dispose();
} }
@ -70,7 +70,7 @@ class _LifeCycleSpyState extends State<LifeCycleSpy> with WidgetsBindingObserver
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (const ListEquality<AppLifecycleState?>().equals(_actualLifeCycleSequence, _expectedLifeCycleSequence)) { if (const ListEquality<AppLifecycleState?>().equals(_actualLifeCycleSequence, _expectedLifeCycleSequence)) {
// Rewires the semantics harness if test passes. // Rewires the semantics harness if test passes.
RendererBinding.instance?.setSemanticsEnabled(true); RendererBinding.instance.setSemanticsEnabled(true);
ui.window.onSemanticsEnabledChanged = originalSemanticsListener; ui.window.onSemanticsEnabledChanged = originalSemanticsListener;
} }
return const MaterialApp( return const MaterialApp(

View File

@ -960,7 +960,7 @@ class _PaintingState extends State<Painting> with SingleTickerProviderStateMixin
} }
_text = buffer.toString(); _text = buffer.toString();
}); });
SchedulerBinding.instance?.addPostFrameCallback((Duration duration) { SchedulerBinding.instance.addPostFrameCallback((Duration duration) {
if (mounted && intrinsicKey.currentContext?.size?.height != controlKey.currentContext?.size?.height) { if (mounted && intrinsicKey.currentContext?.size?.height != controlKey.currentContext?.size?.height) {
debugPrint('Found some text that unexpectedly renders at different heights.'); debugPrint('Found some text that unexpectedly renders at different heights.');
debugPrint('Text: $_text'); debugPrint('Text: $_text');

View File

@ -32,18 +32,18 @@ void main() {
test('Image cache tracing', () async { test('Image cache tracing', () async {
final TestImageStreamCompleter completer1 = TestImageStreamCompleter(); final TestImageStreamCompleter completer1 = TestImageStreamCompleter();
final TestImageStreamCompleter completer2 = TestImageStreamCompleter(); final TestImageStreamCompleter completer2 = TestImageStreamCompleter();
PaintingBinding.instance!.imageCache!.putIfAbsent( PaintingBinding.instance.imageCache.putIfAbsent(
'Test', 'Test',
() => completer1, () => completer1,
); );
PaintingBinding.instance!.imageCache!.clear(); PaintingBinding.instance.imageCache.clear();
completer2.testSetImage(ImageInfo(image: await createTestImage())); completer2.testSetImage(ImageInfo(image: await createTestImage()));
PaintingBinding.instance!.imageCache!.putIfAbsent( PaintingBinding.instance.imageCache.putIfAbsent(
'Test2', 'Test2',
() => completer2, () => completer2,
); );
PaintingBinding.instance!.imageCache!.evict('Test2'); PaintingBinding.instance.imageCache.evict('Test2');
final Timeline timeline = await vmService.getVMTimeline(); final Timeline timeline = await vmService.getVMTimeline();
_expectTimelineEvents( _expectTimelineEvents(

View File

@ -18,12 +18,12 @@ class _LifecycleWatcherState extends State<LifecycleWatcher>
@override @override
void initState() { void initState() {
super.initState(); super.initState();
WidgetsBinding.instance!.addObserver(this); WidgetsBinding.instance.addObserver(this);
} }
@override @override
void dispose() { void dispose() {
WidgetsBinding.instance!.removeObserver(this); WidgetsBinding.instance.removeObserver(this);
super.dispose(); super.dispose();
} }

View File

@ -13,6 +13,6 @@ void main() {
test('layers smoketest for rendering/custom_coordinate_systems.dart', () { test('layers smoketest for rendering/custom_coordinate_systems.dart', () {
FlutterError.onError = (FlutterErrorDetails details) { throw details.exception; }; FlutterError.onError = (FlutterErrorDetails details) { throw details.exception; };
demo.main(); demo.main();
expect(SchedulerBinding.instance!.hasScheduledFrame, true); expect(SchedulerBinding.instance.hasScheduledFrame, true);
}); });
} }

View File

@ -13,6 +13,6 @@ void main() {
test('layers smoketest for rendering/flex_layout.dart', () { test('layers smoketest for rendering/flex_layout.dart', () {
FlutterError.onError = (FlutterErrorDetails details) { throw details.exception; }; FlutterError.onError = (FlutterErrorDetails details) { throw details.exception; };
demo.main(); demo.main();
expect(SchedulerBinding.instance!.hasScheduledFrame, true); expect(SchedulerBinding.instance.hasScheduledFrame, true);
}); });
} }

View File

@ -13,6 +13,6 @@ void main() {
test('layers smoketest for rendering/hello_world.dart', () { test('layers smoketest for rendering/hello_world.dart', () {
FlutterError.onError = (FlutterErrorDetails details) { throw details.exception; }; FlutterError.onError = (FlutterErrorDetails details) { throw details.exception; };
demo.main(); demo.main();
expect(SchedulerBinding.instance!.hasScheduledFrame, true); expect(SchedulerBinding.instance.hasScheduledFrame, true);
}); });
} }

View File

@ -13,6 +13,6 @@ void main() {
test('layers smoketest for rendering/spinning_square.dart', () { test('layers smoketest for rendering/spinning_square.dart', () {
FlutterError.onError = (FlutterErrorDetails details) { throw details.exception; }; FlutterError.onError = (FlutterErrorDetails details) { throw details.exception; };
demo.main(); demo.main();
expect(SchedulerBinding.instance!.hasScheduledFrame, true); expect(SchedulerBinding.instance.hasScheduledFrame, true);
}); });
} }

View File

@ -13,6 +13,6 @@ void main() {
test('layers smoketest for rendering/touch_input.dart', () { test('layers smoketest for rendering/touch_input.dart', () {
FlutterError.onError = (FlutterErrorDetails details) { throw details.exception; }; FlutterError.onError = (FlutterErrorDetails details) { throw details.exception; };
demo.main(); demo.main();
expect(SchedulerBinding.instance!.hasScheduledFrame, true); expect(SchedulerBinding.instance.hasScheduledFrame, true);
}); });
} }

View File

@ -73,7 +73,7 @@ void attachWidgetTreeToRenderTree(RenderProxyBox container) {
), ),
), ),
), ),
).attachToRenderTree(WidgetsBinding.instance!.buildOwner!, element); ).attachToRenderTree(WidgetsBinding.instance.buildOwner!, element);
} }
Duration? timeBase; Duration? timeBase;
@ -86,7 +86,7 @@ void rotate(Duration timeStamp) {
transformBox.setIdentity(); transformBox.setIdentity();
transformBox.rotateZ(delta); transformBox.rotateZ(delta);
WidgetsBinding.instance!.buildOwner!.buildScope(element!); WidgetsBinding.instance.buildOwner!.buildScope(element!);
} }
void main() { void main() {

View File

@ -567,7 +567,7 @@ class AnimationController extends Animation<double>
TickerFuture _animateToInternal(double target, { Duration? duration, Curve curve = Curves.linear }) { TickerFuture _animateToInternal(double target, { Duration? duration, Curve curve = Curves.linear }) {
double scale = 1.0; double scale = 1.0;
if (SemanticsBinding.instance!.disableAnimations) { if (SemanticsBinding.instance.disableAnimations) {
switch (animationBehavior) { switch (animationBehavior) {
case AnimationBehavior.normal: case AnimationBehavior.normal:
// Since the framework cannot handle zero duration animations, we run it at 5% of the normal // Since the framework cannot handle zero duration animations, we run it at 5% of the normal
@ -688,7 +688,7 @@ class AnimationController extends Animation<double>
: upperBound + _kFlingTolerance.distance; : upperBound + _kFlingTolerance.distance;
double scale = 1.0; double scale = 1.0;
final AnimationBehavior behavior = animationBehavior ?? this.animationBehavior; final AnimationBehavior behavior = animationBehavior ?? this.animationBehavior;
if (SemanticsBinding.instance!.disableAnimations) { if (SemanticsBinding.instance.disableAnimations) {
switch (behavior) { switch (behavior) {
case AnimationBehavior.normal: case AnimationBehavior.normal:
// TODO(jonahwilliams): determine a better process for setting velocity. // TODO(jonahwilliams): determine a better process for setting velocity.

View File

@ -315,7 +315,7 @@ class _CupertinoContextMenuState extends State<CupertinoContextMenu> with Ticker
// because _ContextMenuRoute renders its first frame offscreen. // because _ContextMenuRoute renders its first frame offscreen.
// Otherwise there would be a visible flash when nothing is rendered for // Otherwise there would be a visible flash when nothing is rendered for
// one frame. // one frame.
SchedulerBinding.instance!.addPostFrameCallback((Duration _) { SchedulerBinding.instance.addPostFrameCallback((Duration _) {
_lastOverlayEntry?.remove(); _lastOverlayEntry?.remove();
_lastOverlayEntry = null; _lastOverlayEntry = null;
_openController.reset(); _openController.reset();
@ -742,7 +742,7 @@ class _ContextMenuRoute<T> extends PopupRoute<T> {
// Render one frame offstage in the final position so that we can take // Render one frame offstage in the final position so that we can take
// measurements of its layout and then animate to them. // measurements of its layout and then animate to them.
SchedulerBinding.instance!.addPostFrameCallback((Duration _) { SchedulerBinding.instance.addPostFrameCallback((Duration _) {
_updateTweenRects(); _updateTweenRects();
_internalOffstage = false; _internalOffstage = false;
_setOffstageInternally(); _setOffstageInternally();

View File

@ -585,7 +585,7 @@ class _CupertinoDatePickerDateTimeState extends State<CupertinoDatePicker> {
minuteController = FixedExtentScrollController(initialItem: initialDateTime.minute ~/ widget.minuteInterval); minuteController = FixedExtentScrollController(initialItem: initialDateTime.minute ~/ widget.minuteInterval);
dateController = FixedExtentScrollController(initialItem: 0); dateController = FixedExtentScrollController(initialItem: 0);
PaintingBinding.instance!.systemFonts.addListener(_handleSystemFontsChange); PaintingBinding.instance.systemFonts.addListener(_handleSystemFontsChange);
} }
void _handleSystemFontsChange () { void _handleSystemFontsChange () {
@ -604,7 +604,7 @@ class _CupertinoDatePickerDateTimeState extends State<CupertinoDatePicker> {
minuteController.dispose(); minuteController.dispose();
meridiemController.dispose(); meridiemController.dispose();
PaintingBinding.instance!.systemFonts.removeListener(_handleSystemFontsChange); PaintingBinding.instance.systemFonts.removeListener(_handleSystemFontsChange);
super.dispose(); super.dispose();
} }
@ -930,7 +930,7 @@ class _CupertinoDatePickerDateTimeState extends State<CupertinoDatePicker> {
void _scrollToDate(DateTime newDate, DateTime fromDate) { void _scrollToDate(DateTime newDate, DateTime fromDate) {
assert(newDate != null); assert(newDate != null);
SchedulerBinding.instance!.addPostFrameCallback((Duration timestamp) { SchedulerBinding.instance.addPostFrameCallback((Duration timestamp) {
if (fromDate.year != newDate.year || fromDate.month != newDate.month || fromDate.day != newDate.day) { if (fromDate.year != newDate.year || fromDate.month != newDate.month || fromDate.day != newDate.day) {
_animateColumnControllerToItem(dateController, selectedDayFromInitial); _animateColumnControllerToItem(dateController, selectedDayFromInitial);
} }
@ -1104,7 +1104,7 @@ class _CupertinoDatePickerDateState extends State<CupertinoDatePicker> {
monthController = FixedExtentScrollController(initialItem: selectedMonth - 1); monthController = FixedExtentScrollController(initialItem: selectedMonth - 1);
yearController = FixedExtentScrollController(initialItem: selectedYear); yearController = FixedExtentScrollController(initialItem: selectedYear);
PaintingBinding.instance!.systemFonts.addListener(_handleSystemFontsChange); PaintingBinding.instance.systemFonts.addListener(_handleSystemFontsChange);
} }
void _handleSystemFontsChange() { void _handleSystemFontsChange() {
@ -1120,7 +1120,7 @@ class _CupertinoDatePickerDateState extends State<CupertinoDatePicker> {
monthController.dispose(); monthController.dispose();
yearController.dispose(); yearController.dispose();
PaintingBinding.instance!.systemFonts.removeListener(_handleSystemFontsChange); PaintingBinding.instance.systemFonts.removeListener(_handleSystemFontsChange);
super.dispose(); super.dispose();
} }
@ -1326,7 +1326,7 @@ class _CupertinoDatePickerDateState extends State<CupertinoDatePicker> {
void _scrollToDate(DateTime newDate) { void _scrollToDate(DateTime newDate) {
assert(newDate != null); assert(newDate != null);
SchedulerBinding.instance!.addPostFrameCallback((Duration timestamp) { SchedulerBinding.instance.addPostFrameCallback((Duration timestamp) {
if (selectedYear != newDate.year) { if (selectedYear != newDate.year) {
_animateColumnControllerToItem(yearController, newDate.year); _animateColumnControllerToItem(yearController, newDate.year);
} }
@ -1608,7 +1608,7 @@ class _CupertinoTimerPickerState extends State<CupertinoTimerPicker> {
if (widget.mode != CupertinoTimerPickerMode.hm) if (widget.mode != CupertinoTimerPickerMode.hm)
selectedSecond = widget.initialTimerDuration.inSeconds % 60; selectedSecond = widget.initialTimerDuration.inSeconds % 60;
PaintingBinding.instance!.systemFonts.addListener(_handleSystemFontsChange); PaintingBinding.instance.systemFonts.addListener(_handleSystemFontsChange);
} }
void _handleSystemFontsChange() { void _handleSystemFontsChange() {
@ -1621,7 +1621,7 @@ class _CupertinoTimerPickerState extends State<CupertinoTimerPicker> {
@override @override
void dispose() { void dispose() {
PaintingBinding.instance!.systemFonts.removeListener(_handleSystemFontsChange); PaintingBinding.instance.systemFonts.removeListener(_handleSystemFontsChange);
super.dispose(); super.dispose();
} }

View File

@ -506,10 +506,10 @@ class _CupertinoSliverRefreshControlState extends State<CupertinoSliverRefreshCo
nextState = RefreshIndicatorMode.done; nextState = RefreshIndicatorMode.done;
// Either schedule the RenderSliver to re-layout on the next frame // Either schedule the RenderSliver to re-layout on the next frame
// when not currently in a frame or schedule it on the next frame. // when not currently in a frame or schedule it on the next frame.
if (SchedulerBinding.instance!.schedulerPhase == SchedulerPhase.idle) { if (SchedulerBinding.instance.schedulerPhase == SchedulerPhase.idle) {
setState(() => hasSliverLayoutExtent = false); setState(() => hasSliverLayoutExtent = false);
} else { } else {
SchedulerBinding.instance!.addPostFrameCallback((Duration timestamp) { SchedulerBinding.instance.addPostFrameCallback((Duration timestamp) {
setState(() => hasSliverLayoutExtent = false); setState(() => hasSliverLayoutExtent = false);
}); });
} }
@ -535,7 +535,7 @@ class _CupertinoSliverRefreshControlState extends State<CupertinoSliverRefreshCo
// Call onRefresh after this frame finished since the function is // Call onRefresh after this frame finished since the function is
// user supplied and we're always here in the middle of the sliver's // user supplied and we're always here in the middle of the sliver's
// performLayout. // performLayout.
SchedulerBinding.instance!.addPostFrameCallback((Duration timestamp) { SchedulerBinding.instance.addPostFrameCallback((Duration timestamp) {
refreshTask = widget.onRefresh!()..whenComplete(() { refreshTask = widget.onRefresh!()..whenComplete(() {
if (mounted) { if (mounted) {
setState(() => refreshTask = null); setState(() => refreshTask = null);

View File

@ -7,6 +7,11 @@ Currently they do depend on dart:ui, but only for `VoidCallback` and
`hashValues` (and maybe one day `hashList` and `lerpDouble`), which `hashValues` (and maybe one day `hashList` and `lerpDouble`), which
are all intended to be moved out of `dart:ui` and into `dart:core`. are all intended to be moved out of `dart:ui` and into `dart:core`.
There is currently also an unfortunate dependency on the platform
dispatcher logic (SingletonFlutterWindow, Brightness,
PlatformDispatcher, window), though that should probably move to the
'services' library.
See also: See also:
* https://github.com/dart-lang/sdk/issues/27791 (`VoidCallback`) * https://github.com/dart-lang/sdk/issues/27791 (`VoidCallback`)

View File

@ -14,10 +14,14 @@ import 'assertions.dart';
import 'basic_types.dart'; import 'basic_types.dart';
import 'constants.dart'; import 'constants.dart';
import 'debug.dart'; import 'debug.dart';
import 'diagnostics.dart';
import 'object.dart'; import 'object.dart';
import 'platform.dart'; import 'platform.dart';
import 'print.dart'; import 'print.dart';
// Examples can assume:
// mixin BarBinding on BindingBase { }
/// Signature for service extensions. /// Signature for service extensions.
/// ///
/// The returned map must not contain the keys "type" or "method", as /// The returned map must not contain the keys "type" or "method", as
@ -27,21 +31,101 @@ import 'print.dart';
/// "method" key will be set to the full name of the method. /// "method" key will be set to the full name of the method.
typedef ServiceExtensionCallback = Future<Map<String, dynamic>> Function(Map<String, String> parameters); typedef ServiceExtensionCallback = Future<Map<String, dynamic>> Function(Map<String, String> parameters);
/// Base class for mixins that provide singleton services (also known as /// Base class for mixins that provide singleton services.
/// "bindings").
/// ///
/// To use this class in an `on` clause of a mixin, inherit from it and implement /// The Flutter engine ([dart:ui]) exposes some low-level services,
/// [initInstances()]. The mixin is guaranteed to only be constructed once in /// but these are typically not suitable for direct use, for example
/// the lifetime of the app (more precisely, it will assert if constructed twice /// because they only provide a single callback which an application
/// in checked mode). /// may wish to multiplex to allow multiple listeners.
/// ///
/// The top-most layer used to write the application will have a concrete class /// Bindings provide the glue between these low-level APIs and the
/// that inherits from [BindingBase] and uses all the various [BindingBase] /// higher-level framework APIs. They _bind_ the two together, whence
/// mixins (such as [ServicesBinding]). For example, the Widgets library in /// the name.
/// Flutter introduces a binding called [WidgetsFlutterBinding]. The relevant ///
/// library defines how to create the binding. It could be implied (for example, /// ## Implementing a binding mixin
/// [WidgetsFlutterBinding] is automatically started from [runApp]), or the ///
/// application might be required to explicitly call the constructor. /// 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].
///
/// {@tool snippet}
///
/// Here is a basic example of a binding that implements these features. It relies on
/// another fictional binding called `BarBinding`.
///
/// ```dart
/// 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...
/// }
/// ```
/// {@end-tool}
///
/// ## 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.
///
/// {@tool snippet}
///
/// A typical binding class is shown below. The `ensureInitialized` method's
/// return type is the library's binding mixin, rather than the concrete
/// class.
///
/// ```dart
/// class FooLibraryBinding extends BindingBase with BarBinding, FooBinding {
/// static FooBinding ensureInitialized() {
/// if (FooBinding._instance == null) {
/// FooLibraryBinding();
/// }
/// return FooBinding.instance;
/// }
/// }
/// /// ```
/// {@end-tool}
abstract class BindingBase { abstract class BindingBase {
/// Default abstract constructor for bindings. /// Default abstract constructor for bindings.
/// ///
@ -51,6 +135,10 @@ abstract class BindingBase {
/// observatory service extensions, if any. /// observatory service extensions, if any.
BindingBase() { BindingBase() {
developer.Timeline.startSync('Framework initialization'); developer.Timeline.startSync('Framework initialization');
assert(() {
_debugConstructed = true;
return true;
}());
assert(!_debugInitialized); assert(!_debugInitialized);
initInstances(); initInstances();
@ -65,6 +153,7 @@ abstract class BindingBase {
developer.Timeline.finishSync(); developer.Timeline.finishSync();
} }
bool _debugConstructed = false;
static bool _debugInitialized = false; static bool _debugInitialized = false;
static bool _debugServiceExtensionsRegistered = false; static bool _debugServiceExtensionsRegistered = false;
@ -131,10 +220,39 @@ abstract class BindingBase {
/// the platform and otherwise configure their services. Subclasses must call /// the platform and otherwise configure their services. Subclasses must call
/// "super.initInstances()". /// "super.initInstances()".
/// ///
/// By convention, if the service is to be provided as a singleton, it should /// The binding is not fully initialized when this method runs (for
/// be exposed as `MixinClassName.instance`, a static getter that returns /// example, other binding mixins may not yet have run their
/// [initInstances] method). For this reason, code in this method
/// should avoid invoking callbacks or synchronously triggering any
/// code that would normally assume that the bindings are ready.
///
/// {@tool snippet}
///
/// By convention, if the service is to be provided as a singleton,
/// it should be exposed as `MixinClassName.instance`, a static
/// getter with a non-nullable return type that returns
/// `MixinClassName._instance`, a static field that is set by /// `MixinClassName._instance`, a static field that is set by
/// `initInstances()`. /// `initInstances()`. To improve the developer experience, the
/// return value should actually be
/// `BindingBase.checkInstance(_instance)` (see [checkInstance]), as
/// in the example below.
///
/// ```dart
/// mixin BazBinding on BindingBase {
/// @override
/// void initInstances() {
/// super.initInstances();
/// _instance = this;
/// // ...binding initialization...
/// }
///
/// static BazBinding get instance => BindingBase.checkInstance(_instance);
/// static BazBinding? _instance;
///
/// // ...binding features...
/// }
/// ```
/// {@end-tool}
@protected @protected
@mustCallSuper @mustCallSuper
void initInstances() { void initInstances() {
@ -145,6 +263,100 @@ abstract class BindingBase {
}()); }());
} }
/// A method that shows a useful error message if the given binding
/// instance is not initialized.
///
/// See [initInstances] for advice on using this method.
///
/// This method either returns the argument or throws an exception.
/// In release mode it always returns the argument.
///
/// The type argument `T` should be the kind of binding mixin (e.g.
/// `SchedulerBinding`) that is calling the method. It is used in
/// error messages.
@protected
static T checkInstance<T extends BindingBase>(T? instance) {
assert(() {
if (!_debugInitialized && instance == null) {
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('Binding has not yet been initialized.'),
ErrorDescription('The "instance" getter on the $T binding mixin is only available once that binding has been initialized.'),
ErrorHint(
'Typically, this is done by calling "WidgetsFlutterBinding.ensureInitialized()" or "runApp()" (the '
'latter calls the former). Typically this call is done in the "void main()" method. The "ensureInitialized" method '
'is idempotent; calling it multiple times is not harmful. After calling that method, the "instance" getter will '
'return the binding.',
),
ErrorHint(
'In a test, one can call "TestWidgetsFlutterBinding.ensureInitialized()" as the first line in the test\'s "main()" method '
'to initialize the binding.',
),
ErrorHint(
'If $T is a custom binding mixin, there must also be a custom binding class, like WidgetsFlutterBinding, '
'but that mixes in the selected binding, and that is the class that must be constructed before using the "instance" getter.',
),
]);
}
if (instance == null) {
assert(_debugInitialized);
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('Binding mixin instance is null but bindings are already initialized.'),
ErrorDescription(
'The "instance" property of the $T binding mixin was accessed, but that binding was not initialized when '
'the "initInstances()" method was called.'
),
ErrorHint(
'This probably indicates that the $T mixin was not mixed into the class that was used to initialize the binding. '
'If this is a custom binding mixin, there must also be a custom binding class, like WidgetsFlutterBinding, '
'but that mixes in the selected binding. If this is a test binding, check that the binding being initialized '
'is the same as the one into which the test binding is mixed.'
),
]);
}
try {
assert(instance != null);
if (instance._debugConstructed && !_debugInitialized) {
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('Binding initialized without calling initInstances.'),
ErrorDescription('An instance of $T is non-null, but BindingBase.initInstances() has not yet been called.'),
ErrorHint(
'This could happen because a binding mixin was somehow used outside of the normal binding mechanisms, or because '
'the binding\'s initInstances() method did not call "super.initInstances()".'
),
ErrorHint(
'This could also happen if some code was invoked that used the binding while the binding was initializing, '
'for example if the "initInstances" method invokes a callback. Bindings should not invoke callbacks before '
'"initInstances" has completed.'
),
]);
}
if (!instance._debugConstructed) {
// The state of _debugInitialized doesn't matter in this failure mode.
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('Binding did not complete initialization.'),
ErrorDescription('An instance of $T is non-null, but the BindingBase() constructor has not yet been called.'),
ErrorHint(
'This could also happen if some code was invoked that used the binding while the binding was initializing, '
"for example if the binding's constructor itself invokes a callback. Bindings should not invoke callbacks "
'before "initInstances" has completed.'
),
]);
}
} on NoSuchMethodError {
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('Binding does not extend BindingBase'),
ErrorDescription('An instance of $T was created but the BindingBase constructor was not called.'),
ErrorHint(
'This could happen because the binding was implemented using "implements" rather than "extends" or "with". '
'Concrete binding classes must extend or mix in BindingBase.'
),
]);
}
return true;
}());
return instance!;
}
/// Called when the binding is initialized, to register service /// Called when the binding is initialized, to register service
/// extensions. /// extensions.
/// ///
@ -555,7 +767,7 @@ abstract class BindingBase {
/// available in debug and profile mode. /// available in debug and profile mode.
/// ///
/// ```dart /// ```dart
/// void myRegistrationFunction() { /// void myOtherRegistrationFunction() {
/// // kReleaseMode is defined in the 'flutter/foundation.dart' package. /// // kReleaseMode is defined in the 'flutter/foundation.dart' package.
/// if (!kReleaseMode) { /// if (!kReleaseMode) {
/// // Register your service extension here. /// // Register your service extension here.

View File

@ -73,7 +73,7 @@ class _Resampler {
// Add `event` for resampling or dispatch it directly if // Add `event` for resampling or dispatch it directly if
// not a touch event. // not a touch event.
void addOrDispatch(PointerEvent event) { void addOrDispatch(PointerEvent event) {
final SchedulerBinding? scheduler = SchedulerBinding.instance; final SchedulerBinding scheduler = SchedulerBinding.instance;
assert(scheduler != null); assert(scheduler != null);
// Add touch event to resampler or dispatch pointer event directly. // Add touch event to resampler or dispatch pointer event directly.
if (event.kind == PointerDeviceKind.touch) { if (event.kind == PointerDeviceKind.touch) {
@ -94,9 +94,10 @@ class _Resampler {
// //
// The `samplingOffset` is relative to the current frame time, which // The `samplingOffset` is relative to the current frame time, which
// can be in the past when we're not actively resampling. // can be in the past when we're not actively resampling.
//
// The `samplingClock` is the clock used to determine frame time age. // The `samplingClock` is the clock used to determine frame time age.
void sample(Duration samplingOffset, SamplingClock clock) { void sample(Duration samplingOffset, SamplingClock clock) {
final SchedulerBinding? scheduler = SchedulerBinding.instance; final SchedulerBinding scheduler = SchedulerBinding.instance;
assert(scheduler != null); assert(scheduler != null);
// Initialize `_frameTime` if needed. This will be used for periodic // Initialize `_frameTime` if needed. This will be used for periodic
@ -156,7 +157,7 @@ class _Resampler {
// Add a post frame callback as this avoids producing unnecessary // Add a post frame callback as this avoids producing unnecessary
// frames but ensures that sampling phase is adjusted to frame // frames but ensures that sampling phase is adjusted to frame
// time when frames are produced. // time when frames are produced.
scheduler?.addPostFrameCallback((_) { scheduler.addPostFrameCallback((_) {
_frameCallbackScheduled = false; _frameCallbackScheduled = false;
// We use `currentSystemFrameTimeStamp` here as it's critical that // We use `currentSystemFrameTimeStamp` here as it's critical that
// sample time is in the same clock as the event time stamps, and // sample time is in the same clock as the event time stamps, and
@ -259,16 +260,20 @@ mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, H
window.onPointerDataPacket = _handlePointerDataPacket; window.onPointerDataPacket = _handlePointerDataPacket;
} }
/// The singleton instance of this object.
///
/// Provides access to the features exposed by this mixin. The binding must
/// be initialized before using this getter; this is typically done by calling
/// [runApp] or [WidgetsFlutterBinding.ensureInitialized].
static GestureBinding get instance => BindingBase.checkInstance(_instance);
static GestureBinding? _instance;
@override @override
void unlocked() { void unlocked() {
super.unlocked(); super.unlocked();
_flushPointerEventQueue(); _flushPointerEventQueue();
} }
/// The singleton instance of this object.
static GestureBinding? get instance => _instance;
static GestureBinding? _instance;
final Queue<PointerEvent> _pendingPointerEvents = Queue<PointerEvent>(); final Queue<PointerEvent> _pendingPointerEvents = Queue<PointerEvent>();
void _handlePointerDataPacket(ui.PointerDataPacket packet) { void _handlePointerDataPacket(ui.PointerDataPacket packet) {

View File

@ -231,8 +231,8 @@ abstract class MultiDragGestureRecognizer extends GestureRecognizer {
assert(!_pointers!.containsKey(event.pointer)); assert(!_pointers!.containsKey(event.pointer));
final MultiDragPointerState state = createNewPointerState(event); final MultiDragPointerState state = createNewPointerState(event);
_pointers![event.pointer] = state; _pointers![event.pointer] = state;
GestureBinding.instance!.pointerRouter.addRoute(event.pointer, _handleEvent); GestureBinding.instance.pointerRouter.addRoute(event.pointer, _handleEvent);
state._setArenaEntry(GestureBinding.instance!.gestureArena.add(event.pointer, this)); state._setArenaEntry(GestureBinding.instance.gestureArena.add(event.pointer, this));
} }
/// Subclasses should override this method to create per-pointer state /// Subclasses should override this method to create per-pointer state
@ -312,7 +312,7 @@ abstract class MultiDragGestureRecognizer extends GestureRecognizer {
return; return;
} }
assert(_pointers!.containsKey(pointer)); assert(_pointers!.containsKey(pointer));
GestureBinding.instance!.pointerRouter.removeRoute(pointer, _handleEvent); GestureBinding.instance.pointerRouter.removeRoute(pointer, _handleEvent);
_pointers!.remove(pointer)!.dispose(); _pointers!.remove(pointer)!.dispose();
} }

View File

@ -79,14 +79,14 @@ class _TapTracker {
void startTrackingPointer(PointerRoute route, Matrix4? transform) { void startTrackingPointer(PointerRoute route, Matrix4? transform) {
if (!_isTrackingPointer) { if (!_isTrackingPointer) {
_isTrackingPointer = true; _isTrackingPointer = true;
GestureBinding.instance!.pointerRouter.addRoute(pointer, route, transform); GestureBinding.instance.pointerRouter.addRoute(pointer, route, transform);
} }
} }
void stopTrackingPointer(PointerRoute route) { void stopTrackingPointer(PointerRoute route) {
if (_isTrackingPointer) { if (_isTrackingPointer) {
_isTrackingPointer = false; _isTrackingPointer = false;
GestureBinding.instance!.pointerRouter.removeRoute(pointer, route); GestureBinding.instance.pointerRouter.removeRoute(pointer, route);
} }
} }
@ -242,7 +242,7 @@ class DoubleTapGestureRecognizer extends GestureRecognizer {
_stopDoubleTapTimer(); _stopDoubleTapTimer();
final _TapTracker tracker = _TapTracker( final _TapTracker tracker = _TapTracker(
event: event, event: event,
entry: GestureBinding.instance!.gestureArena.add(event.pointer, this), entry: GestureBinding.instance.gestureArena.add(event.pointer, this),
doubleTapMinTime: kDoubleTapMinTime, doubleTapMinTime: kDoubleTapMinTime,
); );
_trackers[event.pointer] = tracker; _trackers[event.pointer] = tracker;
@ -311,14 +311,14 @@ class DoubleTapGestureRecognizer extends GestureRecognizer {
final _TapTracker tracker = _firstTap!; final _TapTracker tracker = _firstTap!;
_firstTap = null; _firstTap = null;
_reject(tracker); _reject(tracker);
GestureBinding.instance!.gestureArena.release(tracker.pointer); GestureBinding.instance.gestureArena.release(tracker.pointer);
} }
_clearTrackers(); _clearTrackers();
} }
void _registerFirstTap(_TapTracker tracker) { void _registerFirstTap(_TapTracker tracker) {
_startDoubleTapTimer(); _startDoubleTapTimer();
GestureBinding.instance!.gestureArena.hold(tracker.pointer); GestureBinding.instance.gestureArena.hold(tracker.pointer);
// Note, order is important below in order for the clear -> reject logic to // Note, order is important below in order for the clear -> reject logic to
// work properly. // work properly.
_freezeTracker(tracker); _freezeTracker(tracker);
@ -383,7 +383,7 @@ class _TapGesture extends _TapTracker {
}) : _lastPosition = OffsetPair.fromEventPosition(event), }) : _lastPosition = OffsetPair.fromEventPosition(event),
super( super(
event: event as PointerDownEvent, event: event as PointerDownEvent,
entry: GestureBinding.instance!.gestureArena.add(event.pointer, gestureRecognizer), entry: GestureBinding.instance.gestureArena.add(event.pointer, gestureRecognizer),
doubleTapMinTime: kDoubleTapMinTime, doubleTapMinTime: kDoubleTapMinTime,
) { ) {
startTrackingPointer(handleEvent, event.transform); startTrackingPointer(handleEvent, event.transform);

View File

@ -30,7 +30,7 @@ bool _isSameEvent(PointerSignalEvent event1, PointerSignalEvent event2) {
/// ///
/// ```dart /// ```dart
/// void handleSignalEvent(PointerSignalEvent event) { /// void handleSignalEvent(PointerSignalEvent event) {
/// GestureBinding.instance!.pointerSignalResolver.register(event, (PointerSignalEvent event) { /// GestureBinding.instance.pointerSignalResolver.register(event, (PointerSignalEvent event) {
/// // handle the event... /// // handle the event...
/// }); /// });
/// } /// }
@ -96,7 +96,7 @@ bool _isSameEvent(PointerSignalEvent event1, PointerSignalEvent event2) {
/// child: Listener( /// child: Listener(
/// onPointerSignal: (PointerSignalEvent event) { /// onPointerSignal: (PointerSignalEvent event) {
/// if (widget.useResolver) { /// if (widget.useResolver) {
/// GestureBinding.instance!.pointerSignalResolver.register(event, (PointerSignalEvent event) { /// GestureBinding.instance.pointerSignalResolver.register(event, (PointerSignalEvent event) {
/// rotateColor(); /// rotateColor();
/// }); /// });
/// } else { /// } else {

View File

@ -317,7 +317,7 @@ abstract class OneSequenceGestureRecognizer extends GestureRecognizer {
void dispose() { void dispose() {
resolve(GestureDisposition.rejected); resolve(GestureDisposition.rejected);
for (final int pointer in _trackedPointers) for (final int pointer in _trackedPointers)
GestureBinding.instance!.pointerRouter.removeRoute(pointer, handleEvent); GestureBinding.instance.pointerRouter.removeRoute(pointer, handleEvent);
_trackedPointers.clear(); _trackedPointers.clear();
assert(_entries.isEmpty); assert(_entries.isEmpty);
super.dispose(); super.dispose();
@ -347,7 +347,7 @@ abstract class OneSequenceGestureRecognizer extends GestureRecognizer {
GestureArenaEntry _addPointerToArena(int pointer) { GestureArenaEntry _addPointerToArena(int pointer) {
if (_team != null) if (_team != null)
return _team!.add(pointer, this); return _team!.add(pointer, this);
return GestureBinding.instance!.gestureArena.add(pointer, this); return GestureBinding.instance.gestureArena.add(pointer, this);
} }
/// Causes events related to the given pointer ID to be routed to this recognizer. /// Causes events related to the given pointer ID to be routed to this recognizer.
@ -366,7 +366,7 @@ abstract class OneSequenceGestureRecognizer extends GestureRecognizer {
/// This is called by [OneSequenceGestureRecognizer.addAllowedPointer]. /// This is called by [OneSequenceGestureRecognizer.addAllowedPointer].
@protected @protected
void startTrackingPointer(int pointer, [Matrix4? transform]) { void startTrackingPointer(int pointer, [Matrix4? transform]) {
GestureBinding.instance!.pointerRouter.addRoute(pointer, handleEvent, transform); GestureBinding.instance.pointerRouter.addRoute(pointer, handleEvent, transform);
_trackedPointers.add(pointer); _trackedPointers.add(pointer);
assert(!_entries.containsValue(pointer)); assert(!_entries.containsValue(pointer));
_entries[pointer] = _addPointerToArena(pointer); _entries[pointer] = _addPointerToArena(pointer);
@ -381,7 +381,7 @@ abstract class OneSequenceGestureRecognizer extends GestureRecognizer {
@protected @protected
void stopTrackingPointer(int pointer) { void stopTrackingPointer(int pointer) {
if (_trackedPointers.contains(pointer)) { if (_trackedPointers.contains(pointer)) {
GestureBinding.instance!.pointerRouter.removeRoute(pointer, handleEvent); GestureBinding.instance.pointerRouter.removeRoute(pointer, handleEvent);
_trackedPointers.remove(pointer); _trackedPointers.remove(pointer);
if (_trackedPointers.isEmpty) if (_trackedPointers.isEmpty)
didStopTrackingLastPointer(pointer); didStopTrackingLastPointer(pointer);

View File

@ -61,7 +61,7 @@ class _CombiningGestureArenaMember extends GestureArenaMember {
assert(!_resolved); assert(!_resolved);
assert(_pointer == pointer); assert(_pointer == pointer);
_members.add(member); _members.add(member);
_entry ??= GestureBinding.instance!.gestureArena.add(pointer, this); _entry ??= GestureBinding.instance.gestureArena.add(pointer, this);
return _CombiningGestureArenaEntry(this, member); return _CombiningGestureArenaEntry(this, member);
} }

View File

@ -844,7 +844,7 @@ class _PackageLicensePageState extends State<_PackageLicensePage> {
return true; return true;
}()); }());
final List<LicenseParagraph> paragraphs = final List<LicenseParagraph> paragraphs =
await SchedulerBinding.instance!.scheduleTask<List<LicenseParagraph>>( await SchedulerBinding.instance.scheduleTask<List<LicenseParagraph>>(
license.paragraphs.toList, license.paragraphs.toList,
Priority.animation, Priority.animation,
debugLabel: 'License', debugLabel: 'License',
@ -1507,15 +1507,13 @@ class _MasterDetailScaffoldState extends State<_MasterDetailScaffold>
@override @override
void openDetailPage(Object arguments) { void openDetailPage(Object arguments) {
SchedulerBinding.instance! SchedulerBinding.instance.addPostFrameCallback((_) => _detailArguments.value = arguments);
.addPostFrameCallback((_) => _detailArguments.value = arguments);
_MasterDetailFlow.of(context)!.openDetailPage(arguments); _MasterDetailFlow.of(context)!.openDetailPage(arguments);
} }
@override @override
void setInitialDetailPage(Object arguments) { void setInitialDetailPage(Object arguments) {
SchedulerBinding.instance! SchedulerBinding.instance.addPostFrameCallback((_) => _detailArguments.value = arguments);
.addPostFrameCallback((_) => _detailArguments.value = arguments);
_MasterDetailFlow.of(context)!.setInitialDetailPage(arguments); _MasterDetailFlow.of(context)!.setInitialDetailPage(arguments);
} }

View File

@ -301,7 +301,7 @@ class _AutocompleteOptions<T extends Object> extends StatelessWidget {
builder: (BuildContext context) { builder: (BuildContext context) {
final bool highlight = AutocompleteHighlightedOption.of(context) == index; final bool highlight = AutocompleteHighlightedOption.of(context) == index;
if (highlight) { if (highlight) {
SchedulerBinding.instance!.addPostFrameCallback((Duration timeStamp) { SchedulerBinding.instance.addPostFrameCallback((Duration timeStamp) {
Scrollable.ensureVisible(context, alignment: 0.5); Scrollable.ensureVisible(context, alignment: 0.5);
}); });
} }

View File

@ -533,7 +533,7 @@ class _MonthPickerState extends State<_MonthPicker> {
super.didUpdateWidget(oldWidget); super.didUpdateWidget(oldWidget);
if (widget.initialMonth != oldWidget.initialMonth && widget.initialMonth != _currentMonth) { if (widget.initialMonth != oldWidget.initialMonth && widget.initialMonth != _currentMonth) {
// We can't interrupt this widget build with a scroll, so do it next frame // We can't interrupt this widget build with a scroll, so do it next frame
WidgetsBinding.instance!.addPostFrameCallback( WidgetsBinding.instance.addPostFrameCallback(
(Duration timeStamp) => _showMonth(widget.initialMonth, jump: true), (Duration timeStamp) => _showMonth(widget.initialMonth, jump: true),
); );
} }

View File

@ -1237,16 +1237,16 @@ class _DropdownButtonState<T> extends State<DropdownButton<T>> with WidgetsBindi
), ),
}; };
focusNode!.addListener(_handleFocusChanged); focusNode!.addListener(_handleFocusChanged);
final FocusManager focusManager = WidgetsBinding.instance!.focusManager; final FocusManager focusManager = WidgetsBinding.instance.focusManager;
_focusHighlightMode = focusManager.highlightMode; _focusHighlightMode = focusManager.highlightMode;
focusManager.addHighlightModeListener(_handleFocusHighlightModeChange); focusManager.addHighlightModeListener(_handleFocusHighlightModeChange);
} }
@override @override
void dispose() { void dispose() {
WidgetsBinding.instance!.removeObserver(this); WidgetsBinding.instance.removeObserver(this);
_removeDropdownRoute(); _removeDropdownRoute();
WidgetsBinding.instance!.focusManager.removeHighlightModeListener(_handleFocusHighlightModeChange); WidgetsBinding.instance.focusManager.removeHighlightModeListener(_handleFocusHighlightModeChange);
focusNode!.removeListener(_handleFocusChanged); focusNode!.removeListener(_handleFocusChanged);
_internalNode?.dispose(); _internalNode?.dispose();
super.dispose(); super.dispose();

View File

@ -161,7 +161,7 @@ class _InputDatePickerFormFieldState extends State<InputDatePickerFormField> {
super.didUpdateWidget(oldWidget); super.didUpdateWidget(oldWidget);
if (widget.initialDate != oldWidget.initialDate) { if (widget.initialDate != oldWidget.initialDate) {
// Can't update the form field in the middle of a build, so do it next frame // Can't update the form field in the middle of a build, so do it next frame
WidgetsBinding.instance!.addPostFrameCallback((Duration timeStamp) { WidgetsBinding.instance.addPostFrameCallback((Duration timeStamp) {
setState(() { setState(() {
_selectedDate = widget.initialDate; _selectedDate = widget.initialDate;
_updateValueForSelectedDate(); _updateValueForSelectedDate();

View File

@ -833,7 +833,7 @@ class _DialPainter extends CustomPainter {
required this.theta, required this.theta,
required this.textDirection, required this.textDirection,
required this.selectedValue, required this.selectedValue,
}) : super(repaint: PaintingBinding.instance!.systemFonts); }) : super(repaint: PaintingBinding.instance.systemFonts);
final List<_TappableLabel> primaryLabels; final List<_TappableLabel> primaryLabels;
final List<_TappableLabel> secondaryLabels; final List<_TappableLabel> secondaryLabels;

View File

@ -269,7 +269,7 @@ class _TooltipState extends State<Tooltip> with SingleTickerProviderStateMixin {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_mouseIsConnected = RendererBinding.instance!.mouseTracker.mouseIsConnected; _mouseIsConnected = RendererBinding.instance.mouseTracker.mouseIsConnected;
_controller = AnimationController( _controller = AnimationController(
duration: _fadeInDuration, duration: _fadeInDuration,
reverseDuration: _fadeOutDuration, reverseDuration: _fadeOutDuration,
@ -277,10 +277,10 @@ class _TooltipState extends State<Tooltip> with SingleTickerProviderStateMixin {
) )
..addStatusListener(_handleStatusChanged); ..addStatusListener(_handleStatusChanged);
// Listen to see when a mouse is added. // Listen to see when a mouse is added.
RendererBinding.instance!.mouseTracker.addListener(_handleMouseTrackerChange); RendererBinding.instance.mouseTracker.addListener(_handleMouseTrackerChange);
// Listen to global pointer events so that we can hide a tooltip immediately // Listen to global pointer events so that we can hide a tooltip immediately
// if some other control is clicked on. // if some other control is clicked on.
GestureBinding.instance!.pointerRouter.addGlobalRoute(_handlePointerEvent); GestureBinding.instance.pointerRouter.addGlobalRoute(_handlePointerEvent);
} }
// https://material.io/components/tooltips#specs // https://material.io/components/tooltips#specs
@ -325,7 +325,7 @@ class _TooltipState extends State<Tooltip> with SingleTickerProviderStateMixin {
if (!mounted) { if (!mounted) {
return; return;
} }
final bool mouseIsConnected = RendererBinding.instance!.mouseTracker.mouseIsConnected; final bool mouseIsConnected = RendererBinding.instance.mouseTracker.mouseIsConnected;
if (mouseIsConnected != _mouseIsConnected) { if (mouseIsConnected != _mouseIsConnected) {
setState(() { setState(() {
_mouseIsConnected = mouseIsConnected; _mouseIsConnected = mouseIsConnected;
@ -456,8 +456,8 @@ class _TooltipState extends State<Tooltip> with SingleTickerProviderStateMixin {
@override @override
void dispose() { void dispose() {
GestureBinding.instance!.pointerRouter.removeGlobalRoute(_handlePointerEvent); GestureBinding.instance.pointerRouter.removeGlobalRoute(_handlePointerEvent);
RendererBinding.instance!.mouseTracker.removeListener(_handleMouseTrackerChange); RendererBinding.instance.mouseTracker.removeListener(_handleMouseTrackerChange);
_removeEntry(); _removeEntry();
_controller.dispose(); _controller.dispose();
super.dispose(); super.dispose();

View File

@ -118,7 +118,7 @@ class NetworkImage extends image_provider.ImageProvider<image_provider.NetworkIm
// have had a chance to track the key in the cache at all. // have had a chance to track the key in the cache at all.
// Schedule a microtask to give the cache a chance to add the key. // Schedule a microtask to give the cache a chance to add the key.
scheduleMicrotask(() { scheduleMicrotask(() {
PaintingBinding.instance!.imageCache!.evict(key); PaintingBinding.instance.imageCache.evict(key);
}); });
rethrow; rethrow;
} finally { } finally {

View File

@ -2,7 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:typed_data' show Uint8List; import 'dart:typed_data' show Uint8List;
import 'dart:ui' as ui show instantiateImageCodec, Codec; import 'dart:ui' as ui show instantiateImageCodec, Codec;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
@ -26,7 +25,11 @@ mixin PaintingBinding on BindingBase, ServicesBinding {
} }
/// The current [PaintingBinding], if one has been created. /// The current [PaintingBinding], if one has been created.
static PaintingBinding? get instance => _instance; ///
/// Provides access to the features exposed by this mixin. The binding must
/// be initialized before using this getter; this is typically done by calling
/// [runApp] or [WidgetsFlutterBinding.ensureInitialized].
static PaintingBinding get instance => BindingBase.checkInstance(_instance);
static PaintingBinding? _instance; static PaintingBinding? _instance;
/// [ShaderWarmUp] instance to be executed during [initInstances]. /// [ShaderWarmUp] instance to be executed during [initInstances].
@ -67,8 +70,8 @@ mixin PaintingBinding on BindingBase, ServicesBinding {
/// ///
/// The image cache is created during startup by the [createImageCache] /// The image cache is created during startup by the [createImageCache]
/// method. /// method.
ImageCache? get imageCache => _imageCache; ImageCache get imageCache => _imageCache;
ImageCache? _imageCache; late ImageCache _imageCache;
/// Creates the [ImageCache] singleton (accessible via [imageCache]). /// Creates the [ImageCache] singleton (accessible via [imageCache]).
/// ///
@ -114,14 +117,14 @@ mixin PaintingBinding on BindingBase, ServicesBinding {
@override @override
void evict(String asset) { void evict(String asset) {
super.evict(asset); super.evict(asset);
imageCache!.clear(); imageCache.clear();
imageCache!.clearLiveImages(); imageCache.clearLiveImages();
} }
@override @override
void handleMemoryPressure() { void handleMemoryPressure() {
super.handleMemoryPressure(); super.handleMemoryPressure();
imageCache?.clear(); imageCache.clear();
} }
/// Listenable that notifies when the available fonts on the system have /// Listenable that notifies when the available fonts on the system have
@ -176,4 +179,4 @@ class _SystemFontsNotifier extends Listenable {
/// ///
/// The image cache is created during startup by the [PaintingBinding]'s /// The image cache is created during startup by the [PaintingBinding]'s
/// [PaintingBinding.createImageCache] method. /// [PaintingBinding.createImageCache] method.
ImageCache? get imageCache => PaintingBinding.instance!.imageCache; ImageCache get imageCache => PaintingBinding.instance.imageCache;

View File

@ -539,7 +539,7 @@ void paintImage({
_pendingImageSizeInfo[sizeInfo.source!] = sizeInfo; _pendingImageSizeInfo[sizeInfo.source!] = sizeInfo;
} }
debugOnPaintImage?.call(sizeInfo); debugOnPaintImage?.call(sizeInfo);
SchedulerBinding.instance!.addPostFrameCallback((Duration timeStamp) { SchedulerBinding.instance.addPostFrameCallback((Duration timeStamp) {
_lastFrameImageSizeInfo = _pendingImageSizeInfo.values.toSet(); _lastFrameImageSizeInfo = _pendingImageSizeInfo.values.toSet();
if (_pendingImageSizeInfo.isEmpty) { if (_pendingImageSizeInfo.isEmpty) {
return; return;

View File

@ -622,7 +622,7 @@ abstract class _CachedImageBase {
assert(handle != null); assert(handle != null);
// Give any interested parties a chance to listen to the stream before we // Give any interested parties a chance to listen to the stream before we
// potentially dispose it. // potentially dispose it.
SchedulerBinding.instance!.addPostFrameCallback((Duration timeStamp) { SchedulerBinding.instance.addPostFrameCallback((Duration timeStamp) {
assert(handle != null); assert(handle != null);
handle?.dispose(); handle?.dispose();
handle = null; handle = null;

View File

@ -21,7 +21,7 @@ import 'binding.dart';
/// [PaintingBinding.instantiateImageCodec], and therefore can be mocked in /// [PaintingBinding.instantiateImageCodec], and therefore can be mocked in
/// tests. /// tests.
Future<ui.Image> decodeImageFromList(Uint8List bytes) async { Future<ui.Image> decodeImageFromList(Uint8List bytes) async {
final ui.Codec codec = await PaintingBinding.instance!.instantiateImageCodec(bytes); final ui.Codec codec = await PaintingBinding.instance.instantiateImageCodec(bytes);
final ui.FrameInfo frameInfo = await codec.getNextFrame(); final ui.FrameInfo frameInfo = await codec.getNextFrame();
return frameInfo.image; return frameInfo.image;
} }

View File

@ -387,7 +387,7 @@ abstract class ImageProvider<T extends Object> {
_createErrorHandlerAndKey( _createErrorHandlerAndKey(
configuration, configuration,
(T key, ImageErrorListener innerHandleError) { (T key, ImageErrorListener innerHandleError) {
completer.complete(PaintingBinding.instance!.imageCache!.statusForKey(key)); completer.complete(PaintingBinding.instance.imageCache.statusForKey(key));
}, },
(T? key, Object exception, StackTrace? stack) async { (T? key, Object exception, StackTrace? stack) async {
if (handleError != null) { if (handleError != null) {
@ -492,7 +492,7 @@ abstract class ImageProvider<T extends Object> {
// the image we want before getting to this method. We should avoid calling // the image we want before getting to this method. We should avoid calling
// load again, but still update the image cache with LRU information. // load again, but still update the image cache with LRU information.
if (stream.completer != null) { if (stream.completer != null) {
final ImageStreamCompleter? completer = PaintingBinding.instance!.imageCache!.putIfAbsent( final ImageStreamCompleter? completer = PaintingBinding.instance.imageCache.putIfAbsent(
key, key,
() => stream.completer!, () => stream.completer!,
onError: handleError, onError: handleError,
@ -500,9 +500,9 @@ abstract class ImageProvider<T extends Object> {
assert(identical(completer, stream.completer)); assert(identical(completer, stream.completer));
return; return;
} }
final ImageStreamCompleter? completer = PaintingBinding.instance!.imageCache!.putIfAbsent( final ImageStreamCompleter? completer = PaintingBinding.instance.imageCache.putIfAbsent(
key, key,
() => load(key, PaintingBinding.instance!.instantiateImageCodec), () => load(key, PaintingBinding.instance.instantiateImageCodec),
onError: handleError, onError: handleError,
); );
if (completer != null) { if (completer != null) {
@ -557,7 +557,7 @@ abstract class ImageProvider<T extends Object> {
Future<bool> evict({ ImageCache? cache, ImageConfiguration configuration = ImageConfiguration.empty }) async { Future<bool> evict({ ImageCache? cache, ImageConfiguration configuration = ImageConfiguration.empty }) async {
cache ??= imageCache; cache ??= imageCache;
final T key = await obtainKey(configuration); final T key = await obtainKey(configuration);
return cache!.evict(key); return cache.evict(key);
} }
/// Converts an ImageProvider's settings plus an ImageConfiguration to a key /// Converts an ImageProvider's settings plus an ImageConfiguration to a key
@ -674,11 +674,11 @@ abstract class AssetBundleImageProvider extends ImageProvider<AssetBundleImageKe
try { try {
data = await key.bundle.load(key.name); data = await key.bundle.load(key.name);
} on FlutterError { } on FlutterError {
PaintingBinding.instance!.imageCache!.evict(key); PaintingBinding.instance.imageCache.evict(key);
rethrow; rethrow;
} }
if (data == null) { if (data == null) {
PaintingBinding.instance!.imageCache!.evict(key); PaintingBinding.instance.imageCache.evict(key);
throw StateError('Unable to read data'); throw StateError('Unable to read data');
} }
return decode(data.buffer.asUint8List()); return decode(data.buffer.asUint8List());
@ -891,7 +891,7 @@ class FileImage extends ImageProvider<FileImage> {
if (bytes.lengthInBytes == 0) { if (bytes.lengthInBytes == 0) {
// The file may become available later. // The file may become available later.
PaintingBinding.instance!.imageCache!.evict(key); PaintingBinding.instance.imageCache.evict(key);
throw StateError('$file is empty and cannot be loaded as an image.'); throw StateError('$file is empty and cannot be loaded as an image.');
} }

View File

@ -964,7 +964,7 @@ class MultiFrameImageStreamCompleter extends ImageStreamCompleter {
return; return;
} }
_frameCallbackScheduled = true; _frameCallbackScheduled = true;
SchedulerBinding.instance!.scheduleFrameCallback(_handleAppFrame); SchedulerBinding.instance.scheduleFrameCallback(_handleAppFrame);
} }
void _emitFrame(ImageInfo imageInfo) { void _emitFrame(ImageInfo imageInfo) {

View File

@ -49,7 +49,11 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Gesture
} }
/// The current [RendererBinding], if one has been created. /// The current [RendererBinding], if one has been created.
static RendererBinding? get instance => _instance; ///
/// Provides access to the features exposed by this mixin. The binding must
/// be initialized before using this getter; this is typically done by calling
/// [runApp] or [WidgetsFlutterBinding.ensureInitialized].
static RendererBinding get instance => BindingBase.checkInstance(_instance);
static RendererBinding? _instance; static RendererBinding? _instance;
@override @override
@ -114,7 +118,7 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Gesture
registerServiceExtension( registerServiceExtension(
name: 'debugDumpLayerTree', name: 'debugDumpLayerTree',
callback: (Map<String, String> parameters) async { callback: (Map<String, String> parameters) async {
final String data = RendererBinding.instance?.renderView.debugLayer?.toStringDeep() ?? 'Layer tree unavailable.'; final String data = RendererBinding.instance.renderView.debugLayer?.toStringDeep() ?? 'Layer tree unavailable.';
return <String, Object>{ return <String, Object>{
'data': data, 'data': data,
}; };
@ -128,7 +132,7 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Gesture
registerServiceExtension( registerServiceExtension(
name: 'debugDumpRenderTree', name: 'debugDumpRenderTree',
callback: (Map<String, String> parameters) async { callback: (Map<String, String> parameters) async {
final String data = RendererBinding.instance?.renderView.toStringDeep() ?? 'Render tree unavailable.'; final String data = RendererBinding.instance.renderView.toStringDeep();
return <String, Object>{ return <String, Object>{
'data': data, 'data': data,
}; };
@ -138,7 +142,7 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Gesture
registerServiceExtension( registerServiceExtension(
name: 'debugDumpSemanticsTreeInTraversalOrder', name: 'debugDumpSemanticsTreeInTraversalOrder',
callback: (Map<String, String> parameters) async { callback: (Map<String, String> parameters) async {
final String data = RendererBinding.instance?.renderView.debugSemantics final String data = RendererBinding.instance.renderView.debugSemantics
?.toStringDeep(childOrder: DebugSemanticsDumpOrder.traversalOrder) ?? 'Semantics not collected.'; ?.toStringDeep(childOrder: DebugSemanticsDumpOrder.traversalOrder) ?? 'Semantics not collected.';
return <String, Object>{ return <String, Object>{
'data': data, 'data': data,
@ -149,7 +153,7 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Gesture
registerServiceExtension( registerServiceExtension(
name: 'debugDumpSemanticsTreeInInverseHitTestOrder', name: 'debugDumpSemanticsTreeInInverseHitTestOrder',
callback: (Map<String, String> parameters) async { callback: (Map<String, String> parameters) async {
final String data = RendererBinding.instance?.renderView.debugSemantics final String data = RendererBinding.instance.renderView.debugSemantics
?.toStringDeep(childOrder: DebugSemanticsDumpOrder.inverseHitTest) ?? 'Semantics not collected.'; ?.toStringDeep(childOrder: DebugSemanticsDumpOrder.inverseHitTest) ?? 'Semantics not collected.';
return <String, Object>{ return <String, Object>{
'data': data, 'data': data,
@ -229,7 +233,7 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Gesture
/// Querying [PlatformDispatcher.platformBrightness]. /// Querying [PlatformDispatcher.platformBrightness].
/// ///
/// ```dart /// ```dart
/// final Brightness brightness = WidgetsBinding.instance!.platformDispatcher.platformBrightness; /// final Brightness brightness = WidgetsBinding.instance.platformDispatcher.platformBrightness;
/// ``` /// ```
/// {@end-tool} /// {@end-tool}
/// ///
@ -338,7 +342,7 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Gesture
_debugMouseTrackerUpdateScheduled = true; _debugMouseTrackerUpdateScheduled = true;
return true; return true;
}()); }());
SchedulerBinding.instance!.addPostFrameCallback((Duration duration) { SchedulerBinding.instance.addPostFrameCallback((Duration duration) {
assert(_debugMouseTrackerUpdateScheduled); assert(_debugMouseTrackerUpdateScheduled);
assert(() { assert(() {
_debugMouseTrackerUpdateScheduled = false; _debugMouseTrackerUpdateScheduled = false;
@ -501,19 +505,19 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Gesture
child.markNeedsPaint(); child.markNeedsPaint();
child.visitChildren(visitor); child.visitChildren(visitor);
}; };
instance?.renderView.visitChildren(visitor); instance.renderView.visitChildren(visitor);
return endOfFrame; return endOfFrame;
} }
} }
/// Prints a textual representation of the entire render tree. /// Prints a textual representation of the entire render tree.
void debugDumpRenderTree() { void debugDumpRenderTree() {
debugPrint(RendererBinding.instance?.renderView.toStringDeep() ?? 'Render tree unavailable.'); debugPrint(RendererBinding.instance.renderView.toStringDeep());
} }
/// Prints a textual representation of the entire layer tree. /// Prints a textual representation of the entire layer tree.
void debugDumpLayerTree() { void debugDumpLayerTree() {
debugPrint(RendererBinding.instance?.renderView.debugLayer?.toStringDeep() ?? 'Layer tree unavailable.'); debugPrint(RendererBinding.instance.renderView.debugLayer?.toStringDeep());
} }
/// Prints a textual representation of the entire semantics tree. /// Prints a textual representation of the entire semantics tree.
@ -523,16 +527,27 @@ void debugDumpLayerTree() {
/// The order in which the children of a [SemanticsNode] will be printed is /// The order in which the children of a [SemanticsNode] will be printed is
/// controlled by the [childOrder] parameter. /// controlled by the [childOrder] parameter.
void debugDumpSemanticsTree(DebugSemanticsDumpOrder childOrder) { void debugDumpSemanticsTree(DebugSemanticsDumpOrder childOrder) {
debugPrint(RendererBinding.instance?.renderView.debugSemantics?.toStringDeep(childOrder: childOrder) ?? 'Semantics not collected.'); debugPrint(RendererBinding.instance.renderView.debugSemantics?.toStringDeep(childOrder: childOrder) ?? 'Semantics not collected.');
} }
/// A concrete binding for applications that use the Rendering framework /// A concrete binding for applications that use the Rendering framework
/// directly. This is the glue that binds the framework to the Flutter engine. /// directly. This is the glue that binds the framework to the Flutter engine.
/// ///
/// When using the rendering framework directly, this binding, or one that
/// implements the same interfaces, must be used. The following
/// mixins are used to implement this binding:
///
/// * [GestureBinding], which implements the basics of hit testing.
/// * [SchedulerBinding], which introduces the concepts of frames.
/// * [ServicesBinding], which provides access to the plugin subsystem.
/// * [SemanticsBinding], which supports accessibility.
/// * [PaintingBinding], which enables decoding images.
/// * [RendererBinding], which handles the render tree.
///
/// You would only use this binding if you are writing to the /// You would only use this binding if you are writing to the
/// rendering layer directly. If you are writing to a higher-level /// rendering layer directly. If you are writing to a higher-level
/// library, such as the Flutter Widgets library, then you would use /// library, such as the Flutter Widgets library, then you would use
/// that layer's binding. /// that layer's binding (see [WidgetsFlutterBinding]).
class RenderingFlutterBinding extends BindingBase with GestureBinding, SchedulerBinding, ServicesBinding, SemanticsBinding, PaintingBinding, RendererBinding { class RenderingFlutterBinding extends BindingBase with GestureBinding, SchedulerBinding, ServicesBinding, SemanticsBinding, PaintingBinding, RendererBinding {
/// Creates a binding for the rendering layer. /// Creates a binding for the rendering layer.
/// ///
@ -545,4 +560,18 @@ class RenderingFlutterBinding extends BindingBase with GestureBinding, Scheduler
assert(renderView != null); assert(renderView != null);
renderView.child = root; renderView.child = root;
} }
/// Returns an instance of the binding that implements
/// [RendererBinding]. If no binding has yet been initialized, the
/// [RenderingFlutterBinding] class is used to create and initialize
/// one.
///
/// You need to call this method before using the rendering framework
/// if you are using it directly. If you are using the widgets framework,
/// see [WidgetsFlutterBinding.ensureInitialized].
static RendererBinding ensureInitialized() {
if (RendererBinding._instance == null)
RenderingFlutterBinding();
return RendererBinding.instance;
}
} }

View File

@ -3498,12 +3498,12 @@ mixin RelayoutWhenSystemFontsChangeMixin on RenderObject {
@override @override
void attach(covariant PipelineOwner owner) { void attach(covariant PipelineOwner owner) {
super.attach(owner); super.attach(owner);
PaintingBinding.instance!.systemFonts.addListener(systemFontsDidChange); PaintingBinding.instance.systemFonts.addListener(systemFontsDidChange);
} }
@override @override
void detach() { void detach() {
PaintingBinding.instance!.systemFonts.removeListener(systemFontsDidChange); PaintingBinding.instance.systemFonts.removeListener(systemFontsDidChange);
super.detach(); super.detach();
} }
} }

View File

@ -408,12 +408,12 @@ class RenderUiKitView extends RenderBox {
@override @override
void attach(PipelineOwner owner) { void attach(PipelineOwner owner) {
super.attach(owner); super.attach(owner);
GestureBinding.instance!.pointerRouter.addGlobalRoute(_handleGlobalPointerEvent); GestureBinding.instance.pointerRouter.addGlobalRoute(_handleGlobalPointerEvent);
} }
@override @override
void detach() { void detach() {
GestureBinding.instance!.pointerRouter.removeGlobalRoute(_handleGlobalPointerEvent); GestureBinding.instance.pointerRouter.removeGlobalRoute(_handleGlobalPointerEvent);
_gestureRecognizer!.reset(); _gestureRecognizer!.reset();
super.detach(); super.detach();
} }

View File

@ -18,16 +18,18 @@ export 'dart:ui' show AppLifecycleState, VoidCallback, FrameTiming;
/// Slows down animations by this factor to help in development. /// Slows down animations by this factor to help in development.
double get timeDilation => _timeDilation; double get timeDilation => _timeDilation;
double _timeDilation = 1.0; double _timeDilation = 1.0;
/// Setting the time dilation automatically calls [SchedulerBinding.resetEpoch] /// If the [SchedulerBinding] has been initialized, setting the time dilation
/// to ensure that time stamps seen by consumers of the scheduler binding are /// automatically calls [SchedulerBinding.resetEpoch] to ensure that time stamps
/// always increasing. /// seen by consumers of the scheduler binding are always increasing.
///
/// It is safe to set this before initializing the binding.
set timeDilation(double value) { set timeDilation(double value) {
assert(value > 0.0); assert(value > 0.0);
if (_timeDilation == value) if (_timeDilation == value)
return; return;
// We need to resetEpoch first so that we capture start of the epoch with the // If the binding has been created, we need to resetEpoch first so that we
// current time dilation. // capture start of the epoch with the current time dilation.
SchedulerBinding.instance?.resetEpoch(); SchedulerBinding._instance?.resetEpoch();
_timeDilation = value; _timeDilation = value;
} }
@ -208,6 +210,14 @@ mixin SchedulerBinding on BindingBase {
} }
} }
/// The current [SchedulerBinding], if one has been created.
///
/// Provides access to the features exposed by this mixin. The binding must
/// be initialized before using this getter; this is typically done by calling
/// [runApp] or [WidgetsFlutterBinding.ensureInitialized].
static SchedulerBinding get instance => BindingBase.checkInstance(_instance);
static SchedulerBinding? _instance;
final List<TimingsCallback> _timingsCallbacks = <TimingsCallback>[]; final List<TimingsCallback> _timingsCallbacks = <TimingsCallback>[];
/// Add a [TimingsCallback] that receives [FrameTiming] sent from /// Add a [TimingsCallback] that receives [FrameTiming] sent from
@ -307,10 +317,6 @@ mixin SchedulerBinding on BindingBase {
} }
} }
/// The current [SchedulerBinding], if one has been created.
static SchedulerBinding? get instance => _instance;
static SchedulerBinding? _instance;
@override @override
void initServiceExtensions() { void initServiceExtensions() {
super.initServiceExtensions(); super.initServiceExtensions();

View File

@ -113,9 +113,9 @@ class Ticker {
return false; return false;
if (muted) if (muted)
return false; return false;
if (SchedulerBinding.instance!.framesEnabled) if (SchedulerBinding.instance.framesEnabled)
return true; return true;
if (SchedulerBinding.instance!.schedulerPhase != SchedulerPhase.idle) if (SchedulerBinding.instance.schedulerPhase != SchedulerPhase.idle)
return true; // for example, we might be in a warm-up frame or forced frame return true; // for example, we might be in a warm-up frame or forced frame
return false; return false;
} }
@ -161,9 +161,9 @@ class Ticker {
if (shouldScheduleTick) { if (shouldScheduleTick) {
scheduleTick(); scheduleTick();
} }
if (SchedulerBinding.instance!.schedulerPhase.index > SchedulerPhase.idle.index && if (SchedulerBinding.instance.schedulerPhase.index > SchedulerPhase.idle.index &&
SchedulerBinding.instance!.schedulerPhase.index < SchedulerPhase.postFrameCallbacks.index) SchedulerBinding.instance.schedulerPhase.index < SchedulerPhase.postFrameCallbacks.index)
_startTime = SchedulerBinding.instance!.currentFrameTimeStamp; _startTime = SchedulerBinding.instance.currentFrameTimeStamp;
return _future!; return _future!;
} }
@ -250,7 +250,7 @@ class Ticker {
void scheduleTick({ bool rescheduling = false }) { void scheduleTick({ bool rescheduling = false }) {
assert(!scheduled); assert(!scheduled);
assert(shouldScheduleTick); assert(shouldScheduleTick);
_animationId = SchedulerBinding.instance!.scheduleFrameCallback(_tick, rescheduling: rescheduling); _animationId = SchedulerBinding.instance.scheduleFrameCallback(_tick, rescheduling: rescheduling);
} }
/// Cancels the frame callback that was requested by [scheduleTick], if any. /// Cancels the frame callback that was requested by [scheduleTick], if any.
@ -262,7 +262,7 @@ class Ticker {
@protected @protected
void unscheduleTick() { void unscheduleTick() {
if (scheduled) { if (scheduled) {
SchedulerBinding.instance!.cancelFrameCallbackWithId(_animationId!); SchedulerBinding.instance.cancelFrameCallbackWithId(_animationId!);
_animationId = null; _animationId = null;
} }
assert(!shouldScheduleTick); assert(!shouldScheduleTick);

View File

@ -13,10 +13,6 @@ export 'dart:ui' show AccessibilityFeatures;
/// The glue between the semantics layer and the Flutter engine. /// The glue between the semantics layer and the Flutter engine.
// TODO(jonahwilliams): move the remaining semantic related bindings here. // TODO(jonahwilliams): move the remaining semantic related bindings here.
mixin SemanticsBinding on BindingBase { mixin SemanticsBinding on BindingBase {
/// The current [SemanticsBinding], if one has been created.
static SemanticsBinding? get instance => _instance;
static SemanticsBinding? _instance;
@override @override
void initInstances() { void initInstances() {
super.initInstances(); super.initInstances();
@ -24,6 +20,14 @@ mixin SemanticsBinding on BindingBase {
_accessibilityFeatures = window.accessibilityFeatures; _accessibilityFeatures = window.accessibilityFeatures;
} }
/// The current [SemanticsBinding], if one has been created.
///
/// Provides access to the features exposed by this mixin. The binding must
/// be initialized before using this getter; this is typically done by calling
/// [runApp] or [WidgetsFlutterBinding.ensureInitialized].
static SemanticsBinding get instance => BindingBase.checkInstance(_instance);
static SemanticsBinding? _instance;
/// Called when the platform accessibility features change. /// Called when the platform accessibility features change.
/// ///
/// See [dart:ui.PlatformDispatcher.onAccessibilityFeaturesChanged]. /// See [dart:ui.PlatformDispatcher.onAccessibilityFeaturesChanged].

View File

@ -2938,7 +2938,7 @@ class SemanticsOwner extends ChangeNotifier {
} }
} }
visitedNodes.sort((SemanticsNode a, SemanticsNode b) => a.depth - b.depth); visitedNodes.sort((SemanticsNode a, SemanticsNode b) => a.depth - b.depth);
final ui.SemanticsUpdateBuilder builder = SemanticsBinding.instance!.createSemanticsUpdateBuilder(); final ui.SemanticsUpdateBuilder builder = SemanticsBinding.instance.createSemanticsUpdateBuilder();
for (final SemanticsNode node in visitedNodes) { for (final SemanticsNode node in visitedNodes) {
assert(node.parent?._dirty != true); // could be null (no parent) or false (not dirty) assert(node.parent?._dirty != true); // could be null (no parent) or false (not dirty)
// The _serialize() method marks the node as not dirty, and // The _serialize() method marks the node as not dirty, and
@ -2959,7 +2959,7 @@ class SemanticsOwner extends ChangeNotifier {
final CustomSemanticsAction action = CustomSemanticsAction.getAction(actionId)!; final CustomSemanticsAction action = CustomSemanticsAction.getAction(actionId)!;
builder.updateCustomAction(id: actionId, label: action.label, hint: action.hint, overrideId: action.action?.index ?? -1); builder.updateCustomAction(id: actionId, label: action.label, hint: action.hint, overrideId: action.action?.index ?? -1);
} }
SemanticsBinding.instance!.window.updateSemantics(builder.build()); SemanticsBinding.instance.window.updateSemantics(builder.build());
notifyListeners(); notifyListeners();
} }

View File

@ -219,7 +219,7 @@ class PlatformAssetBundle extends CachingAssetBundle {
Future<ByteData> load(String key) async { Future<ByteData> load(String key) async {
final Uint8List encoded = utf8.encoder.convert(Uri(path: Uri.encodeFull(key)).path); final Uint8List encoded = utf8.encoder.convert(Uri(path: Uri.encodeFull(key)).path);
final ByteData? asset = final ByteData? asset =
await ServicesBinding.instance!.defaultBinaryMessenger.send('flutter/assets', encoded.buffer.asByteData()); await ServicesBinding.instance.defaultBinaryMessenger.send('flutter/assets', encoded.buffer.asByteData());
if (asset == null) if (asset == null)
throw FlutterError('Unable to load asset: $key'); throw FlutterError('Unable to load asset: $key');
return asset; return asset;

View File

@ -2,7 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
@ -39,7 +38,11 @@ mixin ServicesBinding on BindingBase, SchedulerBinding {
} }
/// The current [ServicesBinding], if one has been created. /// The current [ServicesBinding], if one has been created.
static ServicesBinding? get instance => _instance; ///
/// Provides access to the features exposed by this mixin. The binding must
/// be initialized before using this getter; this is typically done by calling
/// [runApp] or [WidgetsFlutterBinding.ensureInitialized].
static ServicesBinding get instance => BindingBase.checkInstance(_instance);
static ServicesBinding? _instance; static ServicesBinding? _instance;
/// The default instance of [BinaryMessenger]. /// The default instance of [BinaryMessenger].

View File

@ -45,7 +45,7 @@ class BasicMessageChannel<T> {
final MessageCodec<T> codec; final MessageCodec<T> codec;
/// The messenger which sends the bytes for this channel, not null. /// The messenger which sends the bytes for this channel, not null.
BinaryMessenger get binaryMessenger => _binaryMessenger ?? ServicesBinding.instance!.defaultBinaryMessenger; BinaryMessenger get binaryMessenger => _binaryMessenger ?? ServicesBinding.instance.defaultBinaryMessenger;
final BinaryMessenger? _binaryMessenger; final BinaryMessenger? _binaryMessenger;
/// Sends the specified [message] to the platform plugins on this channel. /// Sends the specified [message] to the platform plugins on this channel.
@ -118,7 +118,7 @@ class MethodChannel {
/// The messenger used by this channel to send platform messages. /// The messenger used by this channel to send platform messages.
/// ///
/// The messenger may not be null. /// The messenger may not be null.
BinaryMessenger get binaryMessenger => _binaryMessenger ?? ServicesBinding.instance!.defaultBinaryMessenger; BinaryMessenger get binaryMessenger => _binaryMessenger ?? ServicesBinding.instance.defaultBinaryMessenger;
final BinaryMessenger? _binaryMessenger; final BinaryMessenger? _binaryMessenger;
/// Backend implementation of [invokeMethod]. /// Backend implementation of [invokeMethod].
@ -450,7 +450,7 @@ class EventChannel {
final MethodCodec codec; final MethodCodec codec;
/// The messenger used by this channel to send platform messages, not null. /// The messenger used by this channel to send platform messages, not null.
BinaryMessenger get binaryMessenger => _binaryMessenger ?? ServicesBinding.instance!.defaultBinaryMessenger; BinaryMessenger get binaryMessenger => _binaryMessenger ?? ServicesBinding.instance.defaultBinaryMessenger;
final BinaryMessenger? _binaryMessenger; final BinaryMessenger? _binaryMessenger;
/// Sets up a broadcast stream for receiving events on this channel. /// Sets up a broadcast stream for receiving events on this channel.

View File

@ -264,7 +264,7 @@ class RestorationManager extends ChangeNotifier {
_isReplacing = _rootBucketIsValid && enabled; _isReplacing = _rootBucketIsValid && enabled;
if (_isReplacing) { if (_isReplacing) {
SchedulerBinding.instance!.addPostFrameCallback((Duration _) { SchedulerBinding.instance.addPostFrameCallback((Duration _) {
_isReplacing = false; _isReplacing = false;
}); });
} }
@ -350,7 +350,7 @@ class RestorationManager extends ChangeNotifier {
_bucketsNeedingSerialization.add(bucket); _bucketsNeedingSerialization.add(bucket);
if (!_serializationScheduled) { if (!_serializationScheduled) {
_serializationScheduled = true; _serializationScheduled = true;
SchedulerBinding.instance!.addPostFrameCallback((Duration _) => _doSerialization()); SchedulerBinding.instance.addPostFrameCallback((Duration _) => _doSerialization());
} }
} }
@ -413,7 +413,7 @@ class RestorationManager extends ChangeNotifier {
/// current restoration data is directly sent to the engine. /// current restoration data is directly sent to the engine.
void flushData() { void flushData() {
assert(!_debugDoingUpdate); assert(!_debugDoingUpdate);
if (SchedulerBinding.instance!.hasScheduledFrame) { if (SchedulerBinding.instance.hasScheduledFrame) {
return; return;
} }
_doSerialization(); _doSerialization();

View File

@ -502,7 +502,7 @@ class SystemChrome {
/// [SystemUiMode.leanBack]. /// [SystemUiMode.leanBack].
/// ///
static Future<void> setSystemUIChangeCallback(SystemUiChangeCallback? callback) async { static Future<void> setSystemUIChangeCallback(SystemUiChangeCallback? callback) async {
ServicesBinding.instance!.setSystemUiChangeCallback(callback); ServicesBinding.instance.setSystemUiChangeCallback(callback);
// Skip setting up the listener if there is no callback. // Skip setting up the listener if there is no callback.
if (callback != null) { if (callback != null) {
await SystemChannels.platform.invokeMethod<void>( await SystemChannels.platform.invokeMethod<void>(

View File

@ -1324,7 +1324,7 @@ class _FocusableActionDetectorState extends State<FocusableActionDetector> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
SchedulerBinding.instance!.addPostFrameCallback((Duration duration) { SchedulerBinding.instance.addPostFrameCallback((Duration duration) {
_updateHighlightMode(FocusManager.instance.highlightMode); _updateHighlightMode(FocusManager.instance.highlightMode);
}); });
FocusManager.instance.addHighlightModeListener(_handleFocusHighlightModeChange); FocusManager.instance.addHighlightModeListener(_handleFocusHighlightModeChange);
@ -1413,7 +1413,7 @@ class _FocusableActionDetectorState extends State<FocusableActionDetector> {
return _focused && _canShowHighlight && canRequestFocus(target); return _focused && _canShowHighlight && canRequestFocus(target);
} }
assert(SchedulerBinding.instance!.schedulerPhase != SchedulerPhase.persistentCallbacks); assert(SchedulerBinding.instance.schedulerPhase != SchedulerPhase.persistentCallbacks);
final FocusableActionDetector oldTarget = oldWidget ?? widget; final FocusableActionDetector oldTarget = oldWidget ?? widget;
final bool didShowHoverHighlight = shouldShowHoverHighlight(oldTarget); final bool didShowHoverHighlight = shouldShowHoverHighlight(oldTarget);
final bool didShowFocusHighlight = shouldShowFocusHighlight(oldTarget); final bool didShowFocusHighlight = shouldShowFocusHighlight(oldTarget);
@ -1434,7 +1434,7 @@ class _FocusableActionDetectorState extends State<FocusableActionDetector> {
void didUpdateWidget(FocusableActionDetector oldWidget) { void didUpdateWidget(FocusableActionDetector oldWidget) {
super.didUpdateWidget(oldWidget); super.didUpdateWidget(oldWidget);
if (widget.enabled != oldWidget.enabled) { if (widget.enabled != oldWidget.enabled) {
SchedulerBinding.instance!.addPostFrameCallback((Duration duration) { SchedulerBinding.instance.addPostFrameCallback((Duration duration) {
_mayTriggerCallback(oldWidget: oldWidget); _mayTriggerCallback(oldWidget: oldWidget);
}); });
} }

View File

@ -1257,16 +1257,16 @@ class _WidgetsAppState extends State<WidgetsApp> with WidgetsBindingObserver {
// If window.defaultRouteName isn't '/', we should assume it was set // If window.defaultRouteName isn't '/', we should assume it was set
// intentionally via `setInitialRoute`, and should override whatever is in // intentionally via `setInitialRoute`, and should override whatever is in
// [widget.initialRoute]. // [widget.initialRoute].
String get _initialRouteName => WidgetsBinding.instance!.window.defaultRouteName != Navigator.defaultRouteName String get _initialRouteName => WidgetsBinding.instance.window.defaultRouteName != Navigator.defaultRouteName
? WidgetsBinding.instance!.window.defaultRouteName ? WidgetsBinding.instance.window.defaultRouteName
: widget.initialRoute ?? WidgetsBinding.instance!.window.defaultRouteName; : widget.initialRoute ?? WidgetsBinding.instance.window.defaultRouteName;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_updateRouting(); _updateRouting();
_locale = _resolveLocales(WidgetsBinding.instance!.window.locales, widget.supportedLocales); _locale = _resolveLocales(WidgetsBinding.instance.window.locales, widget.supportedLocales);
WidgetsBinding.instance!.addObserver(this); WidgetsBinding.instance.addObserver(this);
} }
@override @override
@ -1277,7 +1277,7 @@ class _WidgetsAppState extends State<WidgetsApp> with WidgetsBindingObserver {
@override @override
void dispose() { void dispose() {
WidgetsBinding.instance!.removeObserver(this); WidgetsBinding.instance.removeObserver(this);
_defaultRouteInformationProvider?.dispose(); _defaultRouteInformationProvider?.dispose();
super.dispose(); super.dispose();
} }
@ -1681,7 +1681,7 @@ class _MediaQueryFromWindowsState extends State<_MediaQueryFromWindow> with Widg
@override @override
void initState() { void initState() {
super.initState(); super.initState();
WidgetsBinding.instance!.addObserver(this); WidgetsBinding.instance.addObserver(this);
} }
// ACCESSIBILITY // ACCESSIBILITY
@ -1725,7 +1725,7 @@ class _MediaQueryFromWindowsState extends State<_MediaQueryFromWindow> with Widg
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
MediaQueryData data = MediaQueryData.fromWindow(WidgetsBinding.instance!.window); MediaQueryData data = MediaQueryData.fromWindow(WidgetsBinding.instance.window);
if (!kReleaseMode) { if (!kReleaseMode) {
data = data.copyWith(platformBrightness: debugBrightnessOverride); data = data.copyWith(platformBrightness: debugBrightnessOverride);
} }
@ -1737,7 +1737,7 @@ class _MediaQueryFromWindowsState extends State<_MediaQueryFromWindow> with Widg
@override @override
void dispose() { void dispose() {
WidgetsBinding.instance!.removeObserver(this); WidgetsBinding.instance.removeObserver(this);
super.dispose(); super.dispose();
} }
} }

View File

@ -893,7 +893,7 @@ class _RawAutocompleteState<T extends Object> extends State<RawAutocomplete<T>>
AutocompletePreviousOptionIntent: _previousOptionAction, AutocompletePreviousOptionIntent: _previousOptionAction,
AutocompleteNextOptionIntent: _nextOptionAction, AutocompleteNextOptionIntent: _nextOptionAction,
}; };
SchedulerBinding.instance!.addPostFrameCallback((Duration _) { SchedulerBinding.instance.addPostFrameCallback((Duration _) {
_updateOverlay(); _updateOverlay();
}); });
} }
@ -906,7 +906,7 @@ class _RawAutocompleteState<T extends Object> extends State<RawAutocomplete<T>>
widget.textEditingController, widget.textEditingController,
); );
_updateFocusNode(oldWidget.focusNode, widget.focusNode); _updateFocusNode(oldWidget.focusNode, widget.focusNode);
SchedulerBinding.instance!.addPostFrameCallback((Duration _) { SchedulerBinding.instance.addPostFrameCallback((Duration _) {
_updateOverlay(); _updateOverlay();
}); });
} }

View File

@ -89,7 +89,7 @@ class _AutomaticKeepAliveState extends State<AutomaticKeepAlive> {
// If the child doesn't exist yet, we got called during the very first // If the child doesn't exist yet, we got called during the very first
// build of this subtree. Wait until the end of the frame to update // build of this subtree. Wait until the end of the frame to update
// the child when the child is guaranteed to be present. // the child when the child is guaranteed to be present.
SchedulerBinding.instance!.addPostFrameCallback((Duration timeStamp) { SchedulerBinding.instance.addPostFrameCallback((Duration timeStamp) {
if (!mounted) { if (!mounted) {
return; return;
} }
@ -155,7 +155,7 @@ class _AutomaticKeepAliveState extends State<AutomaticKeepAlive> {
}()); }());
_handles!.remove(handle); _handles!.remove(handle);
if (_handles!.isEmpty) { if (_handles!.isEmpty) {
if (SchedulerBinding.instance!.schedulerPhase.index < SchedulerPhase.persistentCallbacks.index) { if (SchedulerBinding.instance.schedulerPhase.index < SchedulerPhase.persistentCallbacks.index) {
// Build/layout haven't started yet so let's just schedule this for // Build/layout haven't started yet so let's just schedule this for
// the next frame. // the next frame.
setState(() { _keepingAlive = false; }); setState(() { _keepingAlive = false; });

View File

@ -69,7 +69,7 @@ class BannerPainter extends CustomPainter {
assert(location != null), assert(location != null),
assert(color != null), assert(color != null),
assert(textStyle != null), assert(textStyle != null),
super(repaint: PaintingBinding.instance!.systemFonts); super(repaint: PaintingBinding.instance.systemFonts);
/// The message to show in the banner. /// The message to show in the banner.
final String message; final String message;

View File

@ -50,12 +50,12 @@ export 'dart:ui' show AppLifecycleState, Locale;
/// @override /// @override
/// void initState() { /// void initState() {
/// super.initState(); /// super.initState();
/// WidgetsBinding.instance!.addObserver(this); /// WidgetsBinding.instance.addObserver(this);
/// } /// }
/// ///
/// @override /// @override
/// void dispose() { /// void dispose() {
/// WidgetsBinding.instance!.removeObserver(this); /// WidgetsBinding.instance.removeObserver(this);
/// super.dispose(); /// super.dispose();
/// } /// }
/// ///
@ -147,19 +147,19 @@ abstract class WidgetsBindingObserver {
/// @override /// @override
/// void initState() { /// void initState() {
/// super.initState(); /// super.initState();
/// _lastSize = WidgetsBinding.instance!.window.physicalSize; /// _lastSize = WidgetsBinding.instance.window.physicalSize;
/// WidgetsBinding.instance!.addObserver(this); /// WidgetsBinding.instance.addObserver(this);
/// } /// }
/// ///
/// @override /// @override
/// void dispose() { /// void dispose() {
/// WidgetsBinding.instance!.removeObserver(this); /// WidgetsBinding.instance.removeObserver(this);
/// super.dispose(); /// super.dispose();
/// } /// }
/// ///
/// @override /// @override
/// void didChangeMetrics() { /// void didChangeMetrics() {
/// setState(() { _lastSize = WidgetsBinding.instance!.window.physicalSize; }); /// setState(() { _lastSize = WidgetsBinding.instance.window.physicalSize; });
/// } /// }
/// ///
/// @override /// @override
@ -203,12 +203,12 @@ abstract class WidgetsBindingObserver {
/// @override /// @override
/// void initState() { /// void initState() {
/// super.initState(); /// super.initState();
/// WidgetsBinding.instance!.addObserver(this); /// WidgetsBinding.instance.addObserver(this);
/// } /// }
/// ///
/// @override /// @override
/// void dispose() { /// void dispose() {
/// WidgetsBinding.instance!.removeObserver(this); /// WidgetsBinding.instance.removeObserver(this);
/// super.dispose(); /// super.dispose();
/// } /// }
/// ///
@ -216,7 +216,7 @@ abstract class WidgetsBindingObserver {
/// ///
/// @override /// @override
/// void didChangeTextScaleFactor() { /// void didChangeTextScaleFactor() {
/// setState(() { _lastTextScaleFactor = WidgetsBinding.instance!.window.textScaleFactor; }); /// setState(() { _lastTextScaleFactor = WidgetsBinding.instance.window.textScaleFactor; });
/// } /// }
/// ///
/// @override /// @override
@ -296,6 +296,14 @@ mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureB
}()); }());
} }
/// The current [WidgetsBinding], if one has been created.
///
/// Provides access to the features exposed by this mixin. The binding must
/// be initialized before using this getter; this is typically done by calling
/// [runApp] or [WidgetsFlutterBinding.ensureInitialized].
static WidgetsBinding get instance => BindingBase.checkInstance(_instance);
static WidgetsBinding? _instance;
void _debugAddStackFilters() { void _debugAddStackFilters() {
const PartialStackFrame elementInflateWidget = PartialStackFrame(package: 'package:flutter/src/widgets/framework.dart', className: 'Element', method: 'inflateWidget'); const PartialStackFrame elementInflateWidget = PartialStackFrame(package: 'package:flutter/src/widgets/framework.dart', className: 'Element', method: 'inflateWidget');
const PartialStackFrame elementUpdateChild = PartialStackFrame(package: 'package:flutter/src/widgets/framework.dart', className: 'Element', method: 'updateChild'); const PartialStackFrame elementUpdateChild = PartialStackFrame(package: 'package:flutter/src/widgets/framework.dart', className: 'Element', method: 'updateChild');
@ -377,14 +385,6 @@ mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureB
)); ));
} }
/// The current [WidgetsBinding], if one has been created.
///
/// If you need the binding to be constructed before calling [runApp],
/// you can ensure a Widget binding has been constructed by calling the
/// `WidgetsFlutterBinding.ensureInitialized()` function.
static WidgetsBinding? get instance => _instance;
static WidgetsBinding? _instance;
@override @override
void initServiceExtensions() { void initServiceExtensions() {
super.initServiceExtensions(); super.initServiceExtensions();
@ -862,14 +862,14 @@ mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureB
developer.Timeline.instantSync('Rasterized first useful frame'); developer.Timeline.instantSync('Rasterized first useful frame');
developer.postEvent('Flutter.FirstFrame', <String, dynamic>{}); developer.postEvent('Flutter.FirstFrame', <String, dynamic>{});
} }
SchedulerBinding.instance!.removeTimingsCallback(firstFrameCallback!); SchedulerBinding.instance.removeTimingsCallback(firstFrameCallback!);
firstFrameCallback = null; firstFrameCallback = null;
_firstFrameCompleter.complete(); _firstFrameCompleter.complete();
}; };
// Callback is only invoked when FlutterView.render is called. When // Callback is only invoked when FlutterView.render is called. When
// sendFramesToEngine is set to false during the frame, it will not be // sendFramesToEngine is set to false during the frame, it will not be
// called and we need to remove the callback (see below). // called and we need to remove the callback (see below).
SchedulerBinding.instance!.addTimingsCallback(firstFrameCallback!); SchedulerBinding.instance.addTimingsCallback(firstFrameCallback!);
} }
try { try {
@ -893,7 +893,7 @@ mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureB
// This frame is deferred and not the first frame sent to the engine that // This frame is deferred and not the first frame sent to the engine that
// should be reported. // should be reported.
_needToReportFirstFrame = true; _needToReportFirstFrame = true;
SchedulerBinding.instance!.removeTimingsCallback(firstFrameCallback!); SchedulerBinding.instance.removeTimingsCallback(firstFrameCallback!);
} }
} }
@ -938,7 +938,7 @@ mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureB
child: rootWidget, child: rootWidget,
).attachToRenderTree(buildOwner!, renderViewElement as RenderObjectToWidgetElement<RenderBox>?); ).attachToRenderTree(buildOwner!, renderViewElement as RenderObjectToWidgetElement<RenderBox>?);
if (isBootstrapFrame) { if (isBootstrapFrame) {
SchedulerBinding.instance!.ensureVisualUpdate(); SchedulerBinding.instance.ensureVisualUpdate();
} }
} }
@ -1033,12 +1033,11 @@ void runApp(Widget app) {
} }
String _debugDumpAppString() { String _debugDumpAppString() {
assert(WidgetsBinding.instance != null); const String mode = kDebugMode ? 'DEBUG MODE' : kReleaseMode ? 'RELEASE MODE' : 'PROFILE MODE';
const String mode = kDebugMode ? 'DEBUG MODE' : 'PROFILE MODE';
final StringBuffer buffer = StringBuffer(); final StringBuffer buffer = StringBuffer();
buffer.writeln('${WidgetsBinding.instance.runtimeType} - $mode'); buffer.writeln('${WidgetsBinding.instance.runtimeType} - $mode');
if (WidgetsBinding.instance!.renderViewElement != null) { if (WidgetsBinding.instance.renderViewElement != null) {
buffer.writeln(WidgetsBinding.instance!.renderViewElement!.toStringDeep()); buffer.writeln(WidgetsBinding.instance.renderViewElement!.toStringDeep());
} else { } else {
buffer.writeln('<no tree currently mounted>'); buffer.writeln('<no tree currently mounted>');
} }
@ -1047,8 +1046,7 @@ String _debugDumpAppString() {
/// Print a string representation of the currently running app. /// Print a string representation of the currently running app.
void debugDumpApp() { void debugDumpApp() {
final String value = _debugDumpAppString(); debugPrint(_debugDumpAppString());
debugPrint(value);
} }
/// A bridge from a [RenderObject] to an [Element] tree. /// A bridge from a [RenderObject] to an [Element] tree.
@ -1229,22 +1227,34 @@ class RenderObjectToWidgetElement<T extends RenderObject> extends RootRenderObje
/// A concrete binding for applications based on the Widgets framework. /// A concrete binding for applications based on the Widgets framework.
/// ///
/// This is the glue that binds the framework to the Flutter engine. /// This is the glue that binds the framework to the Flutter engine.
///
/// When using the widgets framework, this binding, or one that
/// implements the same interfaces, must be used. The following
/// mixins are used to implement this binding:
///
/// * [GestureBinding], which implements the basics of hit testing.
/// * [SchedulerBinding], which introduces the concepts of frames.
/// * [ServicesBinding], which provides access to the plugin subsystem.
/// * [PaintingBinding], which enables decoding images.
/// * [SemanticsBinding], which supports accessibility.
/// * [RendererBinding], which handles the render tree.
/// * [WidgetsBinding], which handles the widget tree.
class WidgetsFlutterBinding extends BindingBase with GestureBinding, SchedulerBinding, ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding { class WidgetsFlutterBinding extends BindingBase with GestureBinding, SchedulerBinding, ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {
/// Returns an instance of the binding that implements
/// Returns an instance of the [WidgetsBinding], creating and /// [WidgetsBinding]. If no binding has yet been initialized, the
/// initializing it if necessary. If one is created, it will be a /// [WidgetsFlutterBinding] class is used to create and initialize
/// [WidgetsFlutterBinding]. If one was previously initialized, then /// one.
/// it will at least implement [WidgetsBinding].
/// ///
/// You only need to call this method if you need the binding to be /// You only need to call this method if you need the binding to be
/// initialized before calling [runApp]. /// initialized before calling [runApp].
/// ///
/// In the `flutter_test` framework, [testWidgets] initializes the /// In the `flutter_test` framework, [testWidgets] initializes the
/// binding instance to a [TestWidgetsFlutterBinding], not a /// binding instance to a [TestWidgetsFlutterBinding], not a
/// [WidgetsFlutterBinding]. /// [WidgetsFlutterBinding]. See
/// [TestWidgetsFlutterBinding.ensureInitialized].
static WidgetsBinding ensureInitialized() { static WidgetsBinding ensureInitialized() {
if (WidgetsBinding.instance == null) if (WidgetsBinding._instance == null)
WidgetsFlutterBinding(); WidgetsFlutterBinding();
return WidgetsBinding.instance!; return WidgetsBinding.instance;
} }
} }

View File

@ -919,7 +919,7 @@ class _DragAvatar<T extends Object> extends Drag {
_lastOffset = globalPosition - dragStartPoint; _lastOffset = globalPosition - dragStartPoint;
_entry!.markNeedsBuild(); _entry!.markNeedsBuild();
final HitTestResult result = HitTestResult(); final HitTestResult result = HitTestResult();
WidgetsBinding.instance!.hitTest(result, globalPosition + feedbackOffset); WidgetsBinding.instance.hitTest(result, globalPosition + feedbackOffset);
final List<_DragTargetState<Object>> targets = _getDragTargets(result.path).toList(); final List<_DragTargetState<Object>> targets = _getDragTargets(result.path).toList();

View File

@ -1625,7 +1625,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
if (!_didAutoFocus && widget.autofocus) { if (!_didAutoFocus && widget.autofocus) {
_didAutoFocus = true; _didAutoFocus = true;
SchedulerBinding.instance!.addPostFrameCallback((_) { SchedulerBinding.instance.addPostFrameCallback((_) {
if (mounted) { if (mounted) {
FocusScope.of(context).autofocus(widget.focusNode); FocusScope.of(context).autofocus(widget.focusNode);
} }
@ -1700,7 +1700,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
_selectionOverlay = null; _selectionOverlay = null;
_focusAttachment!.detach(); _focusAttachment!.detach();
widget.focusNode.removeListener(_handleFocusChanged); widget.focusNode.removeListener(_handleFocusChanged);
WidgetsBinding.instance!.removeObserver(this); WidgetsBinding.instance.removeObserver(this);
_clipboardStatus?.removeListener(_onChangedClipboardStatus); _clipboardStatus?.removeListener(_onChangedClipboardStatus);
_clipboardStatus?.dispose(); _clipboardStatus?.dispose();
super.dispose(); super.dispose();
@ -2219,7 +2219,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
return; return;
} }
_showCaretOnScreenScheduled = true; _showCaretOnScreenScheduled = true;
SchedulerBinding.instance!.addPostFrameCallback((Duration _) { SchedulerBinding.instance.addPostFrameCallback((Duration _) {
_showCaretOnScreenScheduled = false; _showCaretOnScreenScheduled = false;
if (_currentCaretRect == null || !_scrollController!.hasClients) { if (_currentCaretRect == null || !_scrollController!.hasClients) {
return; return;
@ -2272,10 +2272,10 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
@override @override
void didChangeMetrics() { void didChangeMetrics() {
if (_lastBottomViewInset < WidgetsBinding.instance!.window.viewInsets.bottom) { if (_lastBottomViewInset < WidgetsBinding.instance.window.viewInsets.bottom) {
_scheduleShowCaretOnScreen(); _scheduleShowCaretOnScreen();
} }
_lastBottomViewInset = WidgetsBinding.instance!.window.viewInsets.bottom; _lastBottomViewInset = WidgetsBinding.instance.window.viewInsets.bottom;
} }
@pragma('vm:notify-debugger-on-exception') @pragma('vm:notify-debugger-on-exception')
@ -2432,8 +2432,8 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
_updateOrDisposeSelectionOverlayIfNeeded(); _updateOrDisposeSelectionOverlayIfNeeded();
if (_hasFocus) { if (_hasFocus) {
// Listen for changing viewInsets, which indicates keyboard showing up. // Listen for changing viewInsets, which indicates keyboard showing up.
WidgetsBinding.instance!.addObserver(this); WidgetsBinding.instance.addObserver(this);
_lastBottomViewInset = WidgetsBinding.instance!.window.viewInsets.bottom; _lastBottomViewInset = WidgetsBinding.instance.window.viewInsets.bottom;
if (!widget.readOnly) { if (!widget.readOnly) {
_scheduleShowCaretOnScreen(); _scheduleShowCaretOnScreen();
} }
@ -2442,7 +2442,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
_handleSelectionChanged(TextSelection.collapsed(offset: _value.text.length), null); _handleSelectionChanged(TextSelection.collapsed(offset: _value.text.length), null);
} }
} else { } else {
WidgetsBinding.instance!.removeObserver(this); WidgetsBinding.instance.removeObserver(this);
// Clear the selection and composition state if this widget lost focus. // Clear the selection and composition state if this widget lost focus.
_value = TextEditingValue(text: _value.text); _value = TextEditingValue(text: _value.text);
_currentPromptRectRange = null; _currentPromptRectRange = null;
@ -2455,8 +2455,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
final Size size = renderEditable.size; final Size size = renderEditable.size;
final Matrix4 transform = renderEditable.getTransformTo(null); final Matrix4 transform = renderEditable.getTransformTo(null);
_textInputConnection!.setEditableSizeAndTransform(size, transform); _textInputConnection!.setEditableSizeAndTransform(size, transform);
SchedulerBinding.instance! SchedulerBinding.instance.addPostFrameCallback((Duration _) => _updateSizeAndTransform());
.addPostFrameCallback((Duration _) => _updateSizeAndTransform());
} }
} }
@ -2478,8 +2477,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
} }
assert(composingRect != null); assert(composingRect != null);
_textInputConnection!.setComposingRect(composingRect); _textInputConnection!.setComposingRect(composingRect);
SchedulerBinding.instance! SchedulerBinding.instance.addPostFrameCallback((Duration _) => _updateComposingRectIfNeeded());
.addPostFrameCallback((Duration _) => _updateComposingRectIfNeeded());
} }
} }
@ -2491,8 +2489,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
final Rect caretRect = renderEditable.getLocalRectForCaret(currentTextPosition); final Rect caretRect = renderEditable.getLocalRectForCaret(currentTextPosition);
_textInputConnection!.setCaretRect(caretRect); _textInputConnection!.setCaretRect(caretRect);
} }
SchedulerBinding.instance! SchedulerBinding.instance.addPostFrameCallback((Duration _) => _updateCaretRectIfNeeded());
.addPostFrameCallback((Duration _) => _updateCaretRectIfNeeded());
} }
} }

View File

@ -1465,21 +1465,21 @@ class FocusManager with DiagnosticableTreeMixin, ChangeNotifier {
void registerGlobalHandlers() { void registerGlobalHandlers() {
assert(RawKeyboard.instance.keyEventHandler == null); assert(RawKeyboard.instance.keyEventHandler == null);
RawKeyboard.instance.keyEventHandler = _handleRawKeyEvent; RawKeyboard.instance.keyEventHandler = _handleRawKeyEvent;
GestureBinding.instance!.pointerRouter.addGlobalRoute(_handlePointerEvent); GestureBinding.instance.pointerRouter.addGlobalRoute(_handlePointerEvent);
} }
@override @override
void dispose() { void dispose() {
if (RawKeyboard.instance.keyEventHandler == _handleRawKeyEvent) { if (RawKeyboard.instance.keyEventHandler == _handleRawKeyEvent) {
RawKeyboard.instance.keyEventHandler = null; RawKeyboard.instance.keyEventHandler = null;
GestureBinding.instance!.pointerRouter.removeGlobalRoute(_handlePointerEvent); GestureBinding.instance.pointerRouter.removeGlobalRoute(_handlePointerEvent);
} }
super.dispose(); super.dispose();
} }
/// Provides convenient access to the current [FocusManager] singleton from /// Provides convenient access to the current [FocusManager] singleton from
/// the [WidgetsBinding] instance. /// the [WidgetsBinding] instance.
static FocusManager get instance => WidgetsBinding.instance!.focusManager; static FocusManager get instance => WidgetsBinding.instance.focusManager;
/// Sets the strategy by which [highlightMode] is determined. /// Sets the strategy by which [highlightMode] is determined.
/// ///
@ -1522,7 +1522,7 @@ class FocusManager with DiagnosticableTreeMixin, ChangeNotifier {
case TargetPlatform.android: case TargetPlatform.android:
case TargetPlatform.fuchsia: case TargetPlatform.fuchsia:
case TargetPlatform.iOS: case TargetPlatform.iOS:
if (WidgetsBinding.instance!.mouseTracker.mouseIsConnected) { if (WidgetsBinding.instance.mouseTracker.mouseIsConnected) {
return FocusHighlightMode.traditional; return FocusHighlightMode.traditional;
} }
return FocusHighlightMode.touch; return FocusHighlightMode.touch;
@ -1821,7 +1821,7 @@ class FocusManager with DiagnosticableTreeMixin, ChangeNotifier {
/// Provides convenient access to the current [FocusManager.primaryFocus] from the /// Provides convenient access to the current [FocusManager.primaryFocus] from the
/// [WidgetsBinding] instance. /// [WidgetsBinding] instance.
FocusNode? get primaryFocus => WidgetsBinding.instance!.focusManager.primaryFocus; FocusNode? get primaryFocus => WidgetsBinding.instance.focusManager.primaryFocus;
/// Returns a text representation of the current focus tree, along with the /// Returns a text representation of the current focus tree, along with the
/// current attributes on each node. /// current attributes on each node.

View File

@ -149,7 +149,7 @@ abstract class GlobalKey<T extends State<StatefulWidget>> extends Key {
/// constructor. /// constructor.
const GlobalKey.constructor() : super.empty(); const GlobalKey.constructor() : super.empty();
Element? get _currentElement => WidgetsBinding.instance!.buildOwner!._globalKeyRegistry[this]; Element? get _currentElement => WidgetsBinding.instance.buildOwner!._globalKeyRegistry[this];
/// The build context in which the widget with this key builds. /// The build context in which the widget with this key builds.
/// ///

View File

@ -930,7 +930,7 @@ class HeroController extends NavigatorObserver {
// going to end up, and the `to` route will go back onstage. // going to end up, and the `to` route will go back onstage.
to.offstage = to.animation!.value == 0.0; to.offstage = to.animation!.value == 0.0;
WidgetsBinding.instance!.addPostFrameCallback((Duration value) { WidgetsBinding.instance.addPostFrameCallback((Duration value) {
_startHeroTransition(from, to, animation, flightType, isUserGestureTransition); _startHeroTransition(from, to, animation, flightType, isUserGestureTransition);
}); });
} }

View File

@ -119,7 +119,7 @@ Future<void> precacheImage(
// Give callers until at least the end of the frame to subscribe to the // Give callers until at least the end of the frame to subscribe to the
// image stream. // image stream.
// See ImageCache._liveImages // See ImageCache._liveImages
SchedulerBinding.instance!.addPostFrameCallback((Duration timeStamp) { SchedulerBinding.instance.addPostFrameCallback((Duration timeStamp) {
stream.removeListener(listener!); stream.removeListener(listener!);
}); });
}, },
@ -1118,14 +1118,14 @@ class _ImageState extends State<Image> with WidgetsBindingObserver {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
WidgetsBinding.instance!.addObserver(this); WidgetsBinding.instance.addObserver(this);
_scrollAwareContext = DisposableBuildContext<State<Image>>(this); _scrollAwareContext = DisposableBuildContext<State<Image>>(this);
} }
@override @override
void dispose() { void dispose() {
assert(_imageStream != null); assert(_imageStream != null);
WidgetsBinding.instance!.removeObserver(this); WidgetsBinding.instance.removeObserver(this);
_stopListeningToStream(); _stopListeningToStream();
_completerHandle?.dispose(); _completerHandle?.dispose();
_scrollAwareContext.dispose(); _scrollAwareContext.dispose();
@ -1175,7 +1175,7 @@ class _ImageState extends State<Image> with WidgetsBindingObserver {
void _updateInvertColors() { void _updateInvertColors() {
_invertColors = MediaQuery.maybeOf(context)?.invertColors _invertColors = MediaQuery.maybeOf(context)?.invertColors
?? SemanticsBinding.instance!.accessibilityFeatures.invertColors; ?? SemanticsBinding.instance.accessibilityFeatures.invertColors;
} }
void _resolveImage() { void _resolveImage() {

View File

@ -759,7 +759,7 @@ class _ListWheelScrollViewState extends State<ListWheelScrollView> {
super.didUpdateWidget(oldWidget); super.didUpdateWidget(oldWidget);
if (widget.controller != null && widget.controller != scrollController) { if (widget.controller != null && widget.controller != scrollController) {
final ScrollController? oldScrollController = scrollController; final ScrollController? oldScrollController = scrollController;
SchedulerBinding.instance!.addPostFrameCallback((_) { SchedulerBinding.instance.addPostFrameCallback((_) {
oldScrollController!.dispose(); oldScrollController!.dispose();
}); });
scrollController = widget.controller; scrollController = widget.controller;

View File

@ -543,7 +543,7 @@ class _LocalizationsState extends State<Localizations> {
// have finished loading. Until then the old locale will continue to be used. // have finished loading. Until then the old locale will continue to be used.
// - If we're running at app startup time then defer reporting the first // - If we're running at app startup time then defer reporting the first
// "useful" frame until after the async load has completed. // "useful" frame until after the async load has completed.
RendererBinding.instance!.deferFirstFrame(); RendererBinding.instance.deferFirstFrame();
typeToResourcesFuture.then<void>((Map<Type, dynamic> value) { typeToResourcesFuture.then<void>((Map<Type, dynamic> value) {
if (mounted) { if (mounted) {
setState(() { setState(() {
@ -551,7 +551,7 @@ class _LocalizationsState extends State<Localizations> {
_locale = locale; _locale = locale;
}); });
} }
RendererBinding.instance!.allowFirstFrame(); RendererBinding.instance.allowFirstFrame();
}); });
} }
} }

View File

@ -3513,7 +3513,7 @@ class NavigatorState extends State<Navigator> with TickerProviderStateMixin, Res
// controller at the end of the build. // controller at the end of the build.
if (newHeroController.navigator != null) { if (newHeroController.navigator != null) {
final NavigatorState previousOwner = newHeroController.navigator!; final NavigatorState previousOwner = newHeroController.navigator!;
ServicesBinding.instance!.addPostFrameCallback((Duration timestamp) { ServicesBinding.instance.addPostFrameCallback((Duration timestamp) {
// We only check if this navigator still owns the hero controller. // We only check if this navigator still owns the hero controller.
if (_heroControllerFromScope == newHeroController) { if (_heroControllerFromScope == newHeroController) {
final bool hasHeroControllerOwnerShip = _heroControllerFromScope!._navigator == this; final bool hasHeroControllerOwnerShip = _heroControllerFromScope!._navigator == this;
@ -5319,7 +5319,7 @@ class NavigatorState extends State<Navigator> with TickerProviderStateMixin, Res
void _cancelActivePointers() { void _cancelActivePointers() {
// TODO(abarth): This mechanism is far from perfect. See https://github.com/flutter/flutter/issues/4770 // TODO(abarth): This mechanism is far from perfect. See https://github.com/flutter/flutter/issues/4770
if (SchedulerBinding.instance!.schedulerPhase == SchedulerPhase.idle) { if (SchedulerBinding.instance.schedulerPhase == SchedulerPhase.idle) {
// If we're between frames (SchedulerPhase.idle) then absorb any // If we're between frames (SchedulerPhase.idle) then absorb any
// subsequent pointers from this frame. The absorbing flag will be // subsequent pointers from this frame. The absorbing flag will be
// reset in the next frame, see build(). // reset in the next frame, see build().
@ -5330,7 +5330,7 @@ class NavigatorState extends State<Navigator> with TickerProviderStateMixin, Res
// to false on the next frame. // to false on the next frame.
}); });
} }
_activePointers.toList().forEach(WidgetsBinding.instance!.cancelPointer); _activePointers.toList().forEach(WidgetsBinding.instance.cancelPointer);
} }
@override @override

View File

@ -1332,7 +1332,7 @@ class _NestedScrollController extends ScrollController {
// the position change notifications because those happen synchronously // the position change notifications because those happen synchronously
// during a frame, at a time where it's too late to call setState. Since the // during a frame, at a time where it's too late to call setState. Since the
// result is usually animated, the lag incurred is no big deal. // result is usually animated, the lag incurred is no big deal.
SchedulerBinding.instance!.addPostFrameCallback( SchedulerBinding.instance.addPostFrameCallback(
(Duration timeStamp) { (Duration timeStamp) {
coordinator.updateShadow(); coordinator.updateShadow();
}, },

View File

@ -151,8 +151,8 @@ class OverlayEntry extends ChangeNotifier {
return; return;
overlay._entries.remove(this); overlay._entries.remove(this);
if (SchedulerBinding.instance!.schedulerPhase == SchedulerPhase.persistentCallbacks) { if (SchedulerBinding.instance.schedulerPhase == SchedulerPhase.persistentCallbacks) {
SchedulerBinding.instance!.addPostFrameCallback((Duration duration) { SchedulerBinding.instance.addPostFrameCallback((Duration duration) {
overlay._markDirty(); overlay._markDirty();
}); });
} else { } else {

View File

@ -627,7 +627,7 @@ class SliverReorderableListState extends State<SliverReorderableList> with Ticke
item.dragging = true; item.dragging = true;
item.rebuild(); item.rebuild();
_dragStartTransitionComplete = false; _dragStartTransitionComplete = false;
SchedulerBinding.instance!.addPostFrameCallback((Duration duration) { SchedulerBinding.instance.addPostFrameCallback((Duration duration) {
_dragStartTransitionComplete = true; _dragStartTransitionComplete = true;
}); });

View File

@ -275,18 +275,18 @@ class _RootRestorationScopeState extends State<RootRestorationScope> {
void _loadRootBucketIfNecessary() { void _loadRootBucketIfNecessary() {
if (_isWaitingForRootBucket && !_isLoadingRootBucket) { if (_isWaitingForRootBucket && !_isLoadingRootBucket) {
_isLoadingRootBucket = true; _isLoadingRootBucket = true;
RendererBinding.instance!.deferFirstFrame(); RendererBinding.instance.deferFirstFrame();
ServicesBinding.instance!.restorationManager.rootBucket.then((RestorationBucket? bucket) { ServicesBinding.instance.restorationManager.rootBucket.then((RestorationBucket? bucket) {
_isLoadingRootBucket = false; _isLoadingRootBucket = false;
if (mounted) { if (mounted) {
ServicesBinding.instance!.restorationManager.addListener(_replaceRootBucket); ServicesBinding.instance.restorationManager.addListener(_replaceRootBucket);
setState(() { setState(() {
_rootBucket = bucket; _rootBucket = bucket;
_rootBucketValid = true; _rootBucketValid = true;
_okToRenderBlankContainer = false; _okToRenderBlankContainer = false;
}); });
} }
RendererBinding.instance!.allowFirstFrame(); RendererBinding.instance.allowFirstFrame();
}); });
} }
} }
@ -294,7 +294,7 @@ class _RootRestorationScopeState extends State<RootRestorationScope> {
void _replaceRootBucket() { void _replaceRootBucket() {
_rootBucketValid = false; _rootBucketValid = false;
_rootBucket = null; _rootBucket = null;
ServicesBinding.instance!.restorationManager.removeListener(_replaceRootBucket); ServicesBinding.instance.restorationManager.removeListener(_replaceRootBucket);
_loadRootBucketIfNecessary(); _loadRootBucketIfNecessary();
assert(!_isWaitingForRootBucket); // Ensure that load finished synchronously. assert(!_isWaitingForRootBucket); // Ensure that load finished synchronously.
} }
@ -302,7 +302,7 @@ class _RootRestorationScopeState extends State<RootRestorationScope> {
@override @override
void dispose() { void dispose() {
if (_rootBucketValid) { if (_rootBucketValid) {
ServicesBinding.instance!.restorationManager.removeListener(_replaceRootBucket); ServicesBinding.instance.restorationManager.removeListener(_replaceRootBucket);
} }
super.dispose(); super.dispose();
} }

View File

@ -504,7 +504,7 @@ class _RouterState<T> extends State<Router<T>> with RestorationMixin {
return; return;
assert(_currentIntentionToReport != _IntentionToReportRouteInformation.none); assert(_currentIntentionToReport != _IntentionToReportRouteInformation.none);
_routeInformationReportingTaskScheduled = true; _routeInformationReportingTaskScheduled = true;
SchedulerBinding.instance!.addPostFrameCallback(_reportRouteInformation); SchedulerBinding.instance.addPostFrameCallback(_reportRouteInformation);
} }
void _reportRouteInformation(Duration timestamp) { void _reportRouteInformation(Duration timestamp) {
@ -962,7 +962,7 @@ class RootBackButtonDispatcher extends BackButtonDispatcher with WidgetsBindingO
@override @override
void addCallback(ValueGetter<Future<bool>> callback) { void addCallback(ValueGetter<Future<bool>> callback) {
if (!hasCallbacks) if (!hasCallbacks)
WidgetsBinding.instance!.addObserver(this); WidgetsBinding.instance.addObserver(this);
super.addCallback(callback); super.addCallback(callback);
} }
@ -970,7 +970,7 @@ class RootBackButtonDispatcher extends BackButtonDispatcher with WidgetsBindingO
void removeCallback(ValueGetter<Future<bool>> callback) { void removeCallback(ValueGetter<Future<bool>> callback) {
super.removeCallback(callback); super.removeCallback(callback);
if (!hasCallbacks) if (!hasCallbacks)
WidgetsBinding.instance!.removeObserver(this); WidgetsBinding.instance.removeObserver(this);
} }
@override @override
@ -1356,7 +1356,7 @@ class PlatformRouteInformationProvider extends RouteInformationProvider with Wid
@override @override
void addListener(VoidCallback listener) { void addListener(VoidCallback listener) {
if (!hasListeners) if (!hasListeners)
WidgetsBinding.instance!.addObserver(this); WidgetsBinding.instance.addObserver(this);
super.addListener(listener); super.addListener(listener);
} }
@ -1364,7 +1364,7 @@ class PlatformRouteInformationProvider extends RouteInformationProvider with Wid
void removeListener(VoidCallback listener) { void removeListener(VoidCallback listener) {
super.removeListener(listener); super.removeListener(listener);
if (!hasListeners) if (!hasListeners)
WidgetsBinding.instance!.removeObserver(this); WidgetsBinding.instance.removeObserver(this);
} }
@override @override
@ -1374,7 +1374,7 @@ class PlatformRouteInformationProvider extends RouteInformationProvider with Wid
// is no longer being used, there's no listener, and so it will get garbage // is no longer being used, there's no listener, and so it will get garbage
// collected. // collected.
if (hasListeners) if (hasListeners)
WidgetsBinding.instance!.removeObserver(this); WidgetsBinding.instance.removeObserver(this);
super.dispose(); super.dispose();
} }

View File

@ -616,11 +616,11 @@ mixin LocalHistoryRoute<T> on Route<T> {
entry._owner = null; entry._owner = null;
entry._notifyRemoved(); entry._notifyRemoved();
if (_localHistory!.isEmpty) { if (_localHistory!.isEmpty) {
if (SchedulerBinding.instance!.schedulerPhase == SchedulerPhase.persistentCallbacks) { if (SchedulerBinding.instance.schedulerPhase == SchedulerPhase.persistentCallbacks) {
// The local history might be removed as a result of disposing inactive // The local history might be removed as a result of disposing inactive
// elements during finalizeTree. The state is locked at this moment, and // elements during finalizeTree. The state is locked at this moment, and
// we can only notify state has changed in the next frame. // we can only notify state has changed in the next frame.
SchedulerBinding.instance!.addPostFrameCallback((Duration duration) { SchedulerBinding.instance.addPostFrameCallback((Duration duration) {
changedInternalState(); changedInternalState();
}); });
} else { } else {

View File

@ -83,7 +83,7 @@ class ScrollAwareImageProvider<T extends Object> extends ImageProvider<T> {
// Do this before checking scrolling, so that if the bytes are available we // Do this before checking scrolling, so that if the bytes are available we
// render them even though we're scrolling fast - there's no additional // render them even though we're scrolling fast - there's no additional
// allocations to do for texture memory, it's already there. // allocations to do for texture memory, it's already there.
if (stream.completer != null || PaintingBinding.instance!.imageCache!.containsKey(key)) { if (stream.completer != null || PaintingBinding.instance.imageCache.containsKey(key)) {
imageProvider.resolveStreamForKey(configuration, stream, key, handleError); imageProvider.resolveStreamForKey(configuration, stream, key, handleError);
return; return;
} }
@ -96,7 +96,7 @@ class ScrollAwareImageProvider<T extends Object> extends ImageProvider<T> {
// Try to get to end of the frame callbacks of the next frame, and then // Try to get to end of the frame callbacks of the next frame, and then
// check again. // check again.
if (Scrollable.recommendDeferredLoadingForContext(context.context!)) { if (Scrollable.recommendDeferredLoadingForContext(context.context!)) {
SchedulerBinding.instance!.scheduleFrameCallback((_) { SchedulerBinding.instance.scheduleFrameCallback((Duration timeStamp) {
scheduleMicrotask(() => resolveStreamForKey(configuration, stream, key, handleError)); scheduleMicrotask(() => resolveStreamForKey(configuration, stream, key, handleError));
}); });
return; return;

View File

@ -224,7 +224,7 @@ class ScrollPhysics {
assert(metrics != null); assert(metrics != null);
assert(context != null); assert(context != null);
if (parent == null) { if (parent == null) {
final double maxPhysicalPixels = WidgetsBinding.instance!.window.physicalSize.longestSide; final double maxPhysicalPixels = WidgetsBinding.instance.window.physicalSize.longestSide;
return velocity.abs() > maxPhysicalPixels; return velocity.abs() > maxPhysicalPixels;
} }
return parent!.recommendDeferredLoading(velocity, metrics, context); return parent!.recommendDeferredLoading(velocity, metrics, context);
@ -356,8 +356,8 @@ class ScrollPhysics {
static final Tolerance _kDefaultTolerance = Tolerance( static final Tolerance _kDefaultTolerance = Tolerance(
// TODO(ianh): Handle the case of the device pixel ratio changing. // TODO(ianh): Handle the case of the device pixel ratio changing.
// TODO(ianh): Get this from the local MediaQuery not dart:ui's window object. // TODO(ianh): Get this from the local MediaQuery not dart:ui's window object.
velocity: 1.0 / (0.050 * WidgetsBinding.instance!.window.devicePixelRatio), // logical pixels per second velocity: 1.0 / (0.050 * WidgetsBinding.instance.window.devicePixelRatio), // logical pixels per second
distance: 1.0 / WidgetsBinding.instance!.window.devicePixelRatio, // logical pixels distance: 1.0 / WidgetsBinding.instance.window.devicePixelRatio, // logical pixels
); );
/// The tolerance to use for ballistic simulations. /// The tolerance to use for ballistic simulations.

View File

@ -254,7 +254,7 @@ abstract class ScrollPosition extends ViewportOffset with ScrollMetrics {
/// If there is any overscroll, it is reported using [didOverscrollBy]. /// If there is any overscroll, it is reported using [didOverscrollBy].
double setPixels(double newPixels) { double setPixels(double newPixels) {
assert(hasPixels); assert(hasPixels);
assert(SchedulerBinding.instance!.schedulerPhase != SchedulerPhase.persistentCallbacks, "A scrollable's position should not change during the build, layout, and paint phases, otherwise the rendering will be confused."); assert(SchedulerBinding.instance.schedulerPhase != SchedulerPhase.persistentCallbacks, "A scrollable's position should not change during the build, layout, and paint phases, otherwise the rendering will be confused.");
if (newPixels != pixels) { if (newPixels != pixels) {
final double overscroll = applyBoundaryConditions(newPixels); final double overscroll = applyBoundaryConditions(newPixels);
assert(() { assert(() {
@ -375,7 +375,7 @@ abstract class ScrollPosition extends ViewportOffset with ScrollMetrics {
_impliedVelocity = value - pixels; _impliedVelocity = value - pixels;
_pixels = value; _pixels = value;
notifyListeners(); notifyListeners();
SchedulerBinding.instance!.addPostFrameCallback((Duration timeStamp) { SchedulerBinding.instance.addPostFrameCallback((Duration timeStamp) {
_impliedVelocity = 0; _impliedVelocity = 0;
}); });
} }

View File

@ -440,7 +440,7 @@ class ScrollableState extends State<Scrollable> with TickerProviderStateMixin, R
_persistedScrollOffset.value = offset; _persistedScrollOffset.value = offset;
// [saveOffset] is called after a scrolling ends and it is usually not // [saveOffset] is called after a scrolling ends and it is usually not
// followed by a frame. Therefore, manually flush restoration data. // followed by a frame. Therefore, manually flush restoration data.
ServicesBinding.instance!.restorationManager.flushData(); ServicesBinding.instance.restorationManager.flushData();
} }
@override @override
@ -705,7 +705,7 @@ class ScrollableState extends State<Scrollable> with TickerProviderStateMixin, R
final double targetScrollOffset = _targetScrollOffsetForPointerScroll(delta); final double targetScrollOffset = _targetScrollOffsetForPointerScroll(delta);
// Only express interest in the event if it would actually result in a scroll. // Only express interest in the event if it would actually result in a scroll.
if (delta != 0.0 && targetScrollOffset != position.pixels) { if (delta != 0.0 && targetScrollOffset != position.pixels) {
GestureBinding.instance!.pointerSignalResolver.register(event, _handlePointerScroll); GestureBinding.instance.pointerSignalResolver.register(event, _handlePointerScroll);
} }
} }
} }

Some files were not shown because too many files have changed in this diff Show More