// Copyright 2014 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import 'package:flutter/cupertino.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import '../rendering/mock_canvas.dart'; const CupertinoDynamicColor _kScrollbarColor = CupertinoDynamicColor.withBrightness( color: Color(0x59000000), darkColor: Color(0x80FFFFFF), ); void main() { const Duration _kScrollbarTimeToFade = Duration(milliseconds: 1200); const Duration _kScrollbarFadeDuration = Duration(milliseconds: 250); const Duration _kScrollbarResizeDuration = Duration(milliseconds: 100); testWidgets('Scrollbar never goes away until finger lift', (WidgetTester tester) async { await tester.pumpWidget( const Directionality( textDirection: TextDirection.ltr, child: MediaQuery( data: MediaQueryData(), child: CupertinoScrollbar( child: SingleChildScrollView(child: SizedBox(width: 4000.0, height: 4000.0)), ), ), ), ); final TestGesture gesture = await tester.startGesture(tester.getCenter(find.byType(SingleChildScrollView))); await gesture.moveBy(const Offset(0.0, -10.0)); await tester.pump(); // Scrollbar fully showing await tester.pump(const Duration(milliseconds: 500)); expect(find.byType(CupertinoScrollbar), paints..rrect( color: _kScrollbarColor.color, )); await tester.pump(const Duration(seconds: 3)); await tester.pump(const Duration(seconds: 3)); // Still there. expect(find.byType(CupertinoScrollbar), paints..rrect( color: _kScrollbarColor.color, )); await gesture.up(); await tester.pump(_kScrollbarTimeToFade); await tester.pump(_kScrollbarFadeDuration * 0.5); // Opacity going down now. expect(find.byType(CupertinoScrollbar), paints..rrect( color: _kScrollbarColor.color.withAlpha(69), )); }); testWidgets('Scrollbar dark mode', (WidgetTester tester) async { Brightness brightness = Brightness.light; StateSetter setState; await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: StatefulBuilder( builder: (BuildContext context, StateSetter setter) { setState = setter; return MediaQuery( data: MediaQueryData(platformBrightness: brightness), child: const CupertinoScrollbar( child: SingleChildScrollView(child: SizedBox(width: 4000.0, height: 4000.0)), ), ); }, ), ), ); final TestGesture gesture = await tester.startGesture(tester.getCenter(find.byType(SingleChildScrollView))); await gesture.moveBy(const Offset(0.0, 10.0)); await tester.pump(); // Scrollbar fully showing await tester.pumpAndSettle(); expect(find.byType(CupertinoScrollbar), paints..rrect( color: _kScrollbarColor.color, )); setState(() { brightness = Brightness.dark; }); await tester.pump(); expect(find.byType(CupertinoScrollbar), paints..rrect( color: _kScrollbarColor.darkColor, )); }); testWidgets('Scrollbar thumb can be dragged with long press', (WidgetTester tester) async { final ScrollController scrollController = ScrollController(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: MediaQuery( data: const MediaQueryData(), child: PrimaryScrollController( controller: scrollController, child: const CupertinoScrollbar( child: SingleChildScrollView(child: SizedBox(width: 4000.0, height: 4000.0)), ), ), ), ), ); expect(scrollController.offset, 0.0); // Scroll a bit. const double scrollAmount = 10.0; final TestGesture scrollGesture = await tester.startGesture(tester.getCenter(find.byType(SingleChildScrollView))); // Scroll down by swiping up. await scrollGesture.moveBy(const Offset(0.0, -scrollAmount)); await tester.pump(); await tester.pump(const Duration(milliseconds: 500)); // Scrollbar thumb is fully showing and scroll offset has moved by // scrollAmount. expect(find.byType(CupertinoScrollbar), paints..rrect( color: _kScrollbarColor.color, )); expect(scrollController.offset, scrollAmount); await scrollGesture.up(); await tester.pump(); int hapticFeedbackCalls = 0; SystemChannels.platform.setMockMethodCallHandler((MethodCall methodCall) async { if (methodCall.method == 'HapticFeedback.vibrate') { hapticFeedbackCalls++; } }); // Longpress on the scrollbar thumb and expect a vibration after it resizes. expect(hapticFeedbackCalls, 0); final TestGesture dragScrollbarGesture = await tester.startGesture(const Offset(796.0, 50.0)); await tester.pump(const Duration(milliseconds: 100)); expect(hapticFeedbackCalls, 0); await tester.pump(_kScrollbarResizeDuration); // Allow the haptic feedback some slack. await tester.pump(const Duration(milliseconds: 1)); expect(hapticFeedbackCalls, 1); // Drag the thumb down to scroll down. await dragScrollbarGesture.moveBy(const Offset(0.0, scrollAmount)); await tester.pump(const Duration(milliseconds: 100)); await dragScrollbarGesture.up(); await tester.pumpAndSettle(); // The view has scrolled more than it would have by a swipe gesture of the // same distance. expect(scrollController.offset, greaterThan(scrollAmount * 2)); // The scrollbar thumb is still fully visible. expect(find.byType(CupertinoScrollbar), paints..rrect( color: _kScrollbarColor.color, )); // Let the thumb fade out so all timers have resolved. await tester.pump(_kScrollbarTimeToFade); await tester.pump(_kScrollbarFadeDuration); }); }