page_forward_transitions_test.dart 8.02 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
Hixie's avatar
Hixie committed
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6
// @dart = 2.8

7
import 'package:flutter_test/flutter_test.dart';
Hixie's avatar
Hixie committed
8
import 'package:flutter/material.dart';
9
import 'package:flutter/rendering.dart';
Hixie's avatar
Hixie committed
10

11
class TestTransition extends AnimatedWidget {
12
  const TestTransition({
Hixie's avatar
Hixie committed
13 14 15
    Key key,
    this.childFirstHalf,
    this.childSecondHalf,
16
    Animation<double> animation,
17
  }) : super(key: key, listenable: animation);
Hixie's avatar
Hixie committed
18 19 20 21

  final Widget childFirstHalf;
  final Widget childSecondHalf;

22
  @override
Hixie's avatar
Hixie committed
23
  Widget build(BuildContext context) {
24
    final Animation<double> animation = listenable as Animation<double>;
25
    if (animation.value >= 0.5)
Hixie's avatar
Hixie committed
26 27 28 29 30
      return childSecondHalf;
    return childFirstHalf;
  }
}

31
class TestRoute<T> extends PageRoute<T> {
32
  TestRoute({ this.child, RouteSettings settings, this.barrierColor }) : super(settings: settings);
33

34
  final Widget child;
35 36

  @override
37
  Duration get transitionDuration => const Duration(milliseconds: 150);
38 39

  @override
40
  final Color barrierColor;
41

42 43 44
  @override
  String get barrierLabel => null;

45 46 47
  @override
  bool get maintainState => false;

48
  @override
49
  Widget buildPage(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
50 51
    return child;
  }
52 53
}

Hixie's avatar
Hixie committed
54
void main() {
55 56
  const Duration kTwoTenthsOfTheTransitionDuration = Duration(milliseconds: 30);
  const Duration kFourTenthsOfTheTransitionDuration = Duration(milliseconds: 60);
Hixie's avatar
Hixie committed
57

58
  testWidgets('Check onstage/offstage handling around transitions', (WidgetTester tester) async {
59

60
    final GlobalKey insideKey = GlobalKey();
61

62
    String state({ bool skipOffstage = true }) {
63
      String result = '';
64
      if (tester.any(find.text('A', skipOffstage: skipOffstage)))
65
        result += 'A';
66
      if (tester.any(find.text('B', skipOffstage: skipOffstage)))
67
        result += 'B';
68
      if (tester.any(find.text('C', skipOffstage: skipOffstage)))
69
        result += 'C';
70
      if (tester.any(find.text('D', skipOffstage: skipOffstage)))
71
        result += 'D';
72
      if (tester.any(find.text('E', skipOffstage: skipOffstage)))
73
        result += 'E';
74
      if (tester.any(find.text('F', skipOffstage: skipOffstage)))
75
        result += 'F';
76
      if (tester.any(find.text('G', skipOffstage: skipOffstage)))
77 78 79 80
        result += 'G';
      return result;
    }

81
    await tester.pumpWidget(
82
      MaterialApp(
83 84 85
        onGenerateRoute: (RouteSettings settings) {
          switch (settings.name) {
            case '/':
86
              return TestRoute<void>(
87
                settings: settings,
88
                child: Builder(
89 90
                  key: insideKey,
                  builder: (BuildContext context) {
91
                    final PageRoute<void> route = ModalRoute.of(context) as PageRoute<void>;
92
                    return Column(
93
                      children: <Widget>[
94
                        TestTransition(
95 96
                          childFirstHalf: const Text('A'),
                          childSecondHalf: const Text('B'),
97
                          animation: route.animation,
98
                        ),
99
                        TestTransition(
100 101
                          childFirstHalf: const Text('C'),
                          childSecondHalf: const Text('D'),
102
                          animation: route.secondaryAnimation,
103
                        ),
104
                      ],
105
                    );
106 107
                  },
                ),
108
              );
109 110 111
            case '/2': return TestRoute<void>(settings: settings, child: const Text('E'));
            case '/3': return TestRoute<void>(settings: settings, child: const Text('F'));
            case '/4': return TestRoute<void>(settings: settings, child: const Text('G'));
Hixie's avatar
Hixie committed
112
          }
113
          return null;
114 115 116
        }
      )
    );
Hixie's avatar
Hixie committed
117

118
    final NavigatorState navigator = insideKey.currentContext.findAncestorStateOfType<NavigatorState>();
Hixie's avatar
Hixie committed
119

120
    expect(state(), equals('BC')); // transition ->1 is at 1.0
Hixie's avatar
Hixie committed
121

122
    navigator.pushNamed('/2');
123
    expect(state(), equals('BC')); // transition 1->2 is not yet built
124
    await tester.pump();
125 126
    expect(state(), equals('BC')); // transition 1->2 is at 0.0
    expect(state(skipOffstage: false), equals('BCE')); // E is offstage
Hixie's avatar
Hixie committed
127

128
    await tester.pump(kFourTenthsOfTheTransitionDuration);
129
    expect(state(), equals('BCE')); // transition 1->2 is at 0.4
Hixie's avatar
Hixie committed
130

131
    await tester.pump(kFourTenthsOfTheTransitionDuration);
132
    expect(state(), equals('BDE')); // transition 1->2 is at 0.8
Hixie's avatar
Hixie committed
133

134
    await tester.pump(kFourTenthsOfTheTransitionDuration);
135
    expect(state(), equals('E')); // transition 1->2 is at 1.0
136
    expect(state(skipOffstage: false), equals('E')); // B and C are gone, the route is inactive with maintainState=false
Hixie's avatar
Hixie committed
137

138
    navigator.pop();
139
    expect(state(), equals('E')); // transition 1<-2 is at 1.0, just reversed
140
    await tester.pump();
Hans Muller's avatar
Hans Muller committed
141 142
    await tester.pump();

143
    expect(state(), equals('BDE')); // transition 1<-2 is at 1.0
Hixie's avatar
Hixie committed
144

145
    await tester.pump(kFourTenthsOfTheTransitionDuration);
146
    expect(state(), equals('BDE')); // transition 1<-2 is at 0.6
Hixie's avatar
Hixie committed
147

148
    navigator.pushNamed('/3');
149
    expect(state(), equals('BDE')); // transition 1<-2 is at 0.6
150
    await tester.pump();
151 152
    expect(state(), equals('BDE')); // transition 1<-2 is at 0.6, 1->3 is at 0.0
    expect(state(skipOffstage: false), equals('BDEF')); // F is offstage since we're at 0.0
Hixie's avatar
Hixie committed
153

154
    await tester.pump(kFourTenthsOfTheTransitionDuration);
155
    expect(state(), equals('BCEF')); // transition 1<-2 is at 0.2, 1->3 is at 0.4
156
    expect(state(skipOffstage: false), equals('BCEF')); // nothing secret going on here
Hixie's avatar
Hixie committed
157

158
    await tester.pump(kFourTenthsOfTheTransitionDuration);
159
    expect(state(), equals('BDF')); // transition 1<-2 is done, 1->3 is at 0.8
Hixie's avatar
Hixie committed
160

161
    navigator.pop();
162
    expect(state(), equals('BDF')); // transition 1<-3 is at 0.8, just reversed
163
    await tester.pump();
164
    expect(state(), equals('BDF')); // transition 1<-3 is at 0.8
Hixie's avatar
Hixie committed
165

166
    await tester.pump(kTwoTenthsOfTheTransitionDuration); // notice that dT=0.2 here, not 0.4
167
    expect(state(), equals('BDF')); // transition 1<-3 is at 0.6
Hixie's avatar
Hixie committed
168

169
    await tester.pump(kFourTenthsOfTheTransitionDuration);
170
    expect(state(), equals('BCF')); // transition 1<-3 is at 0.2
Hixie's avatar
Hixie committed
171

172
    navigator.pushNamed('/4');
173
    expect(state(), equals('BCF')); // transition 1<-3 is at 0.2, 1->4 is not yet built
174
    await tester.pump();
175 176
    expect(state(), equals('BCF')); // transition 1<-3 is at 0.2, 1->4 is at 0.0
    expect(state(skipOffstage: false), equals('BCFG')); // G is offstage
Hixie's avatar
Hixie committed
177

178
    await tester.pump(kFourTenthsOfTheTransitionDuration);
179
    expect(state(), equals('BCG')); // transition 1<-3 is done, 1->4 is at 0.4
Hixie's avatar
Hixie committed
180

181
    await tester.pump(kFourTenthsOfTheTransitionDuration);
182
    expect(state(), equals('BDG')); // transition 1->4 is at 0.8
Hixie's avatar
Hixie committed
183

184
    await tester.pump(kFourTenthsOfTheTransitionDuration);
185
    expect(state(), equals('G')); // transition 1->4 is done
186
    expect(state(skipOffstage: false), equals('G')); // route 1 is not around any more
Hixie's avatar
Hixie committed
187 188

  });
189 190 191

  testWidgets('Check onstage/offstage handling of barriers around transitions', (WidgetTester tester) async {
    await tester.pumpWidget(
192
      MaterialApp(
193 194
        onGenerateRoute: (RouteSettings settings) {
          switch (settings.name) {
195 196
            case '/': return TestRoute<void>(settings: settings, child: const Text('A'));
            case '/1': return TestRoute<void>(settings: settings, barrierColor: const Color(0xFFFFFF00), child: const Text('B'));
197
          }
198
          return null;
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
        }
      )
    );
    expect(find.byType(ModalBarrier), findsOneWidget);

    tester.state<NavigatorState>(find.byType(Navigator)).pushNamed('/1');
    expect(find.byType(ModalBarrier), findsOneWidget);

    await tester.pump();
    expect(find.byType(ModalBarrier), findsNWidgets(2));
    expect(tester.widget<ModalBarrier>(find.byType(ModalBarrier).first).color, isNull);
    expect(tester.widget<ModalBarrier>(find.byType(ModalBarrier).last).color, isNull);

    await tester.pump(const Duration(seconds: 1));
    expect(find.byType(ModalBarrier), findsOneWidget);
    expect(tester.widget<ModalBarrier>(find.byType(ModalBarrier)).color, const Color(0xFFFFFF00));

  });
Hixie's avatar
Hixie committed
217
}