Unverified Commit 82715394 authored by Kate Lovett's avatar Kate Lovett Committed by GitHub

Conditionally apply clipping in StretchingOverscrollIndicator (#91389)

parent 8d9481b8
...@@ -2807,9 +2807,13 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien ...@@ -2807,9 +2807,13 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
physics: widget.scrollPhysics, physics: widget.scrollPhysics,
dragStartBehavior: widget.dragStartBehavior, dragStartBehavior: widget.dragStartBehavior,
restorationId: widget.restorationId, restorationId: widget.restorationId,
scrollBehavior: widget.scrollBehavior ?? // If a ScrollBehavior is not provided, only apply scrollbars when
// Remove scrollbars if only single line // multiline. The overscroll indicator should not be applied in
(_isMultiline ? null : ScrollConfiguration.of(context).copyWith(scrollbars: false)), // either case, glowing or stretching.
scrollBehavior: widget.scrollBehavior ?? ScrollConfiguration.of(context).copyWith(
scrollbars: _isMultiline,
overscroll: false,
),
viewportBuilder: (BuildContext context, ViewportOffset offset) { viewportBuilder: (BuildContext context, ViewportOffset offset) {
return CompositedTransformTarget( return CompositedTransformTarget(
link: _toolbarLayerLink, link: _toolbarLayerLink,
......
...@@ -12,6 +12,7 @@ import 'package:flutter/scheduler.dart'; ...@@ -12,6 +12,7 @@ import 'package:flutter/scheduler.dart';
import 'basic.dart'; import 'basic.dart';
import 'framework.dart'; import 'framework.dart';
import 'media_query.dart';
import 'notification_listener.dart'; import 'notification_listener.dart';
import 'scroll_notification.dart'; import 'scroll_notification.dart';
import 'ticker_provider.dart'; import 'ticker_provider.dart';
...@@ -737,6 +738,8 @@ class _StretchingOverscrollIndicatorState extends State<StretchingOverscrollIndi ...@@ -737,6 +738,8 @@ class _StretchingOverscrollIndicatorState extends State<StretchingOverscrollIndi
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final Size size = MediaQuery.of(context).size;
double mainAxisSize;
return NotificationListener<ScrollNotification>( return NotificationListener<ScrollNotification>(
onNotification: _handleScrollNotification, onNotification: _handleScrollNotification,
child: AnimatedBuilder( child: AnimatedBuilder(
...@@ -749,9 +752,11 @@ class _StretchingOverscrollIndicatorState extends State<StretchingOverscrollIndi ...@@ -749,9 +752,11 @@ class _StretchingOverscrollIndicatorState extends State<StretchingOverscrollIndi
switch (widget.axis) { switch (widget.axis) {
case Axis.horizontal: case Axis.horizontal:
x += stretch; x += stretch;
mainAxisSize = size.width;
break; break;
case Axis.vertical: case Axis.vertical:
y += stretch; y += stretch;
mainAxisSize = size.height;
break; break;
} }
...@@ -759,13 +764,21 @@ class _StretchingOverscrollIndicatorState extends State<StretchingOverscrollIndi ...@@ -759,13 +764,21 @@ class _StretchingOverscrollIndicatorState extends State<StretchingOverscrollIndi
_lastOverscrollNotification?.overscroll ?? 0.0 _lastOverscrollNotification?.overscroll ?? 0.0
); );
return ClipRect( final double viewportDimension = _lastOverscrollNotification?.metrics.viewportDimension ?? mainAxisSize;
child: Transform(
alignment: alignment, final Widget transform = Transform(
transform: Matrix4.diagonal3Values(x, y, 1.0), alignment: alignment,
child: widget.child, transform: Matrix4.diagonal3Values(x, y, 1.0),
), child: widget.child,
); );
// Only clip if the viewport dimension is smaller than that of the
// screen size in the main axis. If the viewport takes up the whole
// screen, overflow from transforming the viewport is irrelevant.
if (stretch != 0.0 && viewportDimension != mainAxisSize) {
return ClipRect(child: transform);
}
return transform;
}, },
), ),
); );
......
...@@ -11,9 +11,9 @@ import 'package:flutter_test/flutter_test.dart'; ...@@ -11,9 +11,9 @@ import 'package:flutter_test/flutter_test.dart';
void main() { void main() {
Widget buildTest( Widget buildTest(
Key box1Key, GlobalKey box1Key,
Key box2Key, GlobalKey box2Key,
Key box3Key, GlobalKey box3Key,
ScrollController controller, { ScrollController controller, {
Axis axis = Axis.vertical, Axis axis = Axis.vertical,
bool reverse = false, bool reverse = false,
...@@ -30,34 +30,37 @@ void main() { ...@@ -30,34 +30,37 @@ void main() {
return Directionality( return Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
child: ScrollConfiguration( child: MediaQuery(
behavior: const ScrollBehavior().copyWith(overscroll: false), data: const MediaQueryData(size: Size(800.0, 600.0)),
child: StretchingOverscrollIndicator( child: ScrollConfiguration(
axisDirection: axisDirection, behavior: const ScrollBehavior().copyWith(overscroll: false),
child: CustomScrollView( child: StretchingOverscrollIndicator(
reverse: reverse, axisDirection: axisDirection,
scrollDirection: axis, child: CustomScrollView(
controller: controller, reverse: reverse,
slivers: <Widget>[ scrollDirection: axis,
SliverToBoxAdapter(child: Container( controller: controller,
color: const Color(0xD0FF0000), slivers: <Widget>[
key: box1Key, SliverToBoxAdapter(child: Container(
height: 250.0, color: const Color(0xD0FF0000),
width: 300.0, key: box1Key,
)), height: 250.0,
SliverToBoxAdapter(child: Container( width: 300.0,
color: const Color(0xFFFFFF00), )),
key: box2Key, SliverToBoxAdapter(child: Container(
height: 250.0, color: const Color(0xFFFFFF00),
width: 300.0, key: box2Key,
)), height: 250.0,
SliverToBoxAdapter(child: Container( width: 300.0,
color: const Color(0xFF6200EA), )),
key: box3Key, SliverToBoxAdapter(child: Container(
height: 250.0, color: const Color(0xFF6200EA),
width: 300.0, key: box3Key,
)), height: 250.0,
], width: 300.0,
)),
],
),
), ),
), ),
), ),
...@@ -65,9 +68,9 @@ void main() { ...@@ -65,9 +68,9 @@ void main() {
} }
testWidgets('Stretch overscroll vertically', (WidgetTester tester) async { testWidgets('Stretch overscroll vertically', (WidgetTester tester) async {
final Key box1Key = UniqueKey(); final GlobalKey box1Key = GlobalKey();
final Key box2Key = UniqueKey(); final GlobalKey box2Key = GlobalKey();
final Key box3Key = UniqueKey(); final GlobalKey box3Key = GlobalKey();
final ScrollController controller = ScrollController(); final ScrollController controller = ScrollController();
await tester.pumpWidget( await tester.pumpWidget(
buildTest(box1Key, box2Key, box3Key, controller), buildTest(box1Key, box2Key, box3Key, controller),
...@@ -142,9 +145,9 @@ void main() { ...@@ -142,9 +145,9 @@ void main() {
}); });
testWidgets('Stretch overscroll works in reverse - vertical', (WidgetTester tester) async { testWidgets('Stretch overscroll works in reverse - vertical', (WidgetTester tester) async {
final Key box1Key = UniqueKey(); final GlobalKey box1Key = GlobalKey();
final Key box2Key = UniqueKey(); final GlobalKey box2Key = GlobalKey();
final Key box3Key = UniqueKey(); final GlobalKey box3Key = GlobalKey();
final ScrollController controller = ScrollController(); final ScrollController controller = ScrollController();
await tester.pumpWidget( await tester.pumpWidget(
buildTest(box1Key, box2Key, box3Key, controller, reverse: true), buildTest(box1Key, box2Key, box3Key, controller, reverse: true),
...@@ -175,9 +178,9 @@ void main() { ...@@ -175,9 +178,9 @@ void main() {
}); });
testWidgets('Stretch overscroll works in reverse - horizontal', (WidgetTester tester) async { testWidgets('Stretch overscroll works in reverse - horizontal', (WidgetTester tester) async {
final Key box1Key = UniqueKey(); final GlobalKey box1Key = GlobalKey();
final Key box2Key = UniqueKey(); final GlobalKey box2Key = GlobalKey();
final Key box3Key = UniqueKey(); final GlobalKey box3Key = GlobalKey();
final ScrollController controller = ScrollController(); final ScrollController controller = ScrollController();
await tester.pumpWidget( await tester.pumpWidget(
buildTest( buildTest(
...@@ -203,11 +206,11 @@ void main() { ...@@ -203,11 +206,11 @@ void main() {
final TestGesture gesture = await tester.startGesture(tester.getCenter(find.byType(CustomScrollView))); final TestGesture gesture = await tester.startGesture(tester.getCenter(find.byType(CustomScrollView)));
// Overscroll // Overscroll
await gesture.moveBy(const Offset(200.0, 0.0)); await gesture.moveBy(const Offset(-200.0, 0.0));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(box1.localToGlobal(Offset.zero).dx, greaterThan(500.0)); expect(box1.localToGlobal(Offset.zero).dx, lessThan(500.0));
expect(box2.localToGlobal(Offset.zero).dx, greaterThan(200.0)); expect(box2.localToGlobal(Offset.zero).dx, lessThan(200.0));
expect(box3.localToGlobal(Offset.zero).dx, greaterThan(-100.0)); expect(box3.localToGlobal(Offset.zero).dx, lessThan(-100.0));
await expectLater( await expectLater(
find.byType(CustomScrollView), find.byType(CustomScrollView),
matchesGoldenFile('overscroll_stretch.horizontal.reverse.png'), matchesGoldenFile('overscroll_stretch.horizontal.reverse.png'),
...@@ -215,9 +218,9 @@ void main() { ...@@ -215,9 +218,9 @@ void main() {
}); });
testWidgets('Stretch overscroll horizontally', (WidgetTester tester) async { testWidgets('Stretch overscroll horizontally', (WidgetTester tester) async {
final Key box1Key = UniqueKey(); final GlobalKey box1Key = GlobalKey();
final Key box2Key = UniqueKey(); final GlobalKey box2Key = GlobalKey();
final Key box3Key = UniqueKey(); final GlobalKey box3Key = GlobalKey();
final ScrollController controller = ScrollController(); final ScrollController controller = ScrollController();
await tester.pumpWidget( await tester.pumpWidget(
buildTest(box1Key, box2Key, box3Key, controller, axis: Axis.horizontal) buildTest(box1Key, box2Key, box3Key, controller, axis: Axis.horizontal)
...@@ -292,9 +295,9 @@ void main() { ...@@ -292,9 +295,9 @@ void main() {
}); });
testWidgets('Disallow stretching overscroll', (WidgetTester tester) async { testWidgets('Disallow stretching overscroll', (WidgetTester tester) async {
final Key box1Key = UniqueKey(); final GlobalKey box1Key = GlobalKey();
final Key box2Key = UniqueKey(); final GlobalKey box2Key = GlobalKey();
final Key box3Key = UniqueKey(); final GlobalKey box3Key = GlobalKey();
final ScrollController controller = ScrollController(); final ScrollController controller = ScrollController();
double indicatorNotification =0; double indicatorNotification =0;
await tester.pumpWidget( await tester.pumpWidget(
...@@ -337,34 +340,37 @@ void main() { ...@@ -337,34 +340,37 @@ void main() {
// Regression test for https://github.com/flutter/flutter/issues/90197 // Regression test for https://github.com/flutter/flutter/issues/90197
await tester.pumpWidget(Directionality( await tester.pumpWidget(Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
child: ScrollConfiguration( child: MediaQuery(
behavior: const ScrollBehavior().copyWith(overscroll: false), data: const MediaQueryData(size: Size(800.0, 600.0)),
child: Column( child: ScrollConfiguration(
children: <Widget>[ behavior: const ScrollBehavior().copyWith(overscroll: false),
StretchingOverscrollIndicator( child: Column(
axisDirection: AxisDirection.down, children: <Widget>[
child: SizedBox( StretchingOverscrollIndicator(
height: 300, axisDirection: AxisDirection.down,
child: ListView.builder( child: SizedBox(
itemCount: 20, height: 300,
itemBuilder: (BuildContext context, int index){ child: ListView.builder(
return Padding( itemCount: 20,
padding: const EdgeInsets.all(10.0), itemBuilder: (BuildContext context, int index){
child: Text('Index $index'), return Padding(
); padding: const EdgeInsets.all(10.0),
}, child: Text('Index $index'),
);
},
),
), ),
), ),
), Opacity(
Opacity( opacity: 0.5,
opacity: 0.5, child: Container(
child: Container( color: const Color(0xD0FF0000),
color: const Color(0xD0FF0000), height: 100,
height: 100, ),
), )
) ],
], )
) ),
) )
)); ));
......
...@@ -105,17 +105,20 @@ void main() { ...@@ -105,17 +105,20 @@ void main() {
testWidgets('ScrollBehavior stretch android overscroll indicator', (WidgetTester tester) async { testWidgets('ScrollBehavior stretch android overscroll indicator', (WidgetTester tester) async {
await tester.pumpWidget(Directionality( await tester.pumpWidget(Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
child: ScrollConfiguration( child: MediaQuery(
behavior: const ScrollBehavior(androidOverscrollIndicator: AndroidOverscrollIndicator.stretch), data: const MediaQueryData(size: Size(800, 600)),
child: ListView( child: ScrollConfiguration(
children: const <Widget>[ behavior: const ScrollBehavior(androidOverscrollIndicator: AndroidOverscrollIndicator.stretch),
SizedBox( child: ListView(
height: 1000.0, children: const <Widget>[
width: 1000.0, SizedBox(
child: Text('Test'), height: 1000.0,
) width: 1000.0,
] child: Text('Test'),
) )
]
)
),
), ),
)); ));
......
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