Skip to content

Commit 9881aa6

Browse files
author
Jonah Williams
authored
[framework] attempt non-key solution (flutter#128273)
Adds a special opacity widget that does not act like a repaint boundary. Better solution for flutter#128138
1 parent 2be476d commit 9881aa6

File tree

2 files changed

+51
-14
lines changed

2 files changed

+51
-14
lines changed

packages/flutter/lib/src/material/flexible_space_bar.dart

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import 'dart:math' as math;
66
import 'dart:ui' as ui;
77

88
import 'package:flutter/foundation.dart' show clampDouble;
9+
import 'package:flutter/rendering.dart';
910
import 'package:flutter/widgets.dart';
1011

1112
import 'colors.dart';
@@ -251,17 +252,12 @@ class _FlexibleSpaceBarState extends State<FlexibleSpaceBar> {
251252
left: 0.0,
252253
right: 0.0,
253254
height: height,
254-
child: Opacity(
255-
// We need the child widget to repaint, however both the opacity
256-
// and potentially `widget.background` can be constant which won't
257-
// lead to repainting.
258-
// see: https://github.com/flutter/flutter/issues/127836
259-
key: ValueKey<double>(topPadding),
255+
child: _FlexibleSpaceHeaderOpacity(
260256
// IOS is relying on this semantics node to correctly traverse
261257
// through the app bar when it is collapsed.
262258
alwaysIncludeSemantics: true,
263259
opacity: opacity,
264-
child: widget.background,
260+
child: widget.background
265261
),
266262
));
267263

@@ -426,3 +422,33 @@ class FlexibleSpaceBarSettings extends InheritedWidget {
426422
|| isScrolledUnder != oldWidget.isScrolledUnder;
427423
}
428424
}
425+
426+
// We need the child widget to repaint, however both the opacity
427+
// and potentially `widget.background` can be constant which won't
428+
// lead to repainting.
429+
// see: https://github.com/flutter/flutter/issues/127836
430+
class _FlexibleSpaceHeaderOpacity extends SingleChildRenderObjectWidget {
431+
const _FlexibleSpaceHeaderOpacity({required this.opacity, required super.child, required this.alwaysIncludeSemantics});
432+
433+
final double opacity;
434+
final bool alwaysIncludeSemantics;
435+
436+
@override
437+
RenderObject createRenderObject(BuildContext context) {
438+
return _RenderFlexibleSpaceHeaderOpacity(opacity: opacity, alwaysIncludeSemantics: alwaysIncludeSemantics);
439+
}
440+
441+
@override
442+
void updateRenderObject(BuildContext context, covariant _RenderFlexibleSpaceHeaderOpacity renderObject) {
443+
renderObject
444+
..alwaysIncludeSemantics = alwaysIncludeSemantics
445+
..opacity = opacity;
446+
}
447+
}
448+
449+
class _RenderFlexibleSpaceHeaderOpacity extends RenderOpacity {
450+
_RenderFlexibleSpaceHeaderOpacity({super.opacity, super.alwaysIncludeSemantics});
451+
452+
@override
453+
bool get isRepaintBoundary => false;
454+
}

packages/flutter/test/material/flexible_space_bar_test.dart

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
library;
99

1010
import 'package:flutter/material.dart';
11+
import 'package:flutter/rendering.dart';
1112
import 'package:flutter_test/flutter_test.dart';
1213

1314
import '../widgets/semantics_tester.dart';
@@ -159,7 +160,10 @@ void main() {
159160
),
160161
);
161162

162-
final Opacity backgroundOpacity = tester.firstWidget(find.byType(Opacity));
163+
final dynamic backgroundOpacity = tester.firstWidget(
164+
find.byWidgetPredicate((Widget widget) => widget.runtimeType.toString() == '_FlexibleSpaceHeaderOpacity'));
165+
// accessing private type member.
166+
// ignore: avoid_dynamic_calls
163167
expect(backgroundOpacity.opacity, 1.0);
164168
});
165169

@@ -348,7 +352,7 @@ void main() {
348352
rect: const Rect.fromLTRB(0.0, 0.0, 800.0, 56.0),
349353
children: <TestSemantics>[
350354
TestSemantics(
351-
id: 18,
355+
id: 11,
352356
rect: const Rect.fromLTRB(0.0, 36.0, 800.0, 92.0),
353357
label: 'Expanded title',
354358
textDirection: TextDirection.ltr,
@@ -790,15 +794,15 @@ void main() {
790794
home: SubCategoryScreenView(),
791795
));
792796

793-
expect(RebuildTracker.count, 1);
797+
expect(RenderRebuildTracker.count, 1);
794798

795799
// We drag up to fully collapse the space bar.
796800
for (int i = 0; i < 20; i++) {
797801
await tester.drag(find.byKey(SubCategoryScreenView.scrollKey), const Offset(0, -50.0));
798802
await tester.pumpAndSettle();
799803
}
800804

801-
expect(RebuildTracker.count, greaterThan(1));
805+
expect(RenderRebuildTracker.count, greaterThan(1));
802806
});
803807
}
804808

@@ -825,15 +829,22 @@ class TestDelegate extends SliverPersistentHeaderDelegate {
825829
bool shouldRebuild(TestDelegate oldDelegate) => false;
826830
}
827831

828-
class RebuildTracker extends StatelessWidget {
832+
class RebuildTracker extends SingleChildRenderObjectWidget {
829833
const RebuildTracker({super.key});
830834

835+
@override
836+
RenderObject createRenderObject(BuildContext context) {
837+
return RenderRebuildTracker();
838+
}
839+
}
840+
841+
class RenderRebuildTracker extends RenderProxyBox {
831842
static int count = 0;
832843

833844
@override
834-
Widget build(BuildContext context) {
845+
void paint(PaintingContext context, Offset offset) {
835846
count++;
836-
return const SizedBox(width: 100, height: 100);
847+
super.paint(context, offset);
837848
}
838849
}
839850

0 commit comments

Comments
 (0)