Allow DSS to be dragged when its children do not fill extent (#31832)
* Allow DSS to be dragged when its children do not fill extent * Fix when maxChildSize < 1.0
This commit is contained in:
parent
c0d5fd23ab
commit
4bc35fc87a
@ -262,13 +262,16 @@ class _DraggableSheetExtent {
|
|||||||
}
|
}
|
||||||
double get currentExtent => _currentExtent.value;
|
double get currentExtent => _currentExtent.value;
|
||||||
|
|
||||||
|
double get additionalMinExtent => isAtMin ? 0.0 : 1.0;
|
||||||
|
double get additionalMaxExtent => isAtMax ? 0.0 : 1.0;
|
||||||
|
|
||||||
/// The scroll position gets inputs in terms of pixels, but the extent is
|
/// The scroll position gets inputs in terms of pixels, but the extent is
|
||||||
/// expected to be expressed as a number between 0..1.
|
/// expected to be expressed as a number between 0..1.
|
||||||
void addPixelDelta(double delta, BuildContext context) {
|
void addPixelDelta(double delta, BuildContext context) {
|
||||||
if (availablePixels == 0) {
|
if (availablePixels == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
currentExtent += delta / availablePixels;
|
currentExtent += delta / availablePixels * maxExtent;
|
||||||
DraggableScrollableNotification(
|
DraggableScrollableNotification(
|
||||||
minExtent: minExtent,
|
minExtent: minExtent,
|
||||||
maxExtent: maxExtent,
|
maxExtent: maxExtent,
|
||||||
@ -426,6 +429,17 @@ class _DraggableScrollableSheetScrollPosition
|
|||||||
final _DraggableSheetExtent extent;
|
final _DraggableSheetExtent extent;
|
||||||
bool get listShouldScroll => pixels > 0.0;
|
bool get listShouldScroll => pixels > 0.0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool applyContentDimensions(double minScrollExtent, double maxScrollExtent) {
|
||||||
|
// We need to provide some extra extent if we haven't yet reached the max or
|
||||||
|
// min extents. Otherwise, a list with fewer children than the extent of
|
||||||
|
// the available space will get stuck.
|
||||||
|
return super.applyContentDimensions(
|
||||||
|
minScrollExtent - extent.additionalMinExtent,
|
||||||
|
maxScrollExtent + extent.additionalMaxExtent,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void applyUserOffset(double delta) {
|
void applyUserOffset(double delta) {
|
||||||
if (!listShouldScroll &&
|
if (!listShouldScroll &&
|
||||||
|
@ -297,6 +297,7 @@ void main() {
|
|||||||
children: <TestSemantics>[
|
children: <TestSemantics>[
|
||||||
TestSemantics(
|
TestSemantics(
|
||||||
flags: <SemanticsFlag>[SemanticsFlag.hasImplicitScrolling],
|
flags: <SemanticsFlag>[SemanticsFlag.hasImplicitScrolling],
|
||||||
|
actions: <SemanticsAction>[SemanticsAction.scrollDown, SemanticsAction.scrollUp],
|
||||||
children: <TestSemantics>[
|
children: <TestSemantics>[
|
||||||
TestSemantics(
|
TestSemantics(
|
||||||
label: 'BottomSheet',
|
label: 'BottomSheet',
|
||||||
|
@ -8,7 +8,14 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
Widget _boilerplate(VoidCallback onButtonPressed) {
|
Widget _boilerplate(VoidCallback onButtonPressed, {
|
||||||
|
int itemCount = 100,
|
||||||
|
double initialChildSize = .5,
|
||||||
|
double maxChildSize = 1.0,
|
||||||
|
double minChildSize = .25,
|
||||||
|
double itemExtent,
|
||||||
|
Key containerKey,
|
||||||
|
}) {
|
||||||
return Directionality(
|
return Directionality(
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.ltr,
|
||||||
child: Stack(
|
child: Stack(
|
||||||
@ -18,14 +25,17 @@ void main() {
|
|||||||
onPressed: onButtonPressed,
|
onPressed: onButtonPressed,
|
||||||
),
|
),
|
||||||
DraggableScrollableSheet(
|
DraggableScrollableSheet(
|
||||||
maxChildSize: 1.0,
|
maxChildSize: maxChildSize,
|
||||||
minChildSize: .25,
|
minChildSize: minChildSize,
|
||||||
|
initialChildSize: initialChildSize,
|
||||||
builder: (BuildContext context, ScrollController scrollController) {
|
builder: (BuildContext context, ScrollController scrollController) {
|
||||||
return Container(
|
return Container(
|
||||||
|
key: containerKey,
|
||||||
color: const Color(0xFFABCDEF),
|
color: const Color(0xFFABCDEF),
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
controller: scrollController,
|
controller: scrollController,
|
||||||
itemCount: 100,
|
itemExtent: itemExtent,
|
||||||
|
itemCount: itemCount,
|
||||||
itemBuilder: (BuildContext context, int index) => Text('Item $index'),
|
itemBuilder: (BuildContext context, int index) => Text('Item $index'),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -36,6 +46,38 @@ void main() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
testWidgets('Scrolls correct amount when maxChildSize < 1.0', (WidgetTester tester) async {
|
||||||
|
const Key key = ValueKey<String>('container');
|
||||||
|
await tester.pumpWidget(_boilerplate(
|
||||||
|
null,
|
||||||
|
maxChildSize: .6,
|
||||||
|
initialChildSize: .25,
|
||||||
|
itemExtent: 25.0,
|
||||||
|
containerKey: key,
|
||||||
|
));
|
||||||
|
|
||||||
|
expect(tester.getRect(find.byKey(key)), const Rect.fromLTRB(0.0, 450.0, 800.0, 600.0));
|
||||||
|
await tester.drag(find.text('Item 5'), const Offset(0, -125));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(tester.getRect(find.byKey(key)), const Rect.fromLTRB(0.0, 325.0, 800.0, 600.0));
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Scrolls correct amount when maxChildSize == 1.0', (WidgetTester tester) async {
|
||||||
|
const Key key = ValueKey<String>('container');
|
||||||
|
await tester.pumpWidget(_boilerplate(
|
||||||
|
null,
|
||||||
|
maxChildSize: 1.0,
|
||||||
|
initialChildSize: .25,
|
||||||
|
itemExtent: 25.0,
|
||||||
|
containerKey: key,
|
||||||
|
));
|
||||||
|
|
||||||
|
expect(tester.getRect(find.byKey(key)), const Rect.fromLTRB(0.0, 450.0, 800.0, 600.0));
|
||||||
|
await tester.drag(find.text('Item 5'), const Offset(0, -125));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(tester.getRect(find.byKey(key)), const Rect.fromLTRB(0.0, 325.0, 800.0, 600.0));
|
||||||
|
});
|
||||||
|
|
||||||
for (TargetPlatform platform in TargetPlatform.values) {
|
for (TargetPlatform platform in TargetPlatform.values) {
|
||||||
group('$platform Scroll Physics', () {
|
group('$platform Scroll Physics', () {
|
||||||
debugDefaultTargetPlatformOverride = platform;
|
debugDefaultTargetPlatformOverride = platform;
|
||||||
@ -74,6 +116,23 @@ void main() {
|
|||||||
expect(find.text('Item 36'), findsNothing);
|
expect(find.text('Item 36'), findsNothing);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('Can be dragged down when list is shorter than full height', (WidgetTester tester) async {
|
||||||
|
await tester.pumpWidget(_boilerplate(null, itemCount: 30, initialChildSize: .25));
|
||||||
|
|
||||||
|
expect(find.text('Item 1').hitTestable(), findsOneWidget);
|
||||||
|
expect(find.text('Item 29').hitTestable(), findsNothing);
|
||||||
|
|
||||||
|
await tester.drag(find.text('Item 1'), const Offset(0, -325));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(find.text('Item 1').hitTestable(), findsOneWidget);
|
||||||
|
expect(find.text('Item 29').hitTestable(), findsOneWidget);
|
||||||
|
|
||||||
|
await tester.drag(find.text('Item 1'), const Offset(0, 325));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(find.text('Item 1').hitTestable(), findsOneWidget);
|
||||||
|
expect(find.text('Item 29').hitTestable(), findsNothing);
|
||||||
|
});
|
||||||
|
|
||||||
testWidgets('Can be dragged up and cover its container and scroll in single motion, and then dragged back down', (WidgetTester tester) async {
|
testWidgets('Can be dragged up and cover its container and scroll in single motion, and then dragged back down', (WidgetTester tester) async {
|
||||||
int taps = 0;
|
int taps = 0;
|
||||||
await tester.pumpWidget(_boilerplate(() => taps++));
|
await tester.pumpWidget(_boilerplate(() => taps++));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user