Skip to content

Commit b3f3200

Browse files
author
Jonah Williams
authored
[framework] force flexible space background to rebuild. (flutter#128138)
Fixes flutter#127836 The flexible scroll bar needs to rebuild even if the child/opacity hasn't changed. Force this with a value key.
1 parent 1e70c52 commit b3f3200

File tree

2 files changed

+97
-4
lines changed

2 files changed

+97
-4
lines changed

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,12 +245,18 @@ class _FlexibleSpaceBarState extends State<FlexibleSpaceBar> {
245245
constraints.maxHeight > height) {
246246
height = constraints.maxHeight;
247247
}
248+
final double topPadding = _getCollapsePadding(t, settings);
248249
children.add(Positioned(
249-
top: _getCollapsePadding(t, settings),
250+
top: topPadding,
250251
left: 0.0,
251252
right: 0.0,
252253
height: height,
253254
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),
254260
// IOS is relying on this semantics node to correctly traverse
255261
// through the app bar when it is collapsed.
256262
alwaysIncludeSemantics: true,

packages/flutter/test/material/flexible_space_bar_test.dart

Lines changed: 90 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@ void main() {
348348
rect: const Rect.fromLTRB(0.0, 0.0, 800.0, 56.0),
349349
children: <TestSemantics>[
350350
TestSemantics(
351-
id: 11,
351+
id: 18,
352352
rect: const Rect.fromLTRB(0.0, 36.0, 800.0, 92.0),
353353
label: 'Expanded title',
354354
textDirection: TextDirection.ltr,
@@ -409,8 +409,6 @@ void main() {
409409
label: 'Item 6',
410410
textDirection: TextDirection.ltr,
411411
),
412-
413-
414412
],
415413
),
416414
],
@@ -786,6 +784,22 @@ void main() {
786784
await tester.pumpWidget(buildFrame(TargetPlatform.linux, true));
787785
expect(getTitleBottomLeft(), const Offset(390.0, 0.0));
788786
});
787+
788+
testWidgets('FlexibleSpaceBar rebuilds when scrolling.', (WidgetTester tester) async {
789+
await tester.pumpWidget(const MaterialApp(
790+
home: SubCategoryScreenView(),
791+
));
792+
793+
expect(RebuildTracker.count, 1);
794+
795+
// We drag up to fully collapse the space bar.
796+
for (int i = 0; i < 20; i++) {
797+
await tester.drag(find.byKey(SubCategoryScreenView.scrollKey), const Offset(0, -50.0));
798+
await tester.pumpAndSettle();
799+
}
800+
801+
expect(RebuildTracker.count, greaterThan(1));
802+
});
789803
}
790804

791805
class TestDelegate extends SliverPersistentHeaderDelegate {
@@ -810,3 +824,76 @@ class TestDelegate extends SliverPersistentHeaderDelegate {
810824
@override
811825
bool shouldRebuild(TestDelegate oldDelegate) => false;
812826
}
827+
828+
class RebuildTracker extends StatelessWidget {
829+
const RebuildTracker({super.key});
830+
831+
static int count = 0;
832+
833+
@override
834+
Widget build(BuildContext context) {
835+
count++;
836+
return const SizedBox(width: 100, height: 100);
837+
}
838+
}
839+
840+
class SubCategoryScreenView extends StatefulWidget {
841+
const SubCategoryScreenView({
842+
super.key,
843+
});
844+
845+
static const Key scrollKey = Key('orange box');
846+
847+
@override
848+
State<SubCategoryScreenView> createState() => _SubCategoryScreenViewState();
849+
}
850+
851+
class _SubCategoryScreenViewState extends State<SubCategoryScreenView>
852+
with TickerProviderStateMixin {
853+
@override
854+
Widget build(BuildContext context) {
855+
return Scaffold(
856+
appBar: AppBar(
857+
centerTitle: true,
858+
title: const Text('Test'),
859+
),
860+
body: CustomScrollView(
861+
key: SubCategoryScreenView.scrollKey,
862+
slivers: <Widget>[
863+
SliverAppBar(
864+
leading: const SizedBox(),
865+
expandedHeight: MediaQuery.of(context).size.width / 1.7,
866+
collapsedHeight: 0,
867+
toolbarHeight: 0,
868+
titleSpacing: 0,
869+
leadingWidth: 0,
870+
flexibleSpace: const FlexibleSpaceBar(
871+
background: AspectRatio(
872+
aspectRatio: 1.7,
873+
child: RebuildTracker(),
874+
),
875+
),
876+
),
877+
const SliverToBoxAdapter(child: SizedBox(height: 12)),
878+
SliverToBoxAdapter(
879+
child: GridView.builder(
880+
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
881+
crossAxisCount: 3,
882+
),
883+
shrinkWrap: true,
884+
physics: const NeverScrollableScrollPhysics(),
885+
itemCount: 300,
886+
itemBuilder: (BuildContext context, int index) {
887+
return Card(
888+
color: Colors.amber,
889+
child: Center(child: Text('$index')),
890+
);
891+
},
892+
),
893+
),
894+
const SliverToBoxAdapter(child: SizedBox(height: 12)),
895+
],
896+
),
897+
);
898+
}
899+
}

0 commit comments

Comments
 (0)