modal_bottom_sheet_test.dart 7.37 KB
Newer Older
Hixie's avatar
Hixie committed
1 2 3 4
// Copyright 2015 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.

Adam Barth's avatar
Adam Barth committed
5
import 'package:flutter_test/flutter_test.dart';
6 7
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
8
import 'package:flutter/gestures.dart';
9 10

void main() {
11
  testWidgets('Verify that a tap dismisses a modal BottomSheet', (WidgetTester tester) async {
12 13
    BuildContext savedContext;

14
    await tester.pumpWidget(new MaterialApp(
15 16 17 18 19 20 21 22
      home: new Builder(
        builder: (BuildContext context) {
          savedContext = context;
          return new Container();
        }
      )
    ));

23
    await tester.pump();
24 25
    expect(find.text('BottomSheet'), findsNothing);

26
    bool showBottomSheetThenCalled = false;
27
    showModalBottomSheet<Null>(
28
      context: savedContext,
29
      builder: (BuildContext context) => const Text('BottomSheet')
30
    ).then<void>((Null result) {
31
      expectSync(result, isNull);
32
      showBottomSheetThenCalled = true;
33
    });
34

35
    await tester.pump(); // bottom sheet show animation starts
36
    await tester.pump(const Duration(seconds: 1)); // animation done
37 38 39
    expect(find.text('BottomSheet'), findsOneWidget);
    expect(showBottomSheetThenCalled, isFalse);

40
    // Tap on the bottom sheet itself to dismiss it
41 42
    await tester.tap(find.text('BottomSheet'));
    await tester.pump(); // bottom sheet dismiss animation starts
43
    expect(showBottomSheetThenCalled, isTrue);
44 45
    await tester.pump(const Duration(seconds: 1)); // last frame of animation (sheet is entirely off-screen, but still present)
    await tester.pump(const Duration(seconds: 1)); // frame after the animation (sheet has been removed)
46 47
    expect(find.text('BottomSheet'), findsNothing);

48
    showBottomSheetThenCalled = false;
49
    showModalBottomSheet<Null>(
50
      context: savedContext,
51
      builder: (BuildContext context) => const Text('BottomSheet'),
52
    ).then<void>((Null result) {
53 54 55
      expectSync(result, isNull);
      showBottomSheetThenCalled = true;
    });
56
    await tester.pump(); // bottom sheet show animation starts
57
    await tester.pump(const Duration(seconds: 1)); // animation done
58
    expect(find.text('BottomSheet'), findsOneWidget);
59
    expect(showBottomSheetThenCalled, isFalse);
60

61
    // Tap above the bottom sheet to dismiss it
62
    await tester.tapAt(const Offset(20.0, 20.0));
63
    await tester.pump(); // bottom sheet dismiss animation starts
64
    expect(showBottomSheetThenCalled, isTrue);
65 66
    await tester.pump(const Duration(seconds: 1)); // animation done
    await tester.pump(const Duration(seconds: 1)); // rebuild frame
67 68
    expect(find.text('BottomSheet'), findsNothing);
  });
69

70
  testWidgets('Verify that a downwards fling dismisses a persistent BottomSheet', (WidgetTester tester) async {
71
    final GlobalKey<ScaffoldState> scaffoldKey = new GlobalKey<ScaffoldState>();
72 73
    bool showBottomSheetThenCalled = false;

74
    await tester.pumpWidget(new MaterialApp(
75 76
      home: new Scaffold(
        key: scaffoldKey,
77
        body: const Center(child: Text('body'))
78 79 80 81 82 83
      )
    ));

    expect(showBottomSheetThenCalled, isFalse);
    expect(find.text('BottomSheet'), findsNothing);

84
    scaffoldKey.currentState.showBottomSheet<Null>((BuildContext context) {
85
      return new Container(
86
        margin: const EdgeInsets.all(40.0),
87
        child: const Text('BottomSheet')
88
      );
89
    }).closed.whenComplete(() {
90 91
      showBottomSheetThenCalled = true;
    });
92

93 94
    expect(showBottomSheetThenCalled, isFalse);
    expect(find.text('BottomSheet'), findsNothing);
95

96
    await tester.pump(); // bottom sheet show animation starts
97

98 99
    expect(showBottomSheetThenCalled, isFalse);
    expect(find.text('BottomSheet'), findsOneWidget);
100

101
    await tester.pump(const Duration(seconds: 1)); // animation done
102

103 104
    expect(showBottomSheetThenCalled, isFalse);
    expect(find.text('BottomSheet'), findsOneWidget);
105

106 107 108 109 110
    // The fling below must be such that the velocity estimation examines an
    // offset greater than the kTouchSlop. Too slow or too short a distance, and
    // it won't trigger. Also, it musn't be so much that it drags the bottom
    // sheet off the screen, or we won't see it after we pump!
    await tester.fling(find.text('BottomSheet'), const Offset(0.0, 50.0), 2000.0);
111
    await tester.pump(); // drain the microtask queue (Future completion callback)
112

113 114
    expect(showBottomSheetThenCalled, isTrue);
    expect(find.text('BottomSheet'), findsOneWidget);
115

116
    await tester.pump(); // bottom sheet dismiss animation starts
117

118 119
    expect(showBottomSheetThenCalled, isTrue);
    expect(find.text('BottomSheet'), findsOneWidget);
120

121
    await tester.pump(const Duration(seconds: 1)); // animation done
122

123 124
    expect(showBottomSheetThenCalled, isTrue);
    expect(find.text('BottomSheet'), findsNothing);
125 126
  });

127 128
  testWidgets('Verify that dragging past the bottom dismisses a persistent BottomSheet', (WidgetTester tester) async {
    // This is a regression test for https://github.com/flutter/flutter/issues/5528
129
    final GlobalKey<ScaffoldState> scaffoldKey = new GlobalKey<ScaffoldState>();
130 131 132 133

    await tester.pumpWidget(new MaterialApp(
      home: new Scaffold(
        key: scaffoldKey,
134
        body: const Center(child: Text('body'))
135 136 137
      )
    ));

138
    scaffoldKey.currentState.showBottomSheet<Null>((BuildContext context) {
139
      return new Container(
140
        margin: const EdgeInsets.all(40.0),
141
        child: const Text('BottomSheet')
142 143 144 145
      );
    });

    await tester.pump(); // bottom sheet show animation starts
146
    await tester.pump(const Duration(seconds: 1)); // animation done
147 148 149 150 151
    expect(find.text('BottomSheet'), findsOneWidget);

    await tester.fling(find.text('BottomSheet'), const Offset(0.0, 400.0), 1000.0);
    await tester.pump(); // drain the microtask queue (Future completion callback)
    await tester.pump(); // bottom sheet dismiss animation starts
152
    await tester.pump(const Duration(seconds: 1)); // animation done
153 154 155

    expect(find.text('BottomSheet'), findsNothing);
  });
156 157 158 159 160

  testWidgets('modal BottomSheet has no top MediaQuery', (WidgetTester tester) async {
    BuildContext outerContext;
    BuildContext innerContext;

161 162
    await tester.pumpWidget(new Localizations(
      locale: const Locale('en', 'US'),
163
      delegates: const <LocalizationsDelegate<dynamic>>[
164 165 166 167 168 169 170
        DefaultWidgetsLocalizations.delegate,
        DefaultMaterialLocalizations.delegate,
      ],
      child: new Directionality(
        textDirection: TextDirection.ltr,
        child: new MediaQuery(
          data: const MediaQueryData(
171
            padding: EdgeInsets.all(50.0),
172 173 174
          ),
          child: new Navigator(
            onGenerateRoute: (_) {
175
              return new PageRouteBuilder<void>(
176 177 178 179 180 181 182
                pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
                  outerContext = context;
                  return new Container();
                },
              );
            },
          ),
183 184 185 186
        ),
      ),
    ));

187
    showModalBottomSheet<void>(
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
      context: outerContext,
      builder: (BuildContext context) {
        innerContext = context;
        return new Container();
      },
    );
    await tester.pump();
    await tester.pump(const Duration(seconds: 1));

    expect(
      MediaQuery.of(outerContext).padding,
      const EdgeInsets.all(50.0),
    );
    expect(
      MediaQuery.of(innerContext).padding,
      const EdgeInsets.only(left: 50.0, right: 50.0, bottom: 50.0),
    );
  });
206
}