Unverified Commit 4119210e authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[framework] use Visibility instead of Opacity (#112191)

parent 97b53bc9
...@@ -378,9 +378,9 @@ class _CupertinoContextMenuState extends State<CupertinoContextMenu> with Ticker ...@@ -378,9 +378,9 @@ class _CupertinoContextMenuState extends State<CupertinoContextMenu> with Ticker
onTap: _onTap, onTap: _onTap,
child: TickerMode( child: TickerMode(
enabled: !_childHidden, enabled: !_childHidden,
child: Opacity( child: Visibility.maintain(
key: _childGlobalKey, key: _childGlobalKey,
opacity: _childHidden ? 0.0 : 1.0, visible: !_childHidden,
child: widget.child, child: widget.child,
), ),
), ),
......
...@@ -728,9 +728,8 @@ class _Label extends StatelessWidget { ...@@ -728,9 +728,8 @@ class _Label extends StatelessWidget {
if (!showUnselectedLabels && !showSelectedLabels) { if (!showUnselectedLabels && !showSelectedLabels) {
// Never show any labels. // Never show any labels.
text = Opacity( text = Visibility.maintain(
alwaysIncludeSemantics: true, visible: false,
opacity: 0.0,
child: text, child: text,
); );
} else if (!showUnselectedLabels) { } else if (!showUnselectedLabels) {
......
...@@ -619,9 +619,8 @@ class _RailDestination extends StatelessWidget { ...@@ -619,9 +619,8 @@ class _RailDestination extends StatelessWidget {
SizedBox( SizedBox(
width: 0, width: 0,
height: 0, height: 0,
child: Opacity( child: Visibility.maintain(
alwaysIncludeSemantics: true, visible: false,
opacity: 0.0,
child: label, child: label,
), ),
), ),
......
...@@ -289,7 +289,9 @@ class Directionality extends _UbiquitousInheritedWidget { ...@@ -289,7 +289,9 @@ class Directionality extends _UbiquitousInheritedWidget {
/// ///
/// * [Visibility], which can hide a child more efficiently (albeit less /// * [Visibility], which can hide a child more efficiently (albeit less
/// subtly, because it is either visible or hidden, rather than allowing /// subtly, because it is either visible or hidden, rather than allowing
/// fractional opacity values). /// fractional opacity values). Specifically, the [Visibility.maintain]
/// constructor is equivalent to using an opacity widget with values of
/// `0.0` or `1.0`.
/// * [ShaderMask], which can apply more elaborate effects to its child. /// * [ShaderMask], which can apply more elaborate effects to its child.
/// * [Transform], which applies an arbitrary transform to its child widget at /// * [Transform], which applies an arbitrary transform to its child widget at
/// paint time. /// paint time.
......
...@@ -1656,6 +1656,11 @@ class SliverMultiBoxAdaptorElement extends RenderObjectElement implements Render ...@@ -1656,6 +1656,11 @@ class SliverMultiBoxAdaptorElement extends RenderObjectElement implements Render
/// RenderBox layout protocol. /// RenderBox layout protocol.
/// * [AnimatedOpacity], which uses an animation internally to efficiently /// * [AnimatedOpacity], which uses an animation internally to efficiently
/// animate [Opacity]. /// animate [Opacity].
/// * [SliverVisibility], which can hide a child more efficiently (albeit less
/// subtly, because it is either visible or hidden, rather than allowing
/// fractional opacity values). Specifically, the [SliverVisibility.maintain]
/// constructor is equivalent to using a sliver opacity widget with values of
/// `0.0` or `1.0`.
class SliverOpacity extends SingleChildRenderObjectWidget { class SliverOpacity extends SingleChildRenderObjectWidget {
/// Creates a sliver that makes its sliver child partially transparent. /// Creates a sliver that makes its sliver child partially transparent.
/// ///
......
...@@ -85,6 +85,28 @@ class Visibility extends StatelessWidget { ...@@ -85,6 +85,28 @@ class Visibility extends StatelessWidget {
'Cannot maintain interactivity if size is not maintained.', 'Cannot maintain interactivity if size is not maintained.',
); );
/// Control whether the given [child] is [visible].
///
/// The [child] and [replacement] arguments must not be null.
///
/// This is equivalent to the default [Visibility] constructor with all
/// "maintain" fields set to true. This constructor should be used in place of
/// an [Opacity] widget that only takes on values of `0.0` or `1.0`, as it
/// avoids extra compositing when fully opaque.
const Visibility.maintain({
super.key,
required this.child,
this.replacement = const SizedBox.shrink(),
this.visible = true,
}) : assert(child != null),
assert(replacement != null),
assert(visible != null),
maintainState = true,
maintainAnimation = true,
maintainSize = true,
maintainSemantics = true,
maintainInteractivity = true;
/// The widget to show or hide, as controlled by [visible]. /// The widget to show or hide, as controlled by [visible].
/// ///
/// {@macro flutter.widgets.ProxyWidget.child} /// {@macro flutter.widgets.ProxyWidget.child}
...@@ -332,6 +354,28 @@ class SliverVisibility extends StatelessWidget { ...@@ -332,6 +354,28 @@ class SliverVisibility extends StatelessWidget {
'Cannot maintain interactivity if size is not maintained.', 'Cannot maintain interactivity if size is not maintained.',
); );
/// Control whether the given [sliver] is [visible].
///
/// The [sliver] and [replacementSliver] arguments must not be null.
///
/// This is equivalent to the default [SliverVisibility] constructor with all
/// "maintain" fields set to true. This constructor should be used in place of
/// a [SliverOpacity] widget that only takes on values of `0.0` or `1.0`, as it
/// avoids extra compositing when fully opaque.
const SliverVisibility.maintain({
super.key,
required this.sliver,
this.replacementSliver = const SliverToBoxAdapter(),
this.visible = true,
}) : assert(sliver != null),
assert(replacementSliver != null),
assert(visible != null),
maintainState = true,
maintainAnimation = true,
maintainSize = true,
maintainSemantics = true,
maintainInteractivity = true;
/// The sliver to show or hide, as controlled by [visible]. /// The sliver to show or hide, as controlled by [visible].
final Widget sliver; final Widget sliver;
......
...@@ -1971,8 +1971,8 @@ void main() { ...@@ -1971,8 +1971,8 @@ void main() {
await tester.pumpWidget(widget); await tester.pumpWidget(widget);
expect(find.text('Red'), findsOneWidget); expect(find.text('Red'), findsOneWidget);
expect(find.text('Green'), findsOneWidget); expect(find.text('Green'), findsOneWidget);
expect(tester.widget<Opacity>(find.byType(Opacity).first).opacity, 0.0); expect(tester.widget<Visibility>(find.byType(Visibility).first).visible, false);
expect(tester.widget<Opacity>(find.byType(Opacity).last).opacity, 0.0); expect(tester.widget<Visibility>(find.byType(Visibility).last).visible, false);
}, },
); );
...@@ -2009,8 +2009,8 @@ void main() { ...@@ -2009,8 +2009,8 @@ void main() {
); );
expect(find.text('Red'), findsOneWidget); expect(find.text('Red'), findsOneWidget);
expect(find.text('Green'), findsOneWidget); expect(find.text('Green'), findsOneWidget);
expect(tester.widget<Opacity>(find.byType(Opacity).first).opacity, 0.0); expect(tester.widget<Visibility>(find.byType(Visibility).first).visible, false);
expect(tester.widget<Opacity>(find.byType(Opacity).last).opacity, 0.0); expect(tester.widget<Visibility>(find.byType(Visibility).last).visible, false);
}, },
); );
......
...@@ -359,14 +359,14 @@ void main() { ...@@ -359,14 +359,14 @@ void main() {
); );
final Finder findOpacity = find.descendant( final Finder findVisibility = find.descendant(
of: find.byType(BottomNavigationBar), of: find.byType(BottomNavigationBar),
matching: find.byType(Opacity), matching: find.byType(Visibility),
); );
expect(findOpacity, findsNWidgets(2)); expect(findVisibility, findsNWidgets(2));
expect(tester.widget<Opacity>(findOpacity.at(0)).opacity, 0.0); expect(tester.widget<Visibility>(findVisibility.at(0)).visible, false);
expect(tester.widget<Opacity>(findOpacity.at(1)).opacity, 0.0); expect(tester.widget<Visibility>(findVisibility.at(1)).visible, false);
}); });
testWidgets('BottomNavigationBarTheme can be used to hide selected labels', (WidgetTester tester) async { testWidgets('BottomNavigationBarTheme can be used to hide selected labels', (WidgetTester tester) async {
......
...@@ -4693,14 +4693,14 @@ Finder _opacityAboveLabel(String text) { ...@@ -4693,14 +4693,14 @@ Finder _opacityAboveLabel(String text) {
// Only valid when labelType != all. // Only valid when labelType != all.
double? _labelOpacity(WidgetTester tester, String text) { double? _labelOpacity(WidgetTester tester, String text) {
// We search for both Opacity and FadeTransition since in some // We search for both Visibility and FadeTransition since in some
// cases opacity is animated, in other it's not. // cases opacity is animated, in other it's not.
final Iterable<Opacity> opacityWidgets = tester.widgetList<Opacity>(find.ancestor( final Iterable<Visibility> visibilityWidgets = tester.widgetList<Visibility>(find.ancestor(
of: find.text(text), of: find.text(text),
matching: find.byType(Opacity), matching: find.byType(Visibility),
)); ));
if (opacityWidgets.isNotEmpty) { if (visibilityWidgets.isNotEmpty) {
return opacityWidgets.single.opacity; return visibilityWidgets.single.visible ? 1.0 : 0.0;
} }
final FadeTransition fadeTransitionWidget = tester.widget<FadeTransition>( final FadeTransition fadeTransitionWidget = tester.widget<FadeTransition>(
......
...@@ -363,27 +363,27 @@ Align _destinationsAlign(WidgetTester tester) { ...@@ -363,27 +363,27 @@ Align _destinationsAlign(WidgetTester tester) {
} }
NavigationRailLabelType _labelType(WidgetTester tester) { NavigationRailLabelType _labelType(WidgetTester tester) {
if (_opacityAboveLabel('Abc').evaluate().isNotEmpty && _opacityAboveLabel('Def').evaluate().isNotEmpty) { if (_visibilityAboveLabel('Abc').evaluate().isNotEmpty && _visibilityAboveLabel('Def').evaluate().isNotEmpty) {
return _labelOpacity(tester, 'Abc') == 1 ? NavigationRailLabelType.selected : NavigationRailLabelType.none; return _labelVisibility(tester, 'Abc') ? NavigationRailLabelType.selected : NavigationRailLabelType.none;
} else { } else {
return NavigationRailLabelType.all; return NavigationRailLabelType.all;
} }
} }
Finder _opacityAboveLabel(String text) { Finder _visibilityAboveLabel(String text) {
return find.ancestor( return find.ancestor(
of: find.text(text), of: find.text(text),
matching: find.byType(Opacity), matching: find.byType(Visibility),
); );
} }
// Only valid when labelType != all. // Only valid when labelType != all.
double _labelOpacity(WidgetTester tester, String text) { bool _labelVisibility(WidgetTester tester, String text) {
final Opacity opacityWidget = tester.widget<Opacity>( final Visibility visibilityWidget = tester.widget<Visibility>(
find.ancestor( find.ancestor(
of: find.text(text), of: find.text(text),
matching: find.byType(Opacity), matching: find.byType(Visibility),
), ),
); );
return opacityWidget.opacity; return visibilityWidget.visible;
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment