Commit 1d195cb9 authored by Adam Barth's avatar Adam Barth

Fix a large number of Drawer bugs

This patch restructures how we handle drawer. The drawer is now a child of the
Scaffold, which wraps the Drawer in a DrawerController. The DrawerController
manages the interaction with the navigator as well as the edge swiping. The
DrawerController's state machine is driven almost entirely off its Performance,
which it now owns completely.

Fixes #90
Fixes #187
Fixes #192
Fixes #194
Fixes #604
parent 17533e1d
......@@ -55,6 +55,7 @@ class FeedFragment extends StatefulComponent {
class FeedFragmentState extends State<FeedFragment> {
FitnessMode _fitnessMode = FitnessMode.feed;
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
void _handleFitnessModeChange(FitnessMode value) {
setState(() {
......@@ -63,9 +64,8 @@ class FeedFragmentState extends State<FeedFragment> {
Navigator.pop(context);
}
void _showDrawer() {
showDrawer(
context: context,
Widget _buildDrawer() {
return new Drawer(
child: new Block(<Widget>[
new DrawerHeader(child: new Text('Fitness')),
new DrawerItem(
......@@ -106,7 +106,7 @@ class FeedFragmentState extends State<FeedFragment> {
return new ToolBar(
left: new IconButton(
icon: "navigation/menu",
onPressed: _showDrawer),
onPressed: () => _scaffoldKey.currentState?.openDrawer()),
center: new Text(fitnessModeTitle)
);
}
......@@ -207,9 +207,11 @@ class FeedFragmentState extends State<FeedFragment> {
Widget build(BuildContext context) {
return new Scaffold(
key: _scaffoldKey,
toolBar: buildToolBar(),
body: buildBody(),
floatingActionButton: buildFloatingActionButton()
floatingActionButton: buildFloatingActionButton(),
drawer: _buildDrawer()
);
}
}
......
......@@ -6,19 +6,25 @@ import 'package:flutter/material.dart';
import 'demo/widget_demo.dart';
class GalleryPage extends StatelessComponent {
class GalleryPage extends StatefulComponent {
GalleryPage({ this.demos, this.active, this.onThemeChanged });
final List<WidgetDemo> demos;
final WidgetDemo active;
final ValueChanged<ThemeData> onThemeChanged;
void _showDrawer(BuildContext context) {
_GalleryPageState createState() => new _GalleryPageState();
}
class _GalleryPageState extends State<GalleryPage> {
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
Widget _buildDrawer() {
List<Widget> items = <Widget>[
new DrawerHeader(child: new Text('Material demos')),
];
for (WidgetDemo demo in demos) {
for (WidgetDemo demo in config.demos) {
items.add(new DrawerItem(
onPressed: () {
Navigator.pushNamed(context, demo.routeName);
......@@ -27,12 +33,12 @@ class GalleryPage extends StatelessComponent {
));
}
showDrawer(context: context, child: new Block(items));
return new Drawer(child: new Block(items));
}
Widget _body(BuildContext context) {
if (active != null)
return active.builder(context);
Widget _buildBody() {
if (config.active != null)
return config.active.builder(context);
return new Material(
child: new Center(
child: new Text('Select a demo from the drawer')
......@@ -40,22 +46,24 @@ class GalleryPage extends StatelessComponent {
);
}
Widget _tabBar(BuildContext context) {
final WidgetBuilder builder = active?.tabBarBuilder;
Widget _buildTabBar() {
final WidgetBuilder builder = config.active?.tabBarBuilder;
return builder != null ? builder(context) : null;
}
Widget build(BuildContext context) {
return new Scaffold(
key: _scaffoldKey,
toolBar: new ToolBar(
left: new IconButton(
icon: 'navigation/menu',
onPressed: () { _showDrawer(context); }
onPressed: () { _scaffoldKey.currentState?.openDrawer(); }
),
center: new Text(active?.title ?? 'Material gallery'),
tabBar: _tabBar(context)
center: new Text(config.active?.title ?? 'Material gallery'),
tabBar: _buildTabBar()
),
body: _body(context)
drawer: _buildDrawer(),
body: _buildBody()
);
}
}
......@@ -21,7 +21,7 @@ class StockHome extends StatefulComponent {
class StockHomeState extends State<StockHome> {
final GlobalKey scaffoldKey = new GlobalKey();
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
final TabBarSelection _tabBarSelection = new TabBarSelection();
bool _isSearching = false;
String _searchQuery;
......@@ -70,9 +70,8 @@ class StockHomeState extends State<StockHome> {
);
}
void _showDrawer() {
showDrawer(
context: context,
Widget _buildDrawer(BuildContext context) {
return new Drawer(
child: new Block(<Widget>[
new DrawerHeader(child: new Text('Stocks')),
new DrawerItem(
......@@ -151,7 +150,7 @@ class StockHomeState extends State<StockHome> {
elevation: 0,
left: new IconButton(
icon: "navigation/menu",
onPressed: _showDrawer
onPressed: () => _scaffoldKey.currentState?.openDrawer()
),
center: new FadeTransition(
opacity: new AnimatedValue<double>(
......@@ -200,7 +199,7 @@ class StockHomeState extends State<StockHome> {
stock.percentChange = 100.0 * (1.0 / stock.lastSale);
stock.lastSale += 1.0;
});
scaffoldKey.currentState.showSnackBar(new SnackBar(
_scaffoldKey.currentState.showSnackBar(new SnackBar(
content: new Text("Purchased ${stock.symbol} for ${stock.lastSale}"),
actions: <SnackBarAction>[
new SnackBarAction(label: "BUY MORE", onPressed: () { _buyStock(stock, arrowKey); })
......@@ -219,7 +218,7 @@ class StockHomeState extends State<StockHome> {
Navigator.pushNamed(context, '/stock/${stock.symbol}', mostValuableKeys: mostValuableKeys);
},
onShow: (Stock stock, Key arrowKey) {
scaffoldKey.currentState.showBottomSheet((BuildContext context) => new StockSymbolBottomSheet(stock: stock));
_scaffoldKey.currentState.showBottomSheet((BuildContext context) => new StockSymbolBottomSheet(stock: stock));
}
);
}
......@@ -285,9 +284,10 @@ class StockHomeState extends State<StockHome> {
Widget build(BuildContext context) {
return new Scaffold(
key: scaffoldKey,
key: _scaffoldKey,
toolBar: _isSearching ? buildSearchBar() : buildToolBar(),
floatingActionButton: buildFloatingActionButton(),
drawer: _buildDrawer(context),
body: new SizeObserver(
onSizeChanged: _handleSizeChanged,
child: new TabBarView<StockHomeTab>(
......
......@@ -46,6 +46,8 @@ class CardCollectionState extends State<CardCollection> {
InvalidatorCallback _invalidator;
Size _cardCollectionSize = new Size(200.0, 200.0);
GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
void _initVariableSizedCardModels() {
List<double> cardHeights = <double>[
48.0, 63.0, 82.0, 146.0, 60.0, 55.0, 84.0, 96.0, 50.0,
......@@ -117,9 +119,8 @@ class CardCollectionState extends State<CardCollection> {
}
}
void _showDrawer() {
showDrawer(
context: context,
Widget _buildDrawer() {
return new Drawer(
child: new IconTheme(
data: const IconThemeData(color: IconThemeColor.black),
child: new Block(<Widget>[
......@@ -265,9 +266,9 @@ class CardCollectionState extends State<CardCollection> {
);
}
Widget buildToolBar() {
Widget _buildToolBar() {
return new ToolBar(
left: new IconButton(icon: "navigation/menu", onPressed: _showDrawer),
left: new IconButton(icon: "navigation/menu", onPressed: () => _scaffoldKey.currentState?.openDrawer()),
right: <Widget>[
new Text(_dismissDirectionText(_dismissDirection))
],
......@@ -281,7 +282,7 @@ class CardCollectionState extends State<CardCollection> {
);
}
Widget buildCard(BuildContext context, int index) {
Widget _buildCard(BuildContext context, int index) {
if (index >= _cardModels.length)
return null;
......@@ -393,19 +394,18 @@ class CardCollectionState extends State<CardCollection> {
}
Widget build(BuildContext context) {
Widget cardCollection;
if (_fixedSizeCards) {
cardCollection = new ScrollableList<CardModel> (
snapOffsetCallback: _snapToCenter ? _toSnapOffset : null,
snapAlignmentOffset: _cardCollectionSize.height / 2.0,
items: _cardModels,
itemBuilder: (BuildContext context, CardModel card, int index) => buildCard(context, card.value),
itemBuilder: (BuildContext context, CardModel card, int index) => _buildCard(context, card.value),
itemExtent: _cardModels[0].height
);
} else {
cardCollection = new ScrollableMixedWidgetList(
builder: buildCard,
builder: _buildCard,
token: _cardModels.length,
snapOffsetCallback: _snapToCenter ? _toSnapOffset : null,
snapAlignmentOffset: _cardCollectionSize.height / 2.0,
......@@ -446,7 +446,8 @@ class CardCollectionState extends State<CardCollection> {
primarySwatch: _primaryColor
),
child: new Scaffold(
toolBar: buildToolBar(),
toolBar: _buildToolBar(),
drawer: _buildDrawer(),
body: body
)
);
......
......@@ -41,6 +41,8 @@ class PageableListAppState extends State<PageableListApp> {
ScrollDirection scrollDirection = ScrollDirection.horizontal;
bool itemsWrap = false;
GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
void updatePageSize(Size newSize) {
setState(() {
pageSize = newSize;
......@@ -83,9 +85,8 @@ class PageableListAppState extends State<PageableListApp> {
});
}
void _showDrawer() {
showDrawer(
context: context,
Widget _buildDrawer() {
return new Drawer(
child: new Block(<Widget>[
new DrawerHeader(child: new Text('Options')),
new DrawerItem(
......@@ -111,9 +112,9 @@ class PageableListAppState extends State<PageableListApp> {
);
}
Widget buildToolBar() {
Widget _buildToolBar() {
return new ToolBar(
left: new IconButton(icon: "navigation/menu", onPressed: _showDrawer),
left: new IconButton(icon: "navigation/menu", onPressed: () => _scaffoldKey.currentState?.openDrawer()),
center: new Text('PageableList'),
right: <Widget>[
new Text(scrollDirection == ScrollDirection.horizontal ? "horizontal" : "vertical")
......@@ -121,7 +122,7 @@ class PageableListAppState extends State<PageableListApp> {
);
}
Widget buildBody(BuildContext context) {
Widget _buildBody(BuildContext context) {
Widget list = new PageableList<CardModel>(
items: cardModels,
itemsWrap: itemsWrap,
......@@ -141,8 +142,9 @@ class PageableListAppState extends State<PageableListApp> {
return new IconTheme(
data: const IconThemeData(color: IconThemeColor.white),
child: new Scaffold(
toolBar: buildToolBar(),
body: buildBody(context)
toolBar: _buildToolBar(),
drawer: _buildDrawer(),
body: _buildBody(context)
)
);
}
......
......@@ -22,111 +22,48 @@ import 'material.dart';
// The right nav can vary depending on content.
const double _kWidth = 304.0;
const double _kEdgeDragWidth = 20.0;
const double _kMinFlingVelocity = 365.0;
const Duration _kBaseSettleDuration = const Duration(milliseconds: 246);
class _Drawer extends StatelessComponent {
_Drawer({ Key key, this.route }) : super(key: key);
final _DrawerRoute route;
Widget build(BuildContext context) {
return new Focus(
key: new GlobalObjectKey(route),
child: new ConstrainedBox(
constraints: const BoxConstraints.expand(width: _kWidth),
child: new Material(
elevation: route.elevation,
child: route.child
)
)
);
}
}
enum _DrawerState {
showing,
popped,
closed,
}
class _DrawerRoute extends OverlayRoute {
_DrawerRoute({ this.child, this.elevation });
class Drawer extends StatelessComponent {
Drawer({
Key key,
this.elevation: 16,
this.child
}) : super(key: key);
final Widget child;
final int elevation;
final Widget child;
List<WidgetBuilder> get builders => <WidgetBuilder>[ _build ];
final GlobalKey<_DrawerControllerState> _drawerKey = new GlobalKey<_DrawerControllerState>();
_DrawerState _state = _DrawerState.showing;
Widget _build(BuildContext context) {
return new RepaintBoundary(
child: new _DrawerController(
key: _drawerKey,
settleDuration: _kBaseSettleDuration,
onClosed: () {
_DrawerState previousState = _state;
_state = _DrawerState.closed;
switch (previousState) {
case _DrawerState.showing:
Navigator.pop(context);
break;
case _DrawerState.popped:
finished();
break;
case _DrawerState.closed:
assert(false);
break;
}
},
child: new _Drawer(route: this)
Widget build(BuildContext context) {
return new ConstrainedBox(
constraints: const BoxConstraints.expand(width: _kWidth),
child: new Material(
elevation: elevation,
child: child
)
);
}
bool didPop(dynamic result) {
// we don't call the superclass because we want to control the timing of the
// call to finished().
switch (_state) {
case _DrawerState.showing:
_drawerKey.currentState?._close();
_state = _DrawerState.popped;
break;
case _DrawerState.popped:
assert(false);
break;
case _DrawerState.closed:
finished();
break;
}
return true;
}
}
class _DrawerController extends StatefulComponent {
_DrawerController({
Key key,
this.settleDuration,
this.onClosed,
class DrawerController extends StatefulComponent {
DrawerController({
GlobalKey key,
this.child
}) : super(key: key);
final Duration settleDuration;
final Widget child;
final VoidCallback onClosed;
_DrawerControllerState createState() => new _DrawerControllerState();
DrawerControllerState createState() => new DrawerControllerState();
}
class _DrawerControllerState extends State<_DrawerController> {
class DrawerControllerState extends State<DrawerController> {
void initState() {
super.initState();
_performance = new Performance(duration: config.settleDuration)
_performance = new Performance(duration: _kBaseSettleDuration)
..addListener(_performanceChanged)
..addStatusListener(_performanceStatusChanged)
..play();
..addStatusListener(_performanceStatusChanged);
}
void dispose() {
......@@ -143,15 +80,41 @@ class _DrawerControllerState extends State<_DrawerController> {
});
}
LocalHistoryEntry _historyEntry;
void _ensureHistoryEntry() {
if (_historyEntry == null) {
ModalRoute route = ModalRoute.of(context);
if (route != null) {
_historyEntry = new LocalHistoryEntry(onRemove: _handleHistoryEntryRemoved);
route.addLocalHistoryEntry(_historyEntry);
}
}
}
void _performanceStatusChanged(PerformanceStatus status) {
if (status == PerformanceStatus.dismissed && config.onClosed != null)
config.onClosed();
switch (status) {
case PerformanceStatus.forward:
_ensureHistoryEntry();
break;
case PerformanceStatus.reverse:
_historyEntry?.remove();
_historyEntry = null;
break;
case PerformanceStatus.dismissed:
break;
case PerformanceStatus.completed:
break;
}
}
Performance _performance;
double _width;
void _handleHistoryEntryRemoved() {
_historyEntry = null;
close();
}
final AnimatedColorValue _color = new AnimatedColorValue(Colors.transparent, end: Colors.black54);
Performance _performance;
double _width = _kEdgeDragWidth;
void _handleSizeChanged(Size newSize) {
setState(() {
......@@ -161,6 +124,7 @@ class _DrawerControllerState extends State<_DrawerController> {
void _handlePointerDown(_) {
_performance.stop();
_ensureHistoryEntry();
}
void _move(double delta) {
......@@ -168,57 +132,77 @@ class _DrawerControllerState extends State<_DrawerController> {
}
void _settle(Offset velocity) {
if (_performance.isDismissed)
return;
if (velocity.dx.abs() >= _kMinFlingVelocity) {
_performance.fling(velocity: velocity.dx / _width);
} else if (_performance.progress < 0.5) {
_close();
close();
} else {
_performance.fling(velocity: 1.0);
open();
}
}
void _close() {
void open() {
_performance.fling(velocity: 1.0);
}
void close() {
_performance.fling(velocity: -1.0);
}
final AnimatedColorValue _color = new AnimatedColorValue(Colors.transparent, end: Colors.black54);
Widget build(BuildContext context) {
_performance.updateVariable(_color);
return new GestureDetector(
onHorizontalDragUpdate: _move,
onHorizontalDragEnd: _settle,
child: new Stack(<Widget>[
new GestureDetector(
onTap: _close,
child: new DecoratedBox(
decoration: new BoxDecoration(
backgroundColor: _color.value
),
child: new Container()
)
),
new Positioned(
top: 0.0,
left: 0.0,
bottom: 0.0,
child: new Listener(
onPointerDown: _handlePointerDown,
child: new Align(
alignment: const FractionalOffset(1.0, 0.5),
widthFactor: _performance.progress,
child: new SizeObserver(
onSizeChanged: _handleSizeChanged,
child: new RepaintBoundary(
child: config.child
HitTestBehavior behavior;
Widget child;
if (_performance.status == PerformanceStatus.dismissed) {
behavior = HitTestBehavior.translucent;
child = new Align(
alignment: const FractionalOffset(0.0, 0.5),
widthFactor: 1.0,
child: new Container(width: _kEdgeDragWidth)
);
} else {
_performance.updateVariable(_color);
child = new RepaintBoundary(
child: new Stack(<Widget>[
new GestureDetector(
onTap: close,
child: new DecoratedBox(
decoration: new BoxDecoration(
backgroundColor: _color.value
),
child: new Container()
)
),
new Align(
alignment: const FractionalOffset(0.0, 0.5),
child: new Listener(
onPointerDown: _handlePointerDown,
child: new Align(
alignment: const FractionalOffset(1.0, 0.5),
widthFactor: _performance.progress,
child: new SizeObserver(
onSizeChanged: _handleSizeChanged,
child: new RepaintBoundary(
child: new Focus(
key: new GlobalObjectKey(config.key),
child: config.child
)
)
)
)
)
)
)
])
])
);
}
return new GestureDetector(
onHorizontalDragUpdate: _move,
onHorizontalDragEnd: _settle,
behavior: behavior,
child: child
);
}
}
void showDrawer({ BuildContext context, Widget child, int elevation: 16 }) {
Navigator.push(context, new _DrawerRoute(child: child, elevation: elevation));
}
......@@ -15,20 +15,30 @@ import 'bottom_sheet.dart';
import 'material.dart';
import 'snack_bar.dart';
import 'tool_bar.dart';
import 'drawer.dart';
const double _kFloatingActionButtonMargin = 16.0; // TODO(hmuller): should be device dependent
enum _Child { body, toolBar, bottomSheet, snackBar, floatingActionButton }
enum _Child {
body,
toolBar,
bottomSheet,
snackBar,
floatingActionButton,
drawer,
}
class _ScaffoldLayout extends MultiChildLayoutDelegate {
void performLayout(Size size, BoxConstraints constraints) {
BoxConstraints looseConstraints = constraints.loosen();
// This part of the layout has the same effect as putting the toolbar and
// body in a column and making the body flexible. What's different is that
// in this case the toolbar appears -after- the body in the stacking order,
// so the toolbar's shadow is drawn on top of the body.
final BoxConstraints toolBarConstraints = constraints.loosen().tightenWidth(size.width);
final BoxConstraints toolBarConstraints = looseConstraints.tightenWidth(size.width);
Size toolBarSize = Size.zero;
if (isChild(_Child.toolBar)) {
......@@ -52,7 +62,7 @@ class _ScaffoldLayout extends MultiChildLayoutDelegate {
// non-zero height then it's inset from the parent's right and bottom edges
// by _kFloatingActionButtonMargin.
final BoxConstraints fullWidthConstraints = constraints.loosen().tightenWidth(size.width);
final BoxConstraints fullWidthConstraints = looseConstraints.tightenWidth(size.width);
Size bottomSheetSize = Size.zero;
Size snackBarSize = Size.zero;
......@@ -67,7 +77,7 @@ class _ScaffoldLayout extends MultiChildLayoutDelegate {
}
if (isChild(_Child.floatingActionButton)) {
final Size fabSize = layoutChild(_Child.floatingActionButton, constraints.loosen());
final Size fabSize = layoutChild(_Child.floatingActionButton, looseConstraints);
final double fabX = size.width - fabSize.width - _kFloatingActionButtonMargin;
double fabY = size.height - fabSize.height - _kFloatingActionButtonMargin;
if (snackBarSize.height > 0.0)
......@@ -76,6 +86,11 @@ class _ScaffoldLayout extends MultiChildLayoutDelegate {
fabY = math.min(fabY, size.height - bottomSheetSize.height - fabSize.height / 2.0);
positionChild(_Child.floatingActionButton, new Point(fabX, fabY));
}
if (isChild(_Child.drawer)) {
layoutChild(_Child.drawer, looseConstraints);
positionChild(_Child.drawer, Point.origin);
}
}
}
......@@ -86,12 +101,14 @@ class Scaffold extends StatefulComponent {
Key key,
this.toolBar,
this.body,
this.floatingActionButton
this.floatingActionButton,
this.drawer
}) : super(key: key);
final ToolBar toolBar;
final Widget body;
final Widget floatingActionButton;
final Widget drawer;
static ScaffoldState of(BuildContext context) => context.ancestorStateOfType(ScaffoldState);
......@@ -100,6 +117,14 @@ class Scaffold extends StatefulComponent {
class ScaffoldState extends State<Scaffold> {
// DRAWER API
final GlobalKey<DrawerControllerState> _drawerKey = new GlobalKey<DrawerControllerState>();
void openDrawer() {
_drawerKey.currentState.open();
}
// SNACKBAR API
Queue<ScaffoldFeatureController<SnackBar>> _snackBars = new Queue<ScaffoldFeatureController<SnackBar>>();
......@@ -270,6 +295,16 @@ class ScaffoldState extends State<Scaffold> {
_addIfNonNull(children, config.floatingActionButton, _Child.floatingActionButton);
if (config.drawer != null) {
children.add(new LayoutId(
id: _Child.drawer,
child: new DrawerController(
key: _drawerKey,
child: config.drawer
)
));
}
return new CustomMultiChildLayout(children, delegate: _scaffoldLayout);
}
......
......@@ -146,7 +146,7 @@ abstract class TransitionRoute<T> extends OverlayRoute<T> {
super.dispose();
}
final ProxyPerformance forwardPerformance = new ProxyPerformance();
void didPushNext(Route nextRoute) {
......@@ -183,7 +183,7 @@ abstract class TransitionRoute<T> extends OverlayRoute<T> {
forwardPerformance,
buildTransition(
context,
performance,
performance,
child
)
);
......@@ -202,6 +202,7 @@ class LocalHistoryEntry {
LocalHistoryRoute _owner;
void remove() {
_owner.removeLocalHistoryEntry(this);
assert(_owner == null);
}
void _notifyRemoved() {
if (onRemove != null)
......@@ -432,4 +433,4 @@ abstract class PageRoute<T> extends ModalRoute<T> {
NamedRouteSettings settings: const NamedRouteSettings()
}) : super(completer: completer, settings: settings);
bool get opaque => true;
}
\ No newline at end of file
}
......@@ -11,20 +11,25 @@ void main() {
test('Drawer control test', () {
testWidgets((WidgetTester tester) {
GlobalKey<ScaffoldState> scaffoldKey = new GlobalKey<ScaffoldState>();
BuildContext context;
tester.pumpWidget(
new MaterialApp(
routes: <String, RouteBuilder>{
'/': (RouteArguments args) {
context = args.context;
return new Container();
return new Scaffold(
key: scaffoldKey,
drawer: new Text('drawer'),
body: new Container()
);
}
}
)
);
tester.pump(); // no effect
expect(tester.findText('drawer'), isNull);
showDrawer(context: context, child: new Text('drawer'));
scaffoldKey.currentState.openDrawer();
tester.pump(); // drawer should be starting to animate in
expect(tester.findText('drawer'), isNotNull);
tester.pump(new Duration(seconds: 1)); // animation done
......@@ -39,21 +44,24 @@ void main() {
test('Drawer tap test', () {
testWidgets((WidgetTester tester) {
BuildContext context;
GlobalKey<ScaffoldState> scaffoldKey = new GlobalKey<ScaffoldState>();
tester.pumpWidget(new Container()); // throw away the old App and its Navigator
tester.pumpWidget(
new MaterialApp(
routes: <String, RouteBuilder>{
'/': (RouteArguments args) {
context = args.context;
return new Container();
return new Scaffold(
key: scaffoldKey,
drawer: new Text('drawer'),
body: new Container()
);
}
}
)
);
tester.pump(); // no effect
expect(tester.findText('drawer'), isNull);
showDrawer(context: context, child: new Text('drawer'));
scaffoldKey.currentState.openDrawer();
tester.pump(); // drawer should be starting to animate in
expect(tester.findText('drawer'), isNotNull);
tester.pump(new Duration(seconds: 1)); // animation done
......@@ -64,7 +72,9 @@ void main() {
tester.pump(new Duration(seconds: 1)); // ditto
expect(tester.findText('drawer'), isNotNull);
tester.tapAt(const Point(750.0, 100.0)); // on the mask
tester.pump(); // drawer should be starting to animate away
tester.pump();
tester.pump(new Duration(milliseconds: 10));
// drawer should be starting to animate away
expect(tester.findText('drawer'), isNotNull);
tester.pump(new Duration(seconds: 1)); // animation done
expect(tester.findText('drawer'), isNull);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment