@@ -66,6 +66,19 @@ class ViewConfiguration {
6666 return Matrix4 .diagonal3Values (devicePixelRatio, devicePixelRatio, 1.0 );
6767 }
6868
69+ /// Returns whether [toMatrix] would return a different value for this
70+ /// configuration than it would for the given `oldConfiguration` .
71+ bool shouldUpdateMatrix (ViewConfiguration oldConfiguration) {
72+ if (oldConfiguration.runtimeType != runtimeType) {
73+ // New configuration could have different logic, so we don't know
74+ // whether it will need a new transform. Return a conservative result.
75+ return true ;
76+ }
77+ // For this class, the only input to toMatrix is the device pixel ratio,
78+ // so we return true if they differ and false otherwise.
79+ return oldConfiguration.devicePixelRatio != devicePixelRatio;
80+ }
81+
6982 /// Transforms the provided [Size] in logical pixels to physical pixels.
7083 ///
7184 /// The [FlutterView.render] method accepts only sizes in physical pixels, but
@@ -103,6 +116,16 @@ class ViewConfiguration {
103116/// The view represents the total output surface of the render tree and handles
104117/// bootstrapping the rendering pipeline. The view has a unique child
105118/// [RenderBox] , which is required to fill the entire output surface.
119+ ///
120+ /// This object must be bootstrapped in a specific order:
121+ ///
122+ /// 1. First, set the [configuration] (either in the constructor or after
123+ /// construction).
124+ /// 2. Second, [attach] the object to a [PipelineOwner] .
125+ /// 3. Third, use [prepareInitialFrame] to bootstrap the layout and paint logic.
126+ ///
127+ /// After the bootstrapping is complete, the [compositeFrame] method may be used
128+ /// to obtain the rendered output.
106129class RenderView extends RenderObject with RenderObjectWithChildMixin <RenderBox > {
107130 /// Creates the root of the render tree.
108131 ///
@@ -140,6 +163,9 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
140163 /// [TestFlutterView.physicalSize] on the appropriate [TestFlutterView]
141164 /// (typically [WidgetTester.view] ) instead of setting a configuration
142165 /// directly on the [RenderView] .
166+ ///
167+ /// A [configuration] must be set (either directly or by passing one to the
168+ /// constructor) before calling [prepareInitialFrame] .
143169 ViewConfiguration get configuration => _configuration! ;
144170 ViewConfiguration ? _configuration;
145171 set configuration (ViewConfiguration value) {
@@ -149,17 +175,19 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
149175 final ViewConfiguration ? oldConfiguration = _configuration;
150176 _configuration = value;
151177 if (_rootTransform == null ) {
152- // [prepareInitialFrame] has not been called yet, nothing to do for now.
178+ // [prepareInitialFrame] has not been called yet, nothing more to do for now.
153179 return ;
154180 }
155- if (oldConfiguration? . toMatrix () != configuration.toMatrix ( )) {
181+ if (oldConfiguration == null || configuration.shouldUpdateMatrix (oldConfiguration )) {
156182 replaceRootLayer (_updateMatricesAndCreateNewRootLayer ());
157183 }
158184 assert (_rootTransform != null );
159185 markNeedsLayout ();
160186 }
161187
162188 /// Whether a [configuration] has been set.
189+ ///
190+ /// This must be true before calling [prepareInitialFrame] .
163191 bool get hasConfiguration => _configuration != null ;
164192
165193 @override
@@ -202,15 +230,23 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
202230
203231 /// Bootstrap the rendering pipeline by preparing the first frame.
204232 ///
205- /// This should only be called once, and must be called before changing
206- /// [configuration] . It is typically called immediately after calling the
207- /// constructor.
233+ /// This should only be called once. It is typically called immediately after
234+ /// setting the [configuration] the first time (whether by passing one to the
235+ /// constructor, or setting it directly). The [configuration] must have been
236+ /// set before calling this method, and the [RenderView] must have been
237+ /// attached to a [PipelineOwner] using [attach] .
208238 ///
209239 /// This does not actually schedule the first frame. Call
210- /// [PipelineOwner.requestVisualUpdate] on [owner] to do that.
240+ /// [PipelineOwner.requestVisualUpdate] on the [owner] to do that.
241+ ///
242+ /// This should be called before using any methods that rely on the [layer]
243+ /// being initialized, such as [compositeFrame] .
244+ ///
245+ /// This method calls [scheduleInitialLayout] and [scheduleInitialPaint] .
211246 void prepareInitialFrame () {
212- assert (owner != null );
213- assert (_rootTransform == null );
247+ assert (owner != null , 'attach the RenderView to a PipelineOwner before calling prepareInitialFrame' );
248+ assert (_rootTransform == null , 'prepareInitialFrame must only be called once' ); // set by _updateMatricesAndCreateNewRootLayer
249+ assert (hasConfiguration, 'set a configuration before calling prepareInitialFrame' );
214250 scheduleInitialLayout ();
215251 scheduleInitialPaint (_updateMatricesAndCreateNewRootLayer ());
216252 assert (_rootTransform != null );
@@ -219,6 +255,7 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
219255 Matrix4 ? _rootTransform;
220256
221257 TransformLayer _updateMatricesAndCreateNewRootLayer () {
258+ assert (hasConfiguration);
222259 _rootTransform = configuration.toMatrix ();
223260 final TransformLayer rootLayer = TransformLayer (transform: _rootTransform);
224261 rootLayer.attach (this );
@@ -295,12 +332,19 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
295332 /// Uploads the composited layer tree to the engine.
296333 ///
297334 /// Actually causes the output of the rendering pipeline to appear on screen.
335+ ///
336+ /// Before calling this method, the [owner] must be set by calling [attach] ,
337+ /// the [configuration] must be set to a non-null value, and the
338+ /// [prepareInitialFrame] method must have been called.
298339 void compositeFrame () {
299340 if (! kReleaseMode) {
300341 FlutterTimeline .startSync ('COMPOSITING' );
301342 }
302343 try {
303- final ui.SceneBuilder builder = ui.SceneBuilder ();
344+ assert (hasConfiguration, 'set the RenderView configuration before calling compositeFrame' );
345+ assert (_rootTransform != null , 'call prepareInitialFrame before calling compositeFrame' );
346+ assert (layer != null , 'call prepareInitialFrame before calling compositeFrame' );
347+ final ui.SceneBuilder builder = RendererBinding .instance.createSceneBuilder ();
304348 final ui.Scene scene = layer! .buildScene (builder);
305349 if (automaticSystemUiAdjustment) {
306350 _updateSystemChrome ();
0 commit comments