mirror of
https://github.com/flutter/flutter
synced 2024-10-14 04:02:56 +00:00
buildForwardTransition()
For those times when you want to do something as you move away from a route into the next one, as well as when you move into it from the previous one.
This commit is contained in:
parent
9d86ac5e35
commit
bc5307f5af
|
@ -8,6 +8,7 @@ import 'dart:async';
|
|||
import 'dart:math' as math;
|
||||
import 'dart:ui' as ui;
|
||||
|
||||
import 'package:flutter/animation.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/painting.dart';
|
||||
|
|
|
@ -146,13 +146,22 @@ class StockHomeState extends State<StockHome> {
|
|||
}
|
||||
|
||||
Widget buildToolBar() {
|
||||
PageRoute page = ModalRoute.of(context);
|
||||
return new ToolBar(
|
||||
elevation: 0,
|
||||
left: new IconButton(
|
||||
icon: "navigation/menu",
|
||||
onPressed: _showDrawer
|
||||
),
|
||||
center: new Text('Stocks'),
|
||||
center: new FadeTransition(
|
||||
opacity: new AnimatedValue<double>(
|
||||
1.0,
|
||||
end: 0.0,
|
||||
curve: const Interval(0.0, 0.5)
|
||||
),
|
||||
performance: page.forwardPerformance,
|
||||
child: new Text('Stocks')
|
||||
),
|
||||
right: <Widget>[
|
||||
new IconButton(
|
||||
icon: "action/search",
|
||||
|
|
|
@ -53,6 +53,7 @@ class StockSymbolPage extends StatelessComponent {
|
|||
final Stock stock;
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
PageRoute page = ModalRoute.of(context);
|
||||
return new Scaffold(
|
||||
toolBar: new ToolBar(
|
||||
left: new IconButton(
|
||||
|
@ -61,7 +62,15 @@ class StockSymbolPage extends StatelessComponent {
|
|||
Navigator.pop(context);
|
||||
}
|
||||
),
|
||||
center: new Text(stock.name)
|
||||
center: new FadeTransition(
|
||||
opacity: new AnimatedValue<double>(
|
||||
0.0,
|
||||
end: 1.0,
|
||||
curve: const Interval(0.5, 1.0)
|
||||
),
|
||||
performance: page.performance,
|
||||
child: new Text(stock.name)
|
||||
)
|
||||
),
|
||||
body: new Block(<Widget>[
|
||||
new Container(
|
||||
|
|
|
@ -45,6 +45,7 @@ class Interval implements Curve {
|
|||
assert(start <= 1.0);
|
||||
assert(end >= 0.0);
|
||||
assert(end <= 1.0);
|
||||
assert(end >= start);
|
||||
t = ((t - start) / (end - start)).clamp(0.0, 1.0);
|
||||
if (t == 0.0 || t == 1.0)
|
||||
return t;
|
||||
|
|
|
@ -109,12 +109,10 @@ class ReversePerformance extends PerformanceView {
|
|||
|
||||
final List<PerformanceStatusListener> _statusListeners = new List<PerformanceStatusListener>();
|
||||
|
||||
/// Calls listener every time the status of this performance changes
|
||||
void addStatusListener(PerformanceStatusListener listener) {
|
||||
_statusListeners.add(listener);
|
||||
}
|
||||
|
||||
/// Stops calling the listener every time the status of this performance changes
|
||||
void removeStatusListener(PerformanceStatusListener listener) {
|
||||
_statusListeners.remove(listener);
|
||||
}
|
||||
|
@ -148,6 +146,217 @@ class ReversePerformance extends PerformanceView {
|
|||
}
|
||||
}
|
||||
|
||||
enum _TrainHoppingMode { minimize, maximize }
|
||||
|
||||
/// This performance starts by proxying one performance, but can be given a
|
||||
/// second performance. When their times cross (either because the second is
|
||||
/// going in the opposite direction, or because the one overtakes the other),
|
||||
/// the performance hops over to proxying the second performance, and the second
|
||||
/// performance becomes the new "first" performance.
|
||||
class TrainHoppingPerformance extends PerformanceView {
|
||||
TrainHoppingPerformance(this._currentTrain, this._nextTrain, { this.onSwitchedTrain }) {
|
||||
assert(_currentTrain != null);
|
||||
if (_nextTrain != null) {
|
||||
|
||||
if (_currentTrain.progress > _nextTrain.progress) {
|
||||
_mode = _TrainHoppingMode.maximize;
|
||||
} else {
|
||||
_mode = _TrainHoppingMode.minimize;
|
||||
if (_currentTrain.progress == _nextTrain.progress) {
|
||||
_currentTrain = _nextTrain;
|
||||
_nextTrain = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
_currentTrain.addStatusListener(_statusChangeHandler);
|
||||
_currentTrain.addListener(_valueChangeHandler);
|
||||
if (_nextTrain != null)
|
||||
_nextTrain.addListener(_valueChangeHandler);
|
||||
assert(_mode != null);
|
||||
}
|
||||
|
||||
PerformanceView get currentTrain => _currentTrain;
|
||||
PerformanceView _currentTrain;
|
||||
PerformanceView _nextTrain;
|
||||
_TrainHoppingMode _mode;
|
||||
|
||||
VoidCallback onSwitchedTrain;
|
||||
|
||||
void updateVariable(Animatable variable) {
|
||||
assert(_currentTrain != null);
|
||||
variable.setProgress(progress, curveDirection);
|
||||
}
|
||||
|
||||
final List<VoidCallback> _listeners = new List<VoidCallback>();
|
||||
|
||||
void addListener(VoidCallback listener) {
|
||||
assert(_currentTrain != null);
|
||||
_listeners.add(listener);
|
||||
}
|
||||
|
||||
void removeListener(VoidCallback listener) {
|
||||
assert(_currentTrain != null);
|
||||
_listeners.remove(listener);
|
||||
}
|
||||
|
||||
final List<PerformanceStatusListener> _statusListeners = new List<PerformanceStatusListener>();
|
||||
|
||||
void addStatusListener(PerformanceStatusListener listener) {
|
||||
assert(_currentTrain != null);
|
||||
_statusListeners.add(listener);
|
||||
}
|
||||
|
||||
void removeStatusListener(PerformanceStatusListener listener) {
|
||||
assert(_currentTrain != null);
|
||||
_statusListeners.remove(listener);
|
||||
}
|
||||
|
||||
PerformanceStatus _lastStatus;
|
||||
void _statusChangeHandler(PerformanceStatus status) {
|
||||
assert(_currentTrain != null);
|
||||
if (status != _lastStatus) {
|
||||
List<PerformanceStatusListener> localListeners = new List<PerformanceStatusListener>.from(_statusListeners);
|
||||
for (PerformanceStatusListener listener in localListeners)
|
||||
listener(status);
|
||||
_lastStatus = status;
|
||||
}
|
||||
assert(_lastStatus != null);
|
||||
}
|
||||
|
||||
PerformanceStatus get status => _currentTrain.status;
|
||||
AnimationDirection get direction => _currentTrain.direction;
|
||||
AnimationDirection get curveDirection => _currentTrain.curveDirection;
|
||||
|
||||
double _lastProgress;
|
||||
void _valueChangeHandler() {
|
||||
assert(_currentTrain != null);
|
||||
bool hop = false;
|
||||
if (_nextTrain != null) {
|
||||
switch (_mode) {
|
||||
case _TrainHoppingMode.minimize:
|
||||
hop = _nextTrain.progress <= _currentTrain.progress;
|
||||
break;
|
||||
case _TrainHoppingMode.maximize:
|
||||
hop = _nextTrain.progress >= _currentTrain.progress;
|
||||
break;
|
||||
}
|
||||
if (hop) {
|
||||
_currentTrain.removeStatusListener(_statusChangeHandler);
|
||||
_currentTrain.removeListener(_valueChangeHandler);
|
||||
_currentTrain = _nextTrain;
|
||||
_nextTrain.addListener(_valueChangeHandler);
|
||||
_statusChangeHandler(_nextTrain.status);
|
||||
}
|
||||
}
|
||||
double newProgress = progress;
|
||||
if (newProgress != _lastProgress) {
|
||||
List<VoidCallback> localListeners = new List<VoidCallback>.from(_listeners);
|
||||
for (VoidCallback listener in localListeners)
|
||||
listener();
|
||||
_lastProgress = newProgress;
|
||||
}
|
||||
assert(_lastProgress != null);
|
||||
if (hop && onSwitchedTrain != null)
|
||||
onSwitchedTrain();
|
||||
}
|
||||
|
||||
double get progress => _currentTrain.progress;
|
||||
|
||||
/// Frees all the resources used by this performance.
|
||||
/// After this is called, this object is no longer usable.
|
||||
void dispose() {
|
||||
assert(_currentTrain != null);
|
||||
_currentTrain.removeStatusListener(_statusChangeHandler);
|
||||
_currentTrain.removeListener(_valueChangeHandler);
|
||||
_currentTrain = null;
|
||||
if (_nextTrain != null) {
|
||||
_nextTrain.removeListener(_valueChangeHandler);
|
||||
_nextTrain = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ProxyPerformance extends PerformanceView {
|
||||
ProxyPerformance([PerformanceView performance]) {
|
||||
masterPerformance = performance;
|
||||
}
|
||||
|
||||
PerformanceView get masterPerformance => _masterPerformance;
|
||||
PerformanceView _masterPerformance;
|
||||
void set masterPerformance(PerformanceView value) {
|
||||
if (value == _masterPerformance)
|
||||
return;
|
||||
if (_masterPerformance != null) {
|
||||
_masterPerformance.removeStatusListener(_statusChangeHandler);
|
||||
_masterPerformance.removeListener(_valueChangeHandler);
|
||||
}
|
||||
_masterPerformance = value;
|
||||
if (_masterPerformance != null) {
|
||||
_masterPerformance.addListener(_valueChangeHandler);
|
||||
_masterPerformance.addStatusListener(_statusChangeHandler);
|
||||
_valueChangeHandler();
|
||||
_statusChangeHandler(_masterPerformance.status);
|
||||
}
|
||||
}
|
||||
|
||||
void updateVariable(Animatable variable) {
|
||||
variable.setProgress(progress, curveDirection);
|
||||
}
|
||||
|
||||
final List<VoidCallback> _listeners = new List<VoidCallback>();
|
||||
|
||||
void addListener(VoidCallback listener) {
|
||||
_listeners.add(listener);
|
||||
}
|
||||
|
||||
void removeListener(VoidCallback listener) {
|
||||
_listeners.remove(listener);
|
||||
}
|
||||
|
||||
final List<PerformanceStatusListener> _statusListeners = new List<PerformanceStatusListener>();
|
||||
|
||||
void addStatusListener(PerformanceStatusListener listener) {
|
||||
_statusListeners.add(listener);
|
||||
}
|
||||
|
||||
void removeStatusListener(PerformanceStatusListener listener) {
|
||||
_statusListeners.remove(listener);
|
||||
}
|
||||
|
||||
PerformanceStatus _status = PerformanceStatus.dismissed;
|
||||
AnimationDirection _direction = AnimationDirection.forward;
|
||||
AnimationDirection _curveDirection = AnimationDirection.forward;
|
||||
void _statusChangeHandler(PerformanceStatus status) {
|
||||
assert(_masterPerformance != null);
|
||||
if (status != _status) {
|
||||
_status = status;
|
||||
_direction = _masterPerformance.direction;
|
||||
List<PerformanceStatusListener> localListeners = new List<PerformanceStatusListener>.from(_statusListeners);
|
||||
for (PerformanceStatusListener listener in localListeners)
|
||||
listener(status);
|
||||
}
|
||||
}
|
||||
|
||||
PerformanceStatus get status => _status;
|
||||
AnimationDirection get direction => _direction;
|
||||
AnimationDirection get curveDirection => _curveDirection;
|
||||
|
||||
double _progress = 0.0;
|
||||
void _valueChangeHandler() {
|
||||
assert(_masterPerformance != null);
|
||||
double newProgress = _masterPerformance.progress;
|
||||
if (newProgress != _progress) {
|
||||
_progress = newProgress;
|
||||
_curveDirection = _masterPerformance.curveDirection;
|
||||
List<VoidCallback> localListeners = new List<VoidCallback>.from(_listeners);
|
||||
for (VoidCallback listener in localListeners)
|
||||
listener();
|
||||
}
|
||||
}
|
||||
|
||||
double get progress => _progress;
|
||||
}
|
||||
|
||||
class _RepeatingSimulation extends Simulation {
|
||||
_RepeatingSimulation(this.min, this.max, Duration period)
|
||||
: _periodInSeconds = period.inMicroseconds.toDouble() / Duration.MICROSECONDS_PER_SECOND {
|
||||
|
@ -280,12 +489,10 @@ class Performance extends PerformanceView {
|
|||
|
||||
final List<VoidCallback> _listeners = new List<VoidCallback>();
|
||||
|
||||
/// Calls the listener every time the progress of this performance changes
|
||||
void addListener(VoidCallback listener) {
|
||||
_listeners.add(listener);
|
||||
}
|
||||
|
||||
/// Stop calling the listener every time the progress of this performance changes
|
||||
void removeListener(VoidCallback listener) {
|
||||
_listeners.remove(listener);
|
||||
}
|
||||
|
@ -298,12 +505,10 @@ class Performance extends PerformanceView {
|
|||
|
||||
final List<PerformanceStatusListener> _statusListeners = new List<PerformanceStatusListener>();
|
||||
|
||||
/// Calls listener every time the status of this performance changes
|
||||
void addStatusListener(PerformanceStatusListener listener) {
|
||||
_statusListeners.add(listener);
|
||||
}
|
||||
|
||||
/// Stops calling the listener every time the status of this performance changes
|
||||
void removeStatusListener(PerformanceStatusListener listener) {
|
||||
_statusListeners.remove(listener);
|
||||
}
|
||||
|
|
|
@ -38,6 +38,8 @@ class _MaterialPageTransition extends TransitionWithChild {
|
|||
}
|
||||
}
|
||||
|
||||
const Duration kMaterialPageRouteTransitionDuration = const Duration(milliseconds: 150);
|
||||
|
||||
class MaterialPageRoute<T> extends PageRoute<T> {
|
||||
MaterialPageRoute({
|
||||
this.builder,
|
||||
|
@ -49,7 +51,7 @@ class MaterialPageRoute<T> extends PageRoute<T> {
|
|||
|
||||
final WidgetBuilder builder;
|
||||
|
||||
Duration get transitionDuration => const Duration(milliseconds: 150);
|
||||
Duration get transitionDuration => kMaterialPageRouteTransitionDuration;
|
||||
bool get barrierDismissable => false;
|
||||
Color get barrierColor => Colors.black54;
|
||||
|
||||
|
|
|
@ -72,6 +72,9 @@ abstract class TransitionRoute<T> extends OverlayRoute<T> {
|
|||
/// popped.
|
||||
Future<T> get popped => _popCompleter?.future;
|
||||
final Completer<T> _popCompleter;
|
||||
|
||||
/// This future completes only once the transition itself has finished, after
|
||||
/// the overlay entries have been removed from the navigator's overlay.
|
||||
Future<T> get completed => _transitionCompleter?.future;
|
||||
final Completer<T> _transitionCompleter;
|
||||
|
||||
|
@ -143,6 +146,52 @@ abstract class TransitionRoute<T> extends OverlayRoute<T> {
|
|||
super.dispose();
|
||||
}
|
||||
|
||||
|
||||
final ProxyPerformance forwardPerformance = new ProxyPerformance();
|
||||
|
||||
void didPushNext(Route nextRoute) {
|
||||
if (nextRoute is TransitionRoute) {
|
||||
PerformanceView current = forwardPerformance.masterPerformance;
|
||||
if (current != null) {
|
||||
if (current is TrainHoppingPerformance) {
|
||||
TrainHoppingPerformance newPerformance;
|
||||
newPerformance = new TrainHoppingPerformance(
|
||||
current.currentTrain,
|
||||
nextRoute.performance,
|
||||
onSwitchedTrain: () {
|
||||
assert(forwardPerformance.masterPerformance == newPerformance);
|
||||
assert(newPerformance.currentTrain == nextRoute.performance);
|
||||
forwardPerformance.masterPerformance = newPerformance.currentTrain;
|
||||
newPerformance.dispose();
|
||||
}
|
||||
);
|
||||
forwardPerformance.masterPerformance = newPerformance;
|
||||
current.dispose();
|
||||
} else {
|
||||
forwardPerformance.masterPerformance = new TrainHoppingPerformance(current, nextRoute.performance);
|
||||
}
|
||||
} else {
|
||||
forwardPerformance.masterPerformance = nextRoute.performance;
|
||||
}
|
||||
}
|
||||
super.didPushNext(nextRoute);
|
||||
}
|
||||
|
||||
Widget wrapTransition(BuildContext context, Widget child) {
|
||||
return buildForwardTransition(
|
||||
context,
|
||||
forwardPerformance,
|
||||
buildTransition(
|
||||
context,
|
||||
performance,
|
||||
child
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildTransition(BuildContext context, PerformanceView performance, Widget child) => child;
|
||||
Widget buildForwardTransition(BuildContext context, PerformanceView performance, Widget child) => child;
|
||||
|
||||
String get debugLabel => '$runtimeType';
|
||||
String toString() => '$runtimeType(performance: $_performance)';
|
||||
}
|
||||
|
@ -246,7 +295,7 @@ class _ModalScope extends StatusTransitionComponent {
|
|||
key: new GlobalObjectKey(route),
|
||||
child: new IgnorePointer(
|
||||
ignoring: performance.status == PerformanceStatus.reverse,
|
||||
child: route.buildTransition(context, performance, contents)
|
||||
child: route.wrapTransition(context, contents)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -354,7 +403,7 @@ abstract class ModalRoute<T> extends TransitionRoute<T> with LocalHistoryRoute<T
|
|||
performance: performance,
|
||||
current: isCurrent,
|
||||
route: this
|
||||
// calls buildTransition() and buildPage(), defined above
|
||||
// calls buildTransition()/buildForwardTransition() and buildPage(), defined above
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,11 @@ abstract class TransitionComponent extends StatefulComponent {
|
|||
Widget build(BuildContext context);
|
||||
|
||||
_TransitionState createState() => new _TransitionState();
|
||||
|
||||
void debugFillDescription(List<String> description) {
|
||||
super.debugFillDescription(description);
|
||||
description.add('performance: $performance');
|
||||
}
|
||||
}
|
||||
|
||||
class _TransitionState extends State<TransitionComponent> {
|
||||
|
|
165
packages/unit/test/widget/page_forward_transitions_test.dart
Normal file
165
packages/unit/test/widget/page_forward_transitions_test.dart
Normal file
|
@ -0,0 +1,165 @@
|
|||
// 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.
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter/animation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'test_widgets.dart';
|
||||
|
||||
class TestTransition extends TransitionComponent {
|
||||
TestTransition({
|
||||
Key key,
|
||||
this.childFirstHalf,
|
||||
this.childSecondHalf,
|
||||
PerformanceView performance
|
||||
}) : super(key: key, performance: performance);
|
||||
|
||||
final Widget childFirstHalf;
|
||||
final Widget childSecondHalf;
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
if (performance.progress >= 0.5)
|
||||
return childSecondHalf;
|
||||
return childFirstHalf;
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
final Duration kTwoTenthsOfTheTransitionDuration = kMaterialPageRouteTransitionDuration * 0.2;
|
||||
final Duration kFourTenthsOfTheTransitionDuration = kMaterialPageRouteTransitionDuration * 0.4;
|
||||
|
||||
test('Check onstage/offstage handling around transitions', () {
|
||||
testWidgets((WidgetTester tester) {
|
||||
|
||||
GlobalKey insideKey = new GlobalKey();
|
||||
|
||||
String state() {
|
||||
String result = '';
|
||||
if (tester.findText('A') != null)
|
||||
result += 'A';
|
||||
if (tester.findText('B') != null)
|
||||
result += 'B';
|
||||
if (tester.findText('C') != null)
|
||||
result += 'C';
|
||||
if (tester.findText('D') != null)
|
||||
result += 'D';
|
||||
if (tester.findText('E') != null)
|
||||
result += 'E';
|
||||
if (tester.findText('F') != null)
|
||||
result += 'F';
|
||||
if (tester.findText('G') != null)
|
||||
result += 'G';
|
||||
return result;
|
||||
}
|
||||
|
||||
tester.pumpWidget(
|
||||
new MaterialApp(
|
||||
routes: <String, RouteBuilder>{
|
||||
'/': (RouteArguments args) {
|
||||
return new Builder(
|
||||
key: insideKey,
|
||||
builder: (BuildContext context) {
|
||||
PageRoute route = ModalRoute.of(context);
|
||||
return new Column([
|
||||
new TestTransition(
|
||||
childFirstHalf: new Text('A'),
|
||||
childSecondHalf: new Text('B'),
|
||||
performance: route.performance
|
||||
),
|
||||
new TestTransition(
|
||||
childFirstHalf: new Text('C'),
|
||||
childSecondHalf: new Text('D'),
|
||||
performance: route.forwardPerformance
|
||||
),
|
||||
]);
|
||||
}
|
||||
);
|
||||
},
|
||||
'/2': (RouteArguments args) => new Text('E'),
|
||||
'/3': (RouteArguments args) => new Text('F'),
|
||||
'/4': (RouteArguments args) => new Text('G'),
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// TODO(ianh): Remove the first part of this test once the first page doesn't animate in
|
||||
|
||||
NavigatorState navigator = insideKey.currentContext.ancestorStateOfType(NavigatorState);
|
||||
|
||||
expect(state(), equals('AC')); // transition ->1 is at 0.0
|
||||
|
||||
tester.pump(kFourTenthsOfTheTransitionDuration);
|
||||
expect(state(), equals('AC')); // transition ->1 is at 0.4
|
||||
|
||||
tester.pump(kFourTenthsOfTheTransitionDuration);
|
||||
expect(state(), equals('BC')); // transition ->1 is at 0.8
|
||||
|
||||
tester.pump(kFourTenthsOfTheTransitionDuration);
|
||||
expect(state(), equals('BC')); // transition ->1 is at 1.0
|
||||
|
||||
|
||||
navigator.openTransaction((NavigatorTransaction transaction) => transaction.pushNamed('/2'));
|
||||
expect(state(), equals('BC')); // transition 1->2 is not yet built
|
||||
tester.pump();
|
||||
expect(state(), equals('BCE')); // transition 1->2 is at 0.0
|
||||
|
||||
tester.pump(kFourTenthsOfTheTransitionDuration);
|
||||
expect(state(), equals('BCE')); // transition 1->2 is at 0.4
|
||||
|
||||
tester.pump(kFourTenthsOfTheTransitionDuration);
|
||||
expect(state(), equals('BDE')); // transition 1->2 is at 0.8
|
||||
|
||||
tester.pump(kFourTenthsOfTheTransitionDuration);
|
||||
expect(state(), equals('E')); // transition 1->2 is at 1.0
|
||||
|
||||
|
||||
navigator.openTransaction((NavigatorTransaction transaction) => transaction.pop());
|
||||
expect(state(), equals('E')); // transition 1<-2 is at 1.0, just reversed
|
||||
tester.pump();
|
||||
expect(state(), equals('BDE')); // transition 1<-2 is at 1.0
|
||||
|
||||
tester.pump(kFourTenthsOfTheTransitionDuration);
|
||||
expect(state(), equals('BDE')); // transition 1<-2 is at 0.6
|
||||
|
||||
navigator.openTransaction((NavigatorTransaction transaction) => transaction.pushNamed('/3'));
|
||||
expect(state(), equals('BDE')); // transition 1<-2 is at 0.6
|
||||
tester.pump();
|
||||
expect(state(), equals('BDEF')); // transition 1<-2 is at 0.6, 1->3 is at 0.0
|
||||
|
||||
tester.pump(kFourTenthsOfTheTransitionDuration);
|
||||
expect(state(), equals('BCEF')); // transition 1<-2 is at 0.2, 1->3 is at 0.4
|
||||
|
||||
tester.pump(kFourTenthsOfTheTransitionDuration);
|
||||
expect(state(), equals('BDF')); // transition 1<-2 is done, 1->3 is at 0.8
|
||||
|
||||
navigator.openTransaction((NavigatorTransaction transaction) => transaction.pop());
|
||||
expect(state(), equals('BDF')); // transition 1<-3 is at 0.8, just reversed
|
||||
tester.pump();
|
||||
expect(state(), equals('BDF')); // transition 1<-3 is at 0.8
|
||||
|
||||
tester.pump(kTwoTenthsOfTheTransitionDuration); // notice that dT=0.2 here, not 0.4
|
||||
expect(state(), equals('BDF')); // transition 1<-3 is at 0.6
|
||||
|
||||
tester.pump(kFourTenthsOfTheTransitionDuration);
|
||||
expect(state(), equals('BCF')); // transition 1<-3 is at 0.2
|
||||
|
||||
navigator.openTransaction((NavigatorTransaction transaction) => transaction.pushNamed('/4'));
|
||||
expect(state(), equals('BCF')); // transition 1<-3 is at 0.2, 1->4 is not yet built
|
||||
tester.pump();
|
||||
expect(state(), equals('BCFG')); // transition 1<-3 is at 0.2, 1->4 is at 0.0
|
||||
|
||||
tester.pump(kFourTenthsOfTheTransitionDuration);
|
||||
expect(state(), equals('BCG')); // transition 1<-3 is done, 1->4 is at 0.4
|
||||
|
||||
tester.pump(kFourTenthsOfTheTransitionDuration);
|
||||
expect(state(), equals('BDG')); // transition 1->4 is at 0.8
|
||||
|
||||
tester.pump(kFourTenthsOfTheTransitionDuration);
|
||||
expect(state(), equals('G')); // transition 1->4 is done
|
||||
|
||||
});
|
||||
});
|
||||
}
|
|
@ -6,11 +6,7 @@ import 'package:flutter_test/flutter_test.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
class Builder extends StatelessComponent {
|
||||
Builder({ this.builder });
|
||||
final WidgetBuilder builder;
|
||||
Widget build(BuildContext context) => builder(context);
|
||||
}
|
||||
import 'test_widgets.dart';
|
||||
|
||||
void main() {
|
||||
test('SnackBar control test', () {
|
||||
|
|
|
@ -59,3 +59,10 @@ void flipStatefulComponent(WidgetTester tester) {
|
|||
FlipComponentState state = stateElement.state;
|
||||
state.flip();
|
||||
}
|
||||
|
||||
|
||||
class Builder extends StatelessComponent {
|
||||
Builder({ Key key, this.builder }) : super(key: key);
|
||||
final WidgetBuilder builder;
|
||||
Widget build(BuildContext context) => builder(context);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue