From 61e938aa1ef453ea1a665b4165705e6d9e83ced3 Mon Sep 17 00:00:00 2001 From: Jason Simmons Date: Wed, 24 May 2017 09:52:09 -0700 Subject: [PATCH] Avoid divide by zero in scroll thumb rectangle calculations (#10271) --- .../flutter/lib/src/material/scrollbar.dart | 9 +++- .../flutter/test/material/scrollbar_test.dart | 46 +++++++++++++++++++ 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/packages/flutter/lib/src/material/scrollbar.dart b/packages/flutter/lib/src/material/scrollbar.dart index 4845c31795..6c0859f170 100644 --- a/packages/flutter/lib/src/material/scrollbar.dart +++ b/packages/flutter/lib/src/material/scrollbar.dart @@ -156,8 +156,13 @@ class _ScrollbarPainter extends ChangeNotifier implements CustomPainter { void _paintThumb(double before, double inside, double after, double viewport, Canvas canvas, Size size, void painter(Canvas canvas, Size size, double thumbOffset, double thumbExtent)) { - final double thumbExtent = math.max(math.min(viewport, _kMinThumbExtent), viewport * inside / (before + inside + after)); - final double thumbOffset = before * (viewport - thumbExtent) / (before + after); + double thumbExtent = math.min(viewport, _kMinThumbExtent); + if (before + inside + after > 0.0) + thumbExtent = math.max(thumbExtent, viewport * inside / (before + inside + after)); + + final double thumbOffset = (before + after > 0.0) ? + before * (viewport - thumbExtent) / (before + after) : 0.0; + painter(canvas, size, thumbOffset, thumbExtent); } diff --git a/packages/flutter/test/material/scrollbar_test.dart b/packages/flutter/test/material/scrollbar_test.dart index da2dc088a5..a9fbb34d0b 100644 --- a/packages/flutter/test/material/scrollbar_test.dart +++ b/packages/flutter/test/material/scrollbar_test.dart @@ -6,6 +6,17 @@ import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:flutter_test/flutter_test.dart'; +class TestCanvas implements Canvas { + TestCanvas([this.invocations]); + + final List invocations; + + @override + void noSuchMethod(Invocation invocation) { + invocations?.add(invocation); + } +} + void main() { testWidgets('Scrollbar doesn\'t show when tapping list', (WidgetTester tester) async { await tester.pumpWidget( @@ -48,4 +59,39 @@ void main() { await tester.pump(const Duration(milliseconds: 200)); await tester.pump(const Duration(milliseconds: 200)); }); + + testWidgets('ScrollbarPainter does not divide by zero', (WidgetTester tester) async { + await tester.pumpWidget( + new Container( + height: 200.0, + width: 300.0, + child: new Scrollbar( + child: new ListView( + children: [ + new Container(height: 40.0, child: const Text('0')), + ] + ) + ) + ) + ); + + final CustomPaint custom = tester.widget(find.descendant(of: find.byType(Scrollbar), matching: find.byType(CustomPaint)).first); + final dynamic scrollPainter = custom.foregroundPainter; + final ScrollMetrics metrics = new FixedScrollMetrics( + minScrollExtent: 0.0, + maxScrollExtent: 0.0, + pixels: 0.0, + viewportDimension: 100.0, + axisDirection: AxisDirection.down + ); + scrollPainter.update(metrics, AxisDirection.down); + await tester.pump(const Duration(milliseconds: 200)); + await tester.pump(const Duration(milliseconds: 200)); + + final List invocations = []; + final TestCanvas canvas = new TestCanvas(invocations); + scrollPainter.paint(canvas, const Size(10.0, 100.0)); + final Rect thumbRect = invocations.single.positionalArguments[0]; + expect(thumbRect.isFinite, isTrue); + }); }