From 1dd3f5d45bffbb8e929a50e2a26bb8febcbcdfac Mon Sep 17 00:00:00 2001 From: Flop <38378650+hgraceb@users.noreply.github.com> Date: Fri, 31 Jan 2025 01:33:05 +0800 Subject: [PATCH] Fix unexpected shown of Scrollbar (#159386) Fixes [Narrow DatePickerDialog has unnecessary scrollbar on months with 6 rows #141348](https://github.com/flutter/flutter/issues/141348) ## Details `288 / 7 + 288 / 7 * 6 = 288.00000000000006` ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [ ] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [ ] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md --- .../flutter/lib/src/widgets/scrollbar.dart | 13 +++--- .../flutter/test/widgets/scrollbar_test.dart | 44 ++++++++++++++++++- 2 files changed, 50 insertions(+), 7 deletions(-) diff --git a/packages/flutter/lib/src/widgets/scrollbar.dart b/packages/flutter/lib/src/widgets/scrollbar.dart index 7246375a97..0eb3285bc4 100644 --- a/packages/flutter/lib/src/widgets/scrollbar.dart +++ b/packages/flutter/lib/src/widgets/scrollbar.dart @@ -517,9 +517,7 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter { _lastMetrics = metrics; _lastAxisDirection = axisDirection; - bool needPaint(ScrollMetrics? metrics) => - metrics != null && metrics.maxScrollExtent > metrics.minScrollExtent; - if (!needPaint(oldMetrics) && !needPaint(metrics)) { + if (!_needPaint(oldMetrics) && !_needPaint(metrics)) { return; } notifyListeners(); @@ -537,6 +535,11 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter { return Paint()..color = color.withOpacity(color.opacity * fadeoutOpacityAnimation.value); } + bool _needPaint(ScrollMetrics? metrics) { + return metrics != null && + metrics.maxScrollExtent - metrics.minScrollExtent > precisionErrorTolerance; + } + Paint _paintTrack({bool isBorder = false}) { if (isBorder) { return Paint() @@ -629,9 +632,7 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter { @override void paint(Canvas canvas, Size size) { - if (_lastAxisDirection == null || - _lastMetrics == null || - _lastMetrics!.maxScrollExtent <= _lastMetrics!.minScrollExtent) { + if (_lastAxisDirection == null || !_needPaint(_lastMetrics)) { return; } // Skip painting if there's not enough space. diff --git a/packages/flutter/test/widgets/scrollbar_test.dart b/packages/flutter/test/widgets/scrollbar_test.dart index df22d64b88..ea3a2ee698 100644 --- a/packages/flutter/test/widgets/scrollbar_test.dart +++ b/packages/flutter/test/widgets/scrollbar_test.dart @@ -5,8 +5,8 @@ import 'dart:ui' as ui; import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; import 'package:flutter/src/physics/utils.dart' show nearEqual; -import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; const Color _kScrollbarColor = Color(0xFF123456); @@ -3484,4 +3484,46 @@ The provided ScrollController cannot be shared by multiple ScrollView widgets.'' expect(verticalScrollController.offset, greaterThan(0.0)); expect(horizontalScrollController.offset, greaterThan(0.0)); }); + + // Regression test for https://github.com/flutter/flutter/issues/141348. + testWidgets( + 'Scrollbar should not shown due to precision error on desktop', + (WidgetTester tester) async { + Widget buildFrame(Size size) { + tester.view.physicalSize = size; + tester.view.devicePixelRatio = 1.0; + addTearDown(tester.view.reset); + return MaterialApp( + home: Scaffold( + body: Center( + child: DatePickerDialog( + initialDate: DateTime(2020, DateTime.may), // Month with six rows. + firstDate: DateTime(2010), + lastDate: DateTime(2030), + ), + ), + ), + ); + } + + const Size screenSizePortrait = Size(400, 600); + await tester.pumpWidget(buildFrame(screenSizePortrait)); + await tester.pumpAndSettle(); + + // Scrollbar is not shown. + expect(find.byType(Scrollbar), findsOneWidget); + expect(find.byType(Scrollbar), isNot(paints..rect())); + + // Scroll on the Scrollbar. + final TestPointer pointer = TestPointer(1, ui.PointerDeviceKind.mouse); + pointer.hover(tester.getCenter(find.byType(Scrollbar))); + await tester.sendEventToBinding(pointer.scroll(const Offset(0.0, 10.0))); + await tester.pumpAndSettle(); + + // Scrollbar is still not shown. + expect(find.byType(Scrollbar), findsOneWidget); + expect(find.byType(Scrollbar), isNot(paints..rect())); + }, + variant: TargetPlatformVariant.desktop(), + ); }