scrollbar_test.dart 6.59 KB
Newer Older
1 2 3 4
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
import 'package:flutter/cupertino.dart';
6 7 8 9
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter_test/flutter_test.dart';

10 11
import '../rendering/mock_canvas.dart';

12 13 14 15 16 17 18 19 20 21 22
class TestCanvas implements Canvas {
  TestCanvas([this.invocations]);

  final List<Invocation> invocations;

  @override
  void noSuchMethod(Invocation invocation) {
    invocations?.add(invocation);
  }
}

23
Widget _buildBoilerplate({
24 25 26
  TextDirection textDirection = TextDirection.ltr,
  EdgeInsets padding = EdgeInsets.zero,
  Widget child,
27 28 29 30 31 32 33 34 35 36
}) {
  return Directionality(
    textDirection: textDirection,
    child: MediaQuery(
      data: MediaQueryData(padding: padding),
      child: child,
    ),
  );
}

37 38
void main() {
  testWidgets('Scrollbar doesn\'t show when tapping list', (WidgetTester tester) async {
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
    await tester.pumpWidget(
      _buildBoilerplate(
        child: Center(
          child: Container(
            decoration: BoxDecoration(
              border: Border.all(color: const Color(0xFFFFFF00))
            ),
            height: 200.0,
            width: 300.0,
            child: Scrollbar(
              child: ListView(
                children: <Widget>[
                  Container(height: 40.0, child: const Text('0')),
                  Container(height: 40.0, child: const Text('1')),
                  Container(height: 40.0, child: const Text('2')),
                  Container(height: 40.0, child: const Text('3')),
                  Container(height: 40.0, child: const Text('4')),
                  Container(height: 40.0, child: const Text('5')),
                  Container(height: 40.0, child: const Text('6')),
                  Container(height: 40.0, child: const Text('7')),
                ],
              ),
61 62
            ),
          ),
63 64
        ),
      ),
65
    );
66 67

    SchedulerBinding.instance.debugAssertNoTransientCallbacks('Building a list with a scrollbar triggered an animation.');
68
    await tester.tap(find.byType(ListView));
69 70 71 72 73
    SchedulerBinding.instance.debugAssertNoTransientCallbacks('Tapping a block with a scrollbar triggered an animation.');
    await tester.pump(const Duration(milliseconds: 200));
    await tester.pump(const Duration(milliseconds: 200));
    await tester.pump(const Duration(milliseconds: 200));
    await tester.pump(const Duration(milliseconds: 200));
74
    await tester.drag(find.byType(ListView), const Offset(0.0, -10.0));
75 76 77 78 79 80
    expect(SchedulerBinding.instance.transientCallbackCount, greaterThan(0));
    await tester.pump(const Duration(milliseconds: 200));
    await tester.pump(const Duration(milliseconds: 200));
    await tester.pump(const Duration(milliseconds: 200));
    await tester.pump(const Duration(milliseconds: 200));
  });
81 82

  testWidgets('ScrollbarPainter does not divide by zero', (WidgetTester tester) async {
83 84
    await tester.pumpWidget(
      _buildBoilerplate(child: Container(
85 86
        height: 200.0,
        width: 300.0,
87 88
        child: Scrollbar(
          child: ListView(
89
            children: <Widget>[
90
              Container(height: 40.0, child: const Text('0')),
91 92 93
            ],
          ),
        ),
94
      )),
95
    );
96

97 98
    final CustomPaint custom = tester.widget(find.descendant(
      of: find.byType(Scrollbar),
99 100
      matching: find.byType(CustomPaint),
    ).first);
101
    final dynamic scrollPainter = custom.foregroundPainter;
102 103 104 105 106
    // Dragging makes the scrollbar first appear.
    await tester.drag(find.text('0'), const Offset(0.0, -10.0));
    await tester.pump(const Duration(milliseconds: 200));
    await tester.pump(const Duration(milliseconds: 200));

107
    final ScrollMetrics metrics = FixedScrollMetrics(
108 109 110 111
      minScrollExtent: 0.0,
      maxScrollExtent: 0.0,
      pixels: 0.0,
      viewportDimension: 100.0,
112
      axisDirection: AxisDirection.down,
113 114 115 116
    );
    scrollPainter.update(metrics, AxisDirection.down);

    final List<Invocation> invocations = <Invocation>[];
117
    final TestCanvas canvas = TestCanvas(invocations);
118
    scrollPainter.paint(canvas, const Size(10.0, 100.0));
119 120 121

    // Scrollbar is not supposed to draw anything if there isn't enough content.
    expect(invocations.isEmpty, isTrue);
122
  });
123 124 125

  testWidgets('Adaptive scrollbar', (WidgetTester tester) async {
    Widget viewWithScroll(TargetPlatform platform) {
126
      return _buildBoilerplate(
127 128
        child: Theme(
          data: ThemeData(
129 130
            platform: platform
          ),
131
          child: const Scrollbar(
132
            child: SingleChildScrollView(
133
              child: SizedBox(width: 4000.0, height: 4000.0),
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
            ),
          ),
        ),
      );
    }

    await tester.pumpWidget(viewWithScroll(TargetPlatform.android));
    await tester.drag(find.byType(SingleChildScrollView), const Offset(0.0, -10.0));
    await tester.pump();
    // Scrollbar fully showing
    await tester.pump(const Duration(milliseconds: 500));
    expect(find.byType(Scrollbar), paints..rect());

    await tester.pumpWidget(viewWithScroll(TargetPlatform.iOS));
    final TestGesture gesture = await tester.startGesture(
      tester.getCenter(find.byType(SingleChildScrollView))
    );
    await gesture.moveBy(const Offset(0.0, -10.0));
    await tester.drag(find.byType(SingleChildScrollView), const Offset(0.0, -10.0));
    await tester.pump();
    await tester.pump(const Duration(milliseconds: 200));
155
    expect(find.byType(CupertinoScrollbar), paints..rrect());
156
  });
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188

  testWidgets('Scrollbar passes controller to CupertinoScrollbar', (WidgetTester tester) async {
    final ScrollController controller = ScrollController();
    Widget viewWithScroll(TargetPlatform platform) {
      return _buildBoilerplate(
        child: Theme(
          data: ThemeData(
            platform: platform
          ),
          child: Scrollbar(
            controller: controller,
            child: const SingleChildScrollView(
              child: SizedBox(width: 4000.0, height: 4000.0),
            ),
          ),
        ),
      );
    }

    await tester.pumpWidget(viewWithScroll(TargetPlatform.iOS));
    final TestGesture gesture = await tester.startGesture(
      tester.getCenter(find.byType(SingleChildScrollView))
    );
    await gesture.moveBy(const Offset(0.0, -10.0));
    await tester.drag(find.byType(SingleChildScrollView), const Offset(0.0, -10.0));
    await tester.pump();
    await tester.pump(const Duration(milliseconds: 200));
    expect(find.byType(CupertinoScrollbar), paints..rrect());
    final CupertinoScrollbar scrollbar = find.byType(CupertinoScrollbar).evaluate().first.widget;
    expect(scrollbar.controller, isNotNull);
  });

189
}