Commit 5e08b98e authored by Hans Muller's avatar Hans Muller

Refactor bottom sheet support, add a bottom sheet to the stocks demo

Factored OverlayRoute out of the modal and persistent bottom sheet clases, since the bottom sheet classes need to drive the performance.

Added a bottom sheet to the stocks demo: long-press on a stock shows a modal bottom sheet.

Made AnimatedModalBarrier public.
parent 0f2a9c40
......@@ -81,7 +81,7 @@ class StocksAppState extends State<StocksApp> {
if (path.length != 3)
return null;
if (_stocks.containsKey(path[2]))
return (RouteArguments args) => new StockSymbolViewer(_stocks[path[2]]);
return (RouteArguments args) => new StockSymbolViewer(stock: _stocks[path[2]]);
return null;
}
return null;
......
......@@ -186,6 +186,10 @@ class StockHomeState extends State<StockHome> {
stock.percentChange = 100.0 * (1.0 / stock.lastSale);
stock.lastSale += 1.0;
});
showModalBottomSheet(
context: context,
child: new StockSymbolViewer(stock: stock, showToolBar: false)
);
},
onOpen: (Stock stock, Key arrowKey) {
Set<Key> mostValuableKeys = new Set<Key>();
......
......@@ -5,16 +5,54 @@
part of stocks;
class StockSymbolViewer extends StatelessComponent {
StockSymbolViewer(this.stock);
StockSymbolViewer({ this.stock, this.showToolBar: true });
final Stock stock;
final bool showToolBar;
Widget build(BuildContext context) {
String lastSale = "\$${stock.lastSale.toStringAsFixed(2)}";
String changeInPrice = "${stock.percentChange.toStringAsFixed(2)}%";
if (stock.percentChange > 0)
changeInPrice = "+" + changeInPrice;
TextStyle headings = Theme.of(context).text.body2;
Widget body = new Block(<Widget>[
new Container(
margin: new EdgeDims.all(20.0),
child: new Card(
child: new Container(
padding: new EdgeDims.all(20.0),
child: new Column(<Widget>[
new Row(<Widget>[
new Text(
'${stock.symbol}',
style: Theme.of(context).text.display2
),
new Hero(
tag: StockRowPartKind.arrow,
turns: 2,
child: new StockArrow(percentChange: stock.percentChange)
),
],
justifyContent: FlexJustifyContent.spaceBetween
),
new Text('Last Sale', style: headings),
new Text('$lastSale ($changeInPrice)'),
new Container(
height: 8.0
),
new Text('Market Cap', style: headings),
new Text('${stock.marketCap}'),
])
)
)
)
]);
if (!showToolBar)
return body;
return new Scaffold(
toolBar: new ToolBar(
left: new IconButton(
......@@ -25,39 +63,7 @@ class StockSymbolViewer extends StatelessComponent {
),
center: new Text(stock.name)
),
body: new Block(<Widget>[
new Container(
margin: new EdgeDims.all(20.0),
child: new Card(
child: new Container(
padding: new EdgeDims.all(20.0),
child: new Column(<Widget>[
new Row(<Widget>[
new Text(
'${stock.symbol}',
style: Theme.of(context).text.display2
),
new Hero(
tag: StockRowPartKind.arrow,
turns: 2,
child: new StockArrow(percentChange: stock.percentChange)
),
],
justifyContent: FlexJustifyContent.spaceBetween
),
new Text('Last Sale', style: headings),
new Text('$lastSale ($changeInPrice)'),
new Container(
height: 8.0
),
new Text('Market Cap', style: headings),
new Text('${stock.marketCap}'),
])
)
)
)
]
)
body: body
);
}
......
......@@ -15,6 +15,8 @@ import 'material.dart';
const Duration _kBottomSheetDuration = const Duration(milliseconds: 200);
const double _kMinFlingVelocity = 700.0;
const double _kCloseProgressThreshold = 0.5;
const Color _kTransparent = const Color(0x00000000);
const Color _kBarrierColor = Colors.black54;
class _BottomSheetDragController extends StatelessComponent {
_BottomSheetDragController({
......@@ -61,6 +63,29 @@ class _BottomSheetDragController extends StatelessComponent {
}
}
class _BottomSheetRoute extends OverlayRoute {
_BottomSheetRoute({ this.completer, this.child });
final Completer completer;
final Widget child;
Performance performance;
void didPush(OverlayState overlay, OverlayEntry insertionPoint) {
performance = new Performance(duration: _kBottomSheetDuration, debugLabel: debugLabel)
..forward();
super.didPush(overlay, insertionPoint);
}
void didPop(dynamic result) {
completer.complete(result);
performance.reverse().then((_) {
super.didPop(result); // clear the overlay entries
});
}
String get debugLabel => '$runtimeType';
String toString() => '$runtimeType(performance: $performance)';
}
class _ModalBottomSheet extends StatefulComponent {
_ModalBottomSheet({ Key key, this.route }) : super(key: key);
......@@ -97,7 +122,7 @@ class _ModalBottomSheetState extends State<_ModalBottomSheet> {
return new GestureDetector(
onTap: () { Navigator.of(context).pop(); },
child: new BuilderTransition(
performance: config.route._performance,
performance: config.route.performance,
variables: <AnimatedValue<double>>[_layout.childTop],
builder: (BuildContext context) {
return new ClipRect(
......@@ -105,7 +130,7 @@ class _ModalBottomSheetState extends State<_ModalBottomSheet> {
delegate: _layout,
token: _layout.childTop.value,
child: new _BottomSheetDragController(
performance: config.route._performance,
performance: config.route.performance,
child: new Material(child: config.route.child),
childHeight: _layout.childTop.end
)
......@@ -117,29 +142,28 @@ class _ModalBottomSheetState extends State<_ModalBottomSheet> {
}
}
class _ModalBottomSheetRoute extends ModalRoute {
_ModalBottomSheetRoute({ this.completer, this.child });
final Completer completer;
final Widget child;
bool get opaque => false;
Duration get transitionDuration => _kBottomSheetDuration;
Performance _performance;
class _ModalBottomSheetRoute extends _BottomSheetRoute {
_ModalBottomSheetRoute({ Completer completer, Widget child })
: super(completer: completer, child: child);
Performance createPerformance() {
_performance = super.createPerformance();
return _performance;
Widget _buildModalBarrier(BuildContext context) {
return new AnimatedModalBarrier(
color: new AnimatedColorValue(_kTransparent, end: _kBarrierColor, curve: Curves.ease),
performance: performance
);
}
Color get barrierColor => Colors.black54;
Widget buildModalWidget(BuildContext context) => new _ModalBottomSheet(route: this);
void didPop([dynamic result]) {
completer.complete(result);
super.didPop(result);
Widget _buildBottomSheet(BuildContext context) {
return new Focus(
key: new GlobalObjectKey(this),
child: new _ModalBottomSheet(route: this)
);
}
List<WidgetBuilder> get builders => <WidgetBuilder>[
_buildModalBarrier,
_buildBottomSheet,
];
}
Future showModalBottomSheet({ BuildContext context, Widget child }) {
......@@ -155,7 +179,7 @@ Future showModalBottomSheet({ BuildContext context, Widget child }) {
class _PersistentBottomSheet extends StatefulComponent {
_PersistentBottomSheet({ Key key, this.route }) : super(key: key);
final _PersistentBottomSheetRoute route;
final _BottomSheetRoute route;
_PersistentBottomSheetState createState() => new _PersistentBottomSheetState();
}
......@@ -175,7 +199,7 @@ class _PersistentBottomSheetState extends State<_PersistentBottomSheet> {
alignment: new AnimatedValue<FractionalOffset>(const FractionalOffset(0.0, 0.0)),
heightFactor: new AnimatedValue<double>(0.0, end: 1.0),
child: new _BottomSheetDragController(
performance: config.route._performance,
performance: config.route.performance,
childHeight: _childHeight,
child: new Material(
child: new SizeObserver(child: config.route.child, onSizeChanged: _updateChildHeight)
......@@ -185,32 +209,11 @@ class _PersistentBottomSheetState extends State<_PersistentBottomSheet> {
}
}
class _PersistentBottomSheetRoute extends TransitionRoute {
_PersistentBottomSheetRoute({ this.completer, this.child });
final Widget child;
final Completer completer;
bool get opaque => false;
Duration get transitionDuration => _kBottomSheetDuration;
Performance _performance;
Performance createPerformance() {
_performance = super.createPerformance();
return _performance;
}
void didPop([dynamic result]) {
completer.complete(result);
super.didPop(result);
}
}
Future showBottomSheet({ BuildContext context, GlobalKey<PlaceholderState> placeholderKey, Widget child }) {
assert(child != null);
assert(placeholderKey != null);
final Completer completer = new Completer();
_PersistentBottomSheetRoute route = new _PersistentBottomSheetRoute(child: child, completer: completer);
_BottomSheetRoute route = new _BottomSheetRoute(child: child, completer: completer);
placeholderKey.currentState.child = new _PersistentBottomSheet(route: route);
Navigator.of(context).pushEphemeral(route);
return completer.future;
......
......@@ -39,8 +39,8 @@ class ModalBarrier extends StatelessComponent {
}
}
class _AnimatedModalBarrier extends StatelessComponent {
_AnimatedModalBarrier({
class AnimatedModalBarrier extends StatelessComponent {
AnimatedModalBarrier({
Key key,
this.color,
this.performance
......@@ -108,7 +108,7 @@ abstract class ModalRoute extends TransitionRoute {
Widget buildModalWidget(BuildContext context);
Widget _buildModalBarrier(BuildContext context) {
return new _AnimatedModalBarrier(
return new AnimatedModalBarrier(
color: new AnimatedColorValue(_kTransparent, end: barrierColor, curve: Curves.ease),
performance: performance
);
......
......@@ -29,6 +29,7 @@ void main() {
tester.tap(tester.findText('BottomSheet'));
tester.pump(); // bottom sheet dismiss animation starts
tester.pump(new Duration(seconds: 1)); // animation done
tester.pump(new Duration(seconds: 2)); // rebuild frame
expect(tester.findText('BottomSheet'), isNull);
showModalBottomSheet(context: context, child: new Text('BottomSheet'));
......@@ -40,6 +41,7 @@ void main() {
tester.tapAt(new Point(20.0, 20.0));
tester.pump(); // bottom sheet dismiss animation starts
tester.pump(new Duration(seconds: 1)); // animation done
tester.pump(new Duration(seconds: 2)); // rebuild frame
expect(tester.findText('BottomSheet'), 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