Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
5b0fd2f
[ci.yaml] Prefix devicelab targets with the phone used (#87304)
Jul 30, 2021
a69b3ca
Exclude arm64 from iOS app archs if unsupported by plugins (#87244)
jmagman Jul 30, 2021
c863c01
Use FLUTTER_VIEW_ID to find the Flutter view (#87324)
jiahaog Jul 30, 2021
ee06781
Add no-response workflow. (#87318)
godofredoc Jul 30, 2021
70d2497
Add config for animation_with_microtasks test (#87346)
iskakaushik Jul 30, 2021
e4cf7a0
3be118d24 Roll Skia from 8adb6255053f to 14d87226b3ad (5 revisions) (…
engine-flutter-autoroll Jul 30, 2021
1f067ae
[ci.yaml] presubmit docs renamed to docs_test (#87214)
Jul 30, 2021
bdca339
Revert "Add no-response workflow. (#87318)" (#87361)
godofredoc Jul 30, 2021
46f160d
update the scrollbar (#82687)
xu-baolin Jul 30, 2021
1da3fc8
Roll Engine from 3be118d2486c to f18a930f7322 (5 revisions) (#87368)
engine-flutter-autoroll Jul 30, 2021
39b3515
Roll Plugins from 1b7c4fbce750 to 477a13d8b0af (5 revisions) (#87369)
engine-flutter-autoroll Jul 30, 2021
ab6ce71
[flutter_test/integration_test] added setSurfaceSize test coverage (#…
prefanatic Jul 30, 2021
6823a56
Enable viewport_test.dart's shuffling (#87319)
dkwingsmt Jul 30, 2021
9072bac
Mark devtools_profile_start_test unflaky (#87355)
jmagman Jul 30, 2021
7424db4
Exclude arm64 from iOS app archs if unsupported by plugins on x64 Mac…
jmagman Jul 30, 2021
38a6c8a
Re-land no-reponse bot. (#87372)
godofredoc Jul 30, 2021
3ca8d7b
Revert "Re-land no-reponse bot. (#87372)" (#87377)
godofredoc Jul 30, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
226 changes: 114 additions & 112 deletions .ci.yaml
100755 → 100644

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions TESTOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@
/dev/devicelab/bin/tasks/tiles_scroll_perf__timeline_summary.dart @zanderso @flutter/engine

## Mac iOS DeviceLab tests
/dev/devicelab/bin/tasks/animation_with_microtasks_perf_ios__timeline_summary.dart @iskakaushik @flutter/engine
/dev/devicelab/bin/tasks/backdrop_filter_perf_ios__timeline_summary.dart @zanderso @flutter/engine
/dev/devicelab/bin/tasks/basic_material_app_ios__compile.dart @zanderso @flutter/tool
/dev/devicelab/bin/tasks/channels_integration_test_ios.dart @zanderso @flutter/engine
Expand Down
2 changes: 1 addition & 1 deletion bin/internal/engine.version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
232f7e2e5420c8f5c9fc51f0504a113941c63052
f18a930f7322e7fa3f02a347881393c9204da714
2 changes: 1 addition & 1 deletion bin/internal/flutter_plugins.version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1b7c4fbce75073042ef1fe9e92ddafa12f1d3b0b
477a13d8b0af92aea5e8e53445b1bfd0ef28f32b
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;

import java.util.HashMap;

Expand All @@ -32,9 +31,7 @@ public class MainActivity extends FlutterActivity implements MethodChannel.Metho
private MethodChannel.Result permissionResult;

private View getFlutterView() {
// TODO(egarciad): Set an unique ID in FlutterView, so it's easier to look it up.
ViewGroup root = (ViewGroup)findViewById(android.R.id.content);
return ((ViewGroup)root.getChildAt(0)).getChildAt(0);
return findViewById(FLUTTER_VIEW_ID);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,7 @@ public class MainActivity extends FlutterActivity implements MethodChannel.Metho
private MethodChannel.Result permissionResult;

private View getFlutterView() {
// TODO(egarciad): Set an unique ID in FlutterView, so it's easier to look it up.
ViewGroup root = (ViewGroup)findViewById(android.R.id.content);
return ((ViewGroup)root.getChildAt(0)).getChildAt(0);
return findViewById(FLUTTER_VIEW_ID);
}

private String getViewName(View view) {
Expand Down
119 changes: 77 additions & 42 deletions packages/flutter/lib/src/widgets/scrollbar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -334,16 +334,29 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter {
Rect? _trackRect;
late double _thumbOffset;

/// Update with new [ScrollMetrics]. The scrollbar will show and redraw itself
/// based on these new metrics.
/// Update with new [ScrollMetrics]. If the metrics change, the scrollbar will
/// show and redraw itself based on these new metrics.
///
/// The scrollbar will remain on screen.
void update(
ScrollMetrics metrics,
AxisDirection axisDirection,
) {
if (_lastMetrics != null &&
_lastMetrics!.extentBefore == metrics.extentBefore &&
_lastMetrics!.extentInside == metrics.extentInside &&
_lastMetrics!.extentAfter == metrics.extentAfter &&
_lastAxisDirection == axisDirection)
return;

final ScrollMetrics? oldMetrics = _lastMetrics;
_lastMetrics = metrics;
_lastAxisDirection = axisDirection;

bool _needPaint(ScrollMetrics? metrics) => metrics != null && metrics.maxScrollExtent > metrics.minScrollExtent;
if (!_needPaint(oldMetrics) && !_needPaint(metrics))
return;

notifyListeners();
}

Expand Down Expand Up @@ -526,7 +539,8 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter {
void paint(Canvas canvas, Size size) {
if (_lastAxisDirection == null
|| _lastMetrics == null
|| fadeoutOpacityAnimation.value == 0.0)
|| fadeoutOpacityAnimation.value == 0.0
|| _lastMetrics!.maxScrollExtent <= _lastMetrics!.minScrollExtent)
return;

// Skip painting if there's not enough space.
Expand Down Expand Up @@ -1519,18 +1533,37 @@ class RawScrollbarState<T extends RawScrollbar> extends State<T> with TickerProv
);
}

bool _handleScrollMetricsNotification(ScrollMetricsNotification notification) {
if (!widget.notificationPredicate(ScrollUpdateNotification(metrics: notification.metrics, context: notification.context)))
return false;
if (showScrollbar) {
if (_fadeoutAnimationController.status != AnimationStatus.forward
&& _fadeoutAnimationController.status != AnimationStatus.completed)
_fadeoutAnimationController.forward();
}
scrollbarPainter.update(notification.metrics, notification.metrics.axisDirection);
return false;
}

bool _handleScrollNotification(ScrollNotification notification) {
if (!widget.notificationPredicate(notification))
return false;

final ScrollMetrics metrics = notification.metrics;
if (metrics.maxScrollExtent <= metrics.minScrollExtent)
if (metrics.maxScrollExtent <= metrics.minScrollExtent) {
// Hide the bar when the Scrollable widget has no space to scroll.
if (_fadeoutAnimationController.status != AnimationStatus.dismissed
&& _fadeoutAnimationController.status != AnimationStatus.reverse)
_fadeoutAnimationController.reverse();
scrollbarPainter.update(metrics, metrics.axisDirection);
return false;
}

if (notification is ScrollUpdateNotification ||
notification is OverscrollNotification) {
// Any movements always makes the scrollbar start showing up.
if (_fadeoutAnimationController.status != AnimationStatus.forward)
if (_fadeoutAnimationController.status != AnimationStatus.forward
&& _fadeoutAnimationController.status != AnimationStatus.completed)
_fadeoutAnimationController.forward();

_fadeoutTimer?.cancel();
Expand Down Expand Up @@ -1658,47 +1691,49 @@ class RawScrollbarState<T extends RawScrollbar> extends State<T> with TickerProv
super.dispose();
}


@override
Widget build(BuildContext context) {
updateScrollbarPainter();

return NotificationListener<ScrollNotification>(
onNotification: _handleScrollNotification,
child: RepaintBoundary(
child: RawGestureDetector(
gestures: _gestures,
child: MouseRegion(
onExit: (PointerExitEvent event) {
switch(event.kind) {
case PointerDeviceKind.mouse:
if (enableGestures)
handleHoverExit(event);
break;
case PointerDeviceKind.stylus:
case PointerDeviceKind.invertedStylus:
case PointerDeviceKind.unknown:
case PointerDeviceKind.touch:
break;
}
},
onHover: (PointerHoverEvent event) {
switch(event.kind) {
case PointerDeviceKind.mouse:
if (enableGestures)
handleHover(event);
break;
case PointerDeviceKind.stylus:
case PointerDeviceKind.invertedStylus:
case PointerDeviceKind.unknown:
case PointerDeviceKind.touch:
break;
}
},
child: CustomPaint(
key: _scrollbarPainterKey,
foregroundPainter: scrollbarPainter,
child: RepaintBoundary(child: widget.child),
return NotificationListener<ScrollMetricsNotification>(
onNotification: _handleScrollMetricsNotification,
child: NotificationListener<ScrollNotification>(
onNotification: _handleScrollNotification,
child: RepaintBoundary(
child: RawGestureDetector(
gestures: _gestures,
child: MouseRegion(
onExit: (PointerExitEvent event) {
switch(event.kind) {
case PointerDeviceKind.mouse:
if (enableGestures)
handleHoverExit(event);
break;
case PointerDeviceKind.stylus:
case PointerDeviceKind.invertedStylus:
case PointerDeviceKind.unknown:
case PointerDeviceKind.touch:
break;
}
},
onHover: (PointerHoverEvent event) {
switch(event.kind) {
case PointerDeviceKind.mouse:
if (enableGestures)
handleHover(event);
break;
case PointerDeviceKind.stylus:
case PointerDeviceKind.invertedStylus:
case PointerDeviceKind.unknown:
case PointerDeviceKind.touch:
break;
}
},
child: CustomPaint(
key: _scrollbarPainterKey,
foregroundPainter: scrollbarPainter,
child: RepaintBoundary(child: widget.child),
),
),
),
),
Expand Down
36 changes: 19 additions & 17 deletions packages/flutter/test/rendering/viewport_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,7 @@
// initialize a binding, which rendering_tester will attempt to re-initialize
// (or vice versa).

// TODO(gspencergoog): Remove this tag once this test's state leaks/test
// dependencies have been fixed.
// https://github.com/flutter/flutter/issues/85160
// Fails with "flutter test --test-randomize-ordering-seed=123"
@Tags(<String>['no-shuffle'])
import 'dart:ui' as ui;

import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';
Expand Down Expand Up @@ -785,6 +781,12 @@ void main() {
}

testWidgets('Reverse List showOnScreen', (WidgetTester tester) async {
final ui.Size originalScreenSize = tester.binding.window.physicalSize;
final double originalDevicePixelRatio = tester.binding.window.devicePixelRatio;
addTearDown(() {
tester.binding.window.devicePixelRatioTestValue = originalDevicePixelRatio;
tester.binding.window.physicalSizeTestValue = originalScreenSize;
});
const double screenHeight = 400.0;
const double screenWidth = 400.0;
const double itemHeight = screenHeight / 10.0;
Expand Down Expand Up @@ -1891,17 +1893,17 @@ void main() {
controller.jumpTo(controller.position.maxScrollExtent);
await tester.pumpAndSettle();
expect(controller.offset, maxExtent);
expect(tester.getBottomLeft(find.text('Item 19')).dy, 400.0);
expect(tester.getBottomLeft(find.text('Item 19')).dy, 600.0);

overscrollGesture = await tester.startGesture(tester.getCenter(find.byType(ListView)));
await overscrollGesture.moveBy(const Offset(0, -25));
await tester.pump();
expect(controller.offset, greaterThan(maxExtent));
expect(tester.getBottomLeft(find.text('Item 19')).dy, 375.0);
expect(tester.getBottomLeft(find.text('Item 19')).dy, 575.0);
await overscrollGesture.up();
await tester.pumpAndSettle();
expect(controller.offset, maxExtent);
expect(tester.getBottomLeft(find.text('Item 19')).dy, 400.0);
expect(tester.getBottomLeft(find.text('Item 19')).dy, 600.0);
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }));

testWidgets('Shrinkwrap allows overscrolling on default platforms - horizontal', (WidgetTester tester) async {
Expand Down Expand Up @@ -1930,17 +1932,17 @@ void main() {
controller.jumpTo(controller.position.maxScrollExtent);
await tester.pumpAndSettle();
expect(controller.offset, maxExtent);
expect(tester.getTopRight(find.text('Item 19')).dx, 400.0);
expect(tester.getTopRight(find.text('Item 19')).dx, 800.0);

overscrollGesture = await tester.startGesture(tester.getCenter(find.byType(ListView)));
await overscrollGesture.moveBy(const Offset(-25, 0));
await tester.pump();
expect(controller.offset, greaterThan(maxExtent));
expect(tester.getTopRight(find.text('Item 19')).dx, 375.0);
expect(tester.getTopRight(find.text('Item 19')).dx, 775.0);
await overscrollGesture.up();
await tester.pumpAndSettle();
expect(controller.offset, maxExtent);
expect(tester.getTopRight(find.text('Item 19')).dx, 400.0);
expect(tester.getTopRight(find.text('Item 19')).dx, 800.0);
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }));

testWidgets('Shrinkwrap allows overscrolling per physics - vertical', (WidgetTester tester) async {
Expand Down Expand Up @@ -1969,17 +1971,17 @@ void main() {
controller.jumpTo(controller.position.maxScrollExtent);
await tester.pumpAndSettle();
expect(controller.offset, maxExtent);
expect(tester.getBottomLeft(find.text('Item 19')).dy, 400.0);
expect(tester.getBottomLeft(find.text('Item 19')).dy, 600.0);

overscrollGesture = await tester.startGesture(tester.getCenter(find.byType(ListView)));
await overscrollGesture.moveBy(const Offset(0, -25));
await tester.pump();
expect(controller.offset, greaterThan(maxExtent));
expect(tester.getBottomLeft(find.text('Item 19')).dy, 375.0);
expect(tester.getBottomLeft(find.text('Item 19')).dy, 575.0);
await overscrollGesture.up();
await tester.pumpAndSettle();
expect(controller.offset, maxExtent);
expect(tester.getBottomLeft(find.text('Item 19')).dy, 400.0);
expect(tester.getBottomLeft(find.text('Item 19')).dy, 600.0);
});

testWidgets('Shrinkwrap allows overscrolling per physics - horizontal', (WidgetTester tester) async {
Expand Down Expand Up @@ -2012,17 +2014,17 @@ void main() {
controller.jumpTo(controller.position.maxScrollExtent);
await tester.pumpAndSettle();
expect(controller.offset, maxExtent);
expect(tester.getTopRight(find.text('Item 19')).dx, 400.0);
expect(tester.getTopRight(find.text('Item 19')).dx, 800.0);

overscrollGesture = await tester.startGesture(tester.getCenter(find.byType(ListView)));
await overscrollGesture.moveBy(const Offset(-25, 0));
await tester.pump();
expect(controller.offset, greaterThan(maxExtent));
expect(tester.getTopRight(find.text('Item 19')).dx, 375.0);
expect(tester.getTopRight(find.text('Item 19')).dx, 775.0);
await overscrollGesture.up();
await tester.pumpAndSettle();
expect(controller.offset, maxExtent);
expect(tester.getTopRight(find.text('Item 19')).dx, 400.0);
expect(tester.getTopRight(find.text('Item 19')).dx, 800.0);
});

testWidgets('Handles infinite constraints when TargetPlatform is iOS or macOS', (WidgetTester tester) async {
Expand Down
72 changes: 72 additions & 0 deletions packages/flutter/test/widgets/scrollbar_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1565,4 +1565,76 @@ void main() {
..rect(rect: const Rect.fromLTRB(794.0, 0.0, 800.0, 8.0)));
});

testWidgets('The bar can show or hide when the viewport size change', (WidgetTester tester) async {
final ScrollController scrollController = ScrollController();
Widget buildFrame(double height) {
return Directionality(
textDirection: TextDirection.ltr,
child: MediaQuery(
data: const MediaQueryData(),
child: RawScrollbar(
controller: scrollController,
isAlwaysShown: true,
child: SingleChildScrollView(
controller: scrollController,
child: SizedBox(width: double.infinity, height: height)
),
),
),
);
}
await tester.pumpWidget(buildFrame(600.0));
await tester.pumpAndSettle();
expect(find.byType(RawScrollbar), isNot(paints..rect())); // Not shown.

await tester.pumpWidget(buildFrame(600.1));
await tester.pumpAndSettle();
expect(find.byType(RawScrollbar), paints..rect()..rect()); // Show the bar.

await tester.pumpWidget(buildFrame(600.0));
await tester.pumpAndSettle();
expect(find.byType(RawScrollbar), isNot(paints..rect())); // Hide the bar.
});

testWidgets('The bar can show or hide when the window size change', (WidgetTester tester) async {
final ScrollController scrollController = ScrollController();
Widget buildFrame() {
return Directionality(
textDirection: TextDirection.ltr,
child: MediaQuery(
data: const MediaQueryData(),
child: PrimaryScrollController(
controller: scrollController,
child: RawScrollbar(
isAlwaysShown: true,
controller: scrollController,
child: const SingleChildScrollView(
child: SizedBox(
width: double.infinity,
height: 600.0,
),
),
),
),
),
);
}
tester.binding.window.physicalSizeTestValue = const Size(800.0, 600.0);
tester.binding.window.devicePixelRatioTestValue = 1;
addTearDown(tester.binding.window.clearPhysicalSizeTestValue);
addTearDown(tester.binding.window.clearDevicePixelRatioTestValue);

await tester.pumpWidget(buildFrame());
await tester.pumpAndSettle();
expect(scrollController.offset, 0.0);
expect(find.byType(RawScrollbar), isNot(paints..rect())); // Not shown.

tester.binding.window.physicalSizeTestValue = const Size(800.0, 599.0);
await tester.pumpAndSettle();
expect(find.byType(RawScrollbar), paints..rect()..rect()); // Show the bar.

tester.binding.window.physicalSizeTestValue = const Size(800.0, 600.0);
await tester.pumpAndSettle();
expect(find.byType(RawScrollbar), isNot(paints..rect())); // Not shown.
});
}
Loading