-
Notifications
You must be signed in to change notification settings - Fork 29.7k
Description
In the engine an opacity layer is rendered by rendering child layers, performing a render target switch, and then blending the resulting texture with the opacity. This texture created by child rendering is cached, and if the child layers have not changed (as determined by the layer identity/id), the texture can be re-used on subsequent frames even as the opacity itself updates.
Unfortunately in many circumstances the framework does not hit this fast path. For example, consider the following code which uses AnimatedSwitcher and its default FadeTransition:
import 'package:flutter/material.dart';
void main() {
runApp(const MaterialApp(
checkerboardRasterCacheImages: true,
home: Scaffold(
body: Example(),
),
));
}
class Example extends StatefulWidget {
const Example({Key? key}) : super(key: key);
@override
State<Example> createState() => _ExampleState();
}
class _ExampleState extends State<Example> {
bool alternate = false;
void _switch() {
setState(() {
alternate = !alternate;
});
}
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Container(height: 100),
TextButton(onPressed: _switch, child: const Text('Switch')),
AnimatedSwitcher(
duration: const Duration(milliseconds: 250),
child: Container(
key: ValueKey<bool>(alternate),
child: Column(
children: <Widget>[
Container(
width: 200,
height: 200,
color: Colors.red,
child:
alternate ? const Text('TITLE A') : const Text('TITLE B'),
),
],
),
),
),
],
);
}
}
If you observe this on device, you will notice that the raster cache checkerboard flashes - because each new frame gets a new entry as the children are not reused. This can also be observed in the timeline, where each frame has a RasterCachePopulate call indicating the texture created by child rendering has been recreated. Note that this is significantly easier to observe if the child rasterization is more complex.
If we replace the Container in the example above with a RepaintBoundary, then we get cached rendering:
To ensure that opacity performance can hit the fast path more frequently, we should update the Opacity and FadeTransition widgets to insert a RepaintBoundary child. This may have an impact on subpixel rendering. To evaluate I will run tests in google3 and report back. The expected impact of this change is improved raster times in applications that use AnimatedSwitcher, FadeTransition, and Opacity.

