Revert "Add option to delay rendering the first frame (#45135)" (#45385)
This reverts commit d285b8843fce6cf039eb197a09b01ca5cc00d92b.
diff --git a/packages/flutter/lib/src/rendering/binding.dart b/packages/flutter/lib/src/rendering/binding.dart
index 9cfb575..adf1037 100644
--- a/packages/flutter/lib/src/rendering/binding.dart
+++ b/packages/flutter/lib/src/rendering/binding.dart
@@ -283,53 +283,6 @@
drawFrame();
}
- int _firstFrameDeferredCount = 0;
- bool _firstFrameSent = false;
-
- /// Whether frames produced by [drawFrame] are sent to the engine.
- ///
- /// If false the framework will do all the work to produce a frame,
- /// but the frame is never send to the engine to actually appear on screen.
- ///
- /// See also:
- ///
- /// * [deferFirstFrame], which defers when the first frame is send to the
- /// engine.
- bool get sendFramesToEngine => _firstFrameSent || _firstFrameDeferredCount == 0;
-
- /// Tell the framework to not send the first frames to the engine until there
- /// is a corresponding call to [allowFirstFrame].
- ///
- /// Call this to perform asynchronous initialisation work before the first
- /// frame is rendered (which takes down the splash screen). The framework
- /// will still do all the work to produce frames, but those frames are never
- /// send to the engine and will not appear on screen.
- ///
- /// Calling this has no effect after the first frame has been send to the
- /// engine.
- void deferFirstFrame() {
- assert(_firstFrameDeferredCount >= 0);
- _firstFrameDeferredCount += 1;
- }
-
- /// Called after [deferFirstFrame] to tell the framework that it is ok to
- /// send the first frame to the engine now.
- ///
- /// For best performance, this method should only be called while the
- /// [schedulerPhase] is [SchedulerPhase.idle].
- ///
- /// This method may only be called once for each corresponding call
- /// to [deferFirstFrame].
- void allowFirstFrame() {
- assert(_firstFrameDeferredCount > 0);
- _firstFrameDeferredCount -= 1;
- // Always schedule a warm up frame even if the deferral count is not down to
- // zero yet since the removal of a deferral may uncover new deferrals that
- // are lower in the widget tree.
- if (!_firstFrameSent)
- scheduleWarmUpFrame();
- }
-
/// Pump the rendering pipeline to generate a frame.
///
/// This method is called by [handleDrawFrame], which itself is called
@@ -391,11 +344,8 @@
pipelineOwner.flushLayout();
pipelineOwner.flushCompositingBits();
pipelineOwner.flushPaint();
- if (sendFramesToEngine) {
- renderView.compositeFrame(); // this sends the bits to the GPU
- pipelineOwner.flushSemantics(); // this also sends the semantics to the OS.
- _firstFrameSent = true;
- }
+ renderView.compositeFrame(); // this sends the bits to the GPU
+ pipelineOwner.flushSemantics(); // this also sends the semantics to the OS.
}
@override
diff --git a/packages/flutter/lib/src/widgets/binding.dart b/packages/flutter/lib/src/widgets/binding.dart
index 1fd7d81..c4eb958 100644
--- a/packages/flutter/lib/src/widgets/binding.dart
+++ b/packages/flutter/lib/src/widgets/binding.dart
@@ -572,6 +572,9 @@
}
bool _needToReportFirstFrame = true;
+ int _deferFirstFrameReportCount = 0;
+ bool get _reportFirstFrame => _deferFirstFrameReportCount == 0;
+
final Completer<void> _firstFrameCompleter = Completer<void>();
@@ -597,6 +600,11 @@
/// Whether the first frame has finished building.
///
+ /// Only useful in profile and debug builds; in release builds, this always
+ /// return false. This can be deferred using [deferFirstFrameReport] and
+ /// [allowFirstFrameReport]. The value is set at the end of the call to
+ /// [drawFrame].
+ ///
/// This value can also be obtained over the VM service protocol as
/// `ext.flutter.didSendFirstFrameEvent`.
///
@@ -608,30 +616,27 @@
/// Tell the framework not to report the frame it is building as a "useful"
/// first frame until there is a corresponding call to [allowFirstFrameReport].
///
- /// Deprecated. Use [deferFirstFrame]/[allowFirstFrame] to delay rendering the
- /// first frame.
- @Deprecated(
- 'Use deferFirstFrame/allowFirstFrame to delay rendering the first frame. '
- 'This feature was deprecated after v1.12.4.'
- )
+ /// This is used by [WidgetsApp] to avoid reporting frames that aren't useful
+ /// during startup as the "first frame".
void deferFirstFrameReport() {
if (!kReleaseMode) {
- deferFirstFrame();
+ assert(_deferFirstFrameReportCount >= 0);
+ _deferFirstFrameReportCount += 1;
}
}
/// When called after [deferFirstFrameReport]: tell the framework to report
/// the frame it is building as a "useful" first frame.
///
- /// Deprecated. Use [deferFirstFrame]/[allowFirstFrame] to delay rendering the
- /// first frame.
- @Deprecated(
- 'Use deferFirstFrame/allowFirstFrame to delay rendering the first frame. '
- 'This feature was deprecated after v1.12.4.'
- )
+ /// This method may only be called once for each corresponding call
+ /// to [deferFirstFrameReport].
+ ///
+ /// This is used by [WidgetsApp] to report when the first useful frame is
+ /// painted.
void allowFirstFrameReport() {
if (!kReleaseMode) {
- allowFirstFrame();
+ assert(_deferFirstFrameReportCount >= 1);
+ _deferFirstFrameReportCount -= 1;
}
}
@@ -748,23 +753,18 @@
return true;
}());
- TimingsCallback firstFrameCallback;
- if (_needToReportFirstFrame) {
+ if (_needToReportFirstFrame && _reportFirstFrame) {
assert(!_firstFrameCompleter.isCompleted);
+ TimingsCallback firstFrameCallback;
firstFrameCallback = (List<FrameTiming> timings) {
- assert(sendFramesToEngine);
if (!kReleaseMode) {
developer.Timeline.instantSync('Rasterized first useful frame');
developer.postEvent('Flutter.FirstFrame', <String, dynamic>{});
}
SchedulerBinding.instance.removeTimingsCallback(firstFrameCallback);
- firstFrameCallback = null;
_firstFrameCompleter.complete();
};
- // Callback is only invoked when [Window.render] is called. When
- // [sendFramesToEngine] is set to false during the frame, it will not
- // be called and we need to remove the callback (see below).
SchedulerBinding.instance.addTimingsCallback(firstFrameCallback);
}
@@ -780,14 +780,11 @@
}());
}
if (!kReleaseMode) {
- if (_needToReportFirstFrame && sendFramesToEngine) {
+ if (_needToReportFirstFrame && _reportFirstFrame) {
developer.Timeline.instantSync('Widgets built first useful frame');
}
}
_needToReportFirstFrame = false;
- if (firstFrameCallback != null && !sendFramesToEngine) {
- SchedulerBinding.instance.removeTimingsCallback(firstFrameCallback);
- }
}
/// The [Element] that is at the root of the hierarchy (and which wraps the
@@ -835,9 +832,12 @@
return true;
}());
+ deferFirstFrameReport();
if (renderViewElement != null)
buildOwner.reassemble(renderViewElement);
- return super.performReassemble();
+ return super.performReassemble().then((void value) {
+ allowFirstFrameReport();
+ });
}
}
diff --git a/packages/flutter/lib/src/widgets/localizations.dart b/packages/flutter/lib/src/widgets/localizations.dart
index 29196b3..0cc93a8 100644
--- a/packages/flutter/lib/src/widgets/localizations.dart
+++ b/packages/flutter/lib/src/widgets/localizations.dart
@@ -6,7 +6,6 @@
import 'dart:ui' show Locale;
import 'package:flutter/foundation.dart';
-import 'package:flutter/rendering.dart';
import 'basic.dart';
import 'binding.dart';
@@ -520,9 +519,9 @@
// 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
// "useful" frame until after the async load has completed.
- RendererBinding.instance.deferFirstFrame();
+ WidgetsBinding.instance.deferFirstFrameReport();
typeToResourcesFuture.then<void>((Map<Type, dynamic> value) {
- RendererBinding.instance.allowFirstFrame();
+ WidgetsBinding.instance.allowFirstFrameReport();
if (!mounted)
return;
setState(() {
diff --git a/packages/flutter/test/widgets/binding_deferred_first_frame_test.dart b/packages/flutter/test/widgets/binding_deferred_first_frame_test.dart
deleted file mode 100644
index 0f6d019..0000000
--- a/packages/flutter/test/widgets/binding_deferred_first_frame_test.dart
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'dart:async';
-
-import 'package:flutter/rendering.dart';
-import 'package:flutter_test/flutter_test.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter/widgets.dart';
-
-const String _actualContent = 'Actual Content';
-const String _loading = 'Loading...';
-
-void main() {
- testWidgets('deferFirstFrame/allowFirstFrame stops sending frames to engine', (WidgetTester tester) async {
- expect(RendererBinding.instance.sendFramesToEngine, isTrue);
-
- final Completer<void> completer = Completer<void>();
- await tester.pumpWidget(
- Directionality(
- textDirection: TextDirection.ltr,
- child: _DeferringWidget(
- key: UniqueKey(),
- loader: completer.future,
- ),
- ),
- );
- final _DeferringWidgetState state = tester.state<_DeferringWidgetState>(find.byType(_DeferringWidget));
-
- expect(find.text(_loading), findsOneWidget);
- expect(find.text(_actualContent), findsNothing);
- expect(RendererBinding.instance.sendFramesToEngine, isFalse);
-
- await tester.pump();
- expect(find.text(_loading), findsOneWidget);
- expect(find.text(_actualContent), findsNothing);
- expect(RendererBinding.instance.sendFramesToEngine, isFalse);
- expect(state.doneLoading, isFalse);
-
- // Complete the future to start sending frames.
- completer.complete();
- await tester.idle();
- expect(state.doneLoading, isTrue);
- expect(RendererBinding.instance.sendFramesToEngine, isTrue);
-
- await tester.pump();
- expect(find.text(_loading), findsNothing);
- expect(find.text(_actualContent), findsOneWidget);
- expect(RendererBinding.instance.sendFramesToEngine, isTrue);
- });
-}
-
-class _DeferringWidget extends StatefulWidget {
- const _DeferringWidget({Key key, this.loader}) : super(key: key);
-
- final Future<void> loader;
-
- @override
- State<_DeferringWidget> createState() => _DeferringWidgetState();
-}
-
-class _DeferringWidgetState extends State<_DeferringWidget> {
- bool doneLoading = false;
-
- @override
- void initState() {
- super.initState();
- RendererBinding.instance.deferFirstFrame();
- widget.loader.then((_) {
- setState(() {
- doneLoading = true;
- RendererBinding.instance.allowFirstFrame();
- });
- });
- }
-
- @override
- Widget build(BuildContext context) {
- return doneLoading
- ? const Text(_actualContent)
- : const Text(_loading);
- }
-}