Add a persistent bottom sheet to the stocks demo

This commit is contained in:
Hans Muller 2015-11-11 15:38:13 -08:00
parent c49f07eec0
commit 5755b15bf5
6 changed files with 126 additions and 56 deletions

View file

@ -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(stock: _stocks[path[2]]);
return (RouteArguments args) => new StockSymbolPage(stock: _stocks[path[2]]);
return null;
}
return null;

View file

@ -20,6 +20,7 @@ class StockHome extends StatefulComponent {
class StockHomeState extends State<StockHome> {
final GlobalKey<PlaceholderState> _snackBarPlaceholderKey = new GlobalKey<PlaceholderState>();
final GlobalKey<PlaceholderState> _bottomSheetPlaceholderKey = new GlobalKey<PlaceholderState>();
bool _isSearching = false;
String _searchQuery;
@ -188,13 +189,20 @@ class StockHomeState extends State<StockHome> {
});
showModalBottomSheet(
context: context,
child: new StockSymbolViewer(stock: stock, showToolBar: false)
child: new StockSymbolBottomSheet(stock: stock)
);
},
onOpen: (Stock stock, Key arrowKey) {
Set<Key> mostValuableKeys = new Set<Key>();
mostValuableKeys.add(arrowKey);
Navigator.of(context).pushNamed('/stock/${stock.symbol}', mostValuableKeys: mostValuableKeys);
},
onShow: (Stock stock, Key arrowKey) {
showBottomSheet(
placeholderKey: _bottomSheetPlaceholderKey,
context: context,
child: new StockSymbolBottomSheet(stock: stock)
);
}
);
}
@ -267,6 +275,7 @@ class StockHomeState extends State<StockHome> {
toolBar: _isSearching ? buildSearchBar() : buildToolBar(),
body: buildTabNavigator(),
snackBar: new Placeholder(key: _snackBarPlaceholderKey),
bottomSheet: new Placeholder(key: _bottomSheetPlaceholderKey),
floatingActionButton: buildFloatingActionButton()
);
}

View file

@ -5,10 +5,11 @@
part of stocks;
class StockList extends StatelessComponent {
StockList({ Key key, this.stocks, this.onOpen, this.onAction }) : super(key: key);
StockList({ Key key, this.stocks, this.onOpen, this.onShow, this.onAction }) : super(key: key);
final List<Stock> stocks;
final StockRowActionCallback onOpen;
final StockRowActionCallback onShow;
final StockRowActionCallback onAction;
Widget build(BuildContext context) {
@ -19,6 +20,7 @@ class StockList extends StatelessComponent {
return new StockRow(
stock: stock,
onPressed: onOpen,
onDoubleTap: onShow,
onLongPressed: onAction
);
}

View file

@ -27,6 +27,7 @@ class StockRow extends StatelessComponent {
StockRow({
Stock stock,
this.onPressed,
this.onDoubleTap,
this.onLongPressed
}) : this.stock = stock,
_arrowKey = new StockRowPartKey(stock, StockRowPartKind.arrow),
@ -34,22 +35,15 @@ class StockRow extends StatelessComponent {
final Stock stock;
final StockRowActionCallback onPressed;
final StockRowActionCallback onDoubleTap;
final StockRowActionCallback onLongPressed;
final Key _arrowKey;
static const double kHeight = 79.0;
GestureTapCallback _getTapHandler(StockRowActionCallback callback) {
if (callback == null)
return null;
return () => callback(stock, _arrowKey);
}
GestureLongPressCallback _getLongPressHandler(StockRowActionCallback callback) {
if (callback == null)
return null;
return () => callback(stock, _arrowKey);
GestureTapCallback _getHandler(StockRowActionCallback callback) {
return callback == null ? null : () => callback(stock, _arrowKey);
}
Widget build(BuildContext context) {
@ -58,8 +52,9 @@ class StockRow extends StatelessComponent {
if (stock.percentChange > 0)
changeInPrice = "+" + changeInPrice;
return new InkWell(
onTap: _getTapHandler(onPressed),
onLongPress: _getLongPressHandler(onLongPressed),
onTap: _getHandler(onPressed),
onDoubleTap: _getHandler(onDoubleTap),
onLongPress: _getHandler(onLongPressed),
child: new Container(
padding: const EdgeDims.TRBL(16.0, 16.0, 20.0, 16.0),
decoration: new BoxDecoration(

View file

@ -4,11 +4,10 @@
part of stocks;
class StockSymbolViewer extends StatelessComponent {
StockSymbolViewer({ this.stock, this.showToolBar: true });
class StockSymbolView extends StatelessComponent {
StockSymbolView({ this.stock});
final Stock stock;
final bool showToolBar;
Widget build(BuildContext context) {
String lastSale = "\$${stock.lastSale.toStringAsFixed(2)}";
@ -17,11 +16,7 @@ class StockSymbolViewer extends StatelessComponent {
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(
return new Container(
padding: new EdgeDims.all(20.0),
child: new Column(<Widget>[
new Row(<Widget>[
@ -44,15 +39,19 @@ class StockSymbolViewer extends StatelessComponent {
),
new Text('Market Cap', style: headings),
new Text('${stock.marketCap}'),
])
],
justifyContent: FlexJustifyContent.collapse
)
)
)
]);
);
}
}
if (!showToolBar)
return body;
class StockSymbolPage extends StatelessComponent {
StockSymbolPage({ this.stock });
final Stock stock;
Widget build(BuildContext context) {
return new Scaffold(
toolBar: new ToolBar(
left: new IconButton(
@ -63,8 +62,28 @@ class StockSymbolViewer extends StatelessComponent {
),
center: new Text(stock.name)
),
body: body
body: new Block(<Widget>[
new Container(
margin: new EdgeDims.all(20.0),
child: new Card(child: new StockSymbolView(stock: stock))
)
])
);
}
}
class StockSymbolBottomSheet extends StatelessComponent {
StockSymbolBottomSheet({ this.stock });
final Stock stock;
Widget build(BuildContext context) {
return new Container(
child: new StockSymbolView(stock: stock),
padding: new EdgeDims.all(10.0),
decoration: new BoxDecoration(
border: new Border(top: new BorderSide(color: Colors.black26, width: 1.0))
)
);
}
}

View file

@ -28,6 +28,7 @@ class InkWell extends StatefulComponent {
Key key,
this.child,
this.onTap,
this.onDoubleTap,
this.onLongPress,
this.onHighlightChanged,
this.defaultColor,
@ -36,6 +37,7 @@ class InkWell extends StatefulComponent {
final Widget child;
final GestureTapCallback onTap;
final GestureTapCallback onDoubleTap;
final GestureLongPressCallback onLongPress;
final _HighlightChangedCallback onHighlightChanged;
final Color defaultColor;
@ -54,6 +56,7 @@ class _InkWellState extends State<InkWell> {
duration: _kInkWellHighlightFadeDuration,
child: new _InkSplashes(
onTap: config.onTap,
onDoubleTap: config.onDoubleTap,
onLongPress: config.onLongPress,
onHighlightChanged: (bool value) {
setState(() {
@ -134,10 +137,12 @@ class _RenderInkSplashes extends RenderProxyBox {
_RenderInkSplashes({
RenderBox child,
GestureTapCallback onTap,
GestureTapCallback onDoubleTap,
GestureLongPressCallback onLongPress,
this.onHighlightChanged
}) : super(child) {
this.onTap = onTap;
this.onDoubleTap = onDoubleTap;
this.onLongPress = onLongPress;
}
@ -148,6 +153,13 @@ class _RenderInkSplashes extends RenderProxyBox {
_syncTapRecognizer();
}
GestureTapCallback get onDoubleTap => _onDoubleTap;
GestureTapCallback _onDoubleTap;
void set onDoubleTap (GestureTapCallback value) {
_onDoubleTap = value;
_syncDoubleTapRecognizer();
}
GestureTapCallback get onLongPress => _onLongPress;
GestureTapCallback _onLongPress;
void set onLongPress (GestureTapCallback value) {
@ -161,6 +173,7 @@ class _RenderInkSplashes extends RenderProxyBox {
_InkSplash _lastSplash;
TapGestureRecognizer _tap;
DoubleTapGestureRecognizer _doubleTap;
LongPressGestureRecognizer _longPress;
void _removeSplash(_InkSplash splash) {
@ -170,8 +183,9 @@ class _RenderInkSplashes extends RenderProxyBox {
}
void handleEvent(InputEvent event, BoxHitTestEntry entry) {
if (event.type == 'pointerdown' && (onTap != null || onLongPress != null)) {
if (event.type == 'pointerdown' && (onTap != null || onDoubleTap != null || onLongPress != null)) {
_tap?.addPointer(event);
_doubleTap?.addPointer(event);
_longPress?.addPointer(event);
}
}
@ -179,17 +193,19 @@ class _RenderInkSplashes extends RenderProxyBox {
void attach() {
super.attach();
_syncTapRecognizer();
_syncDoubleTapRecognizer();
_syncLongPressRecognizer();
}
void detach() {
_disposeTapRecognizer();
_disposeDoubleTapRecognizer();
_disposeLongPressRecognizer();
super.detach();
}
void _syncTapRecognizer() {
if (onTap == null && onLongPress == null) {
if (onTap == null && doubleTap == null && onLongPress == null) {
_disposeTapRecognizer();
} else {
_tap ??= new TapGestureRecognizer(router: FlutterBinding.instance.pointerRouter)
@ -204,6 +220,20 @@ class _RenderInkSplashes extends RenderProxyBox {
_tap = null;
}
void _syncDoubleTapRecognizer() {
if (onDoubleTap == null) {
_disposeDoubleTapRecognizer();
} else {
_doubleTap ??= new DoubleTapGestureRecognizer(router: FlutterBinding.instance.pointerRouter)
..onDoubleTap = _handleDoubleTap;
}
}
void _disposeDoubleTapRecognizer() {
_doubleTap?.dispose();
_doubleTap = null;
}
void _syncLongPressRecognizer() {
if (onLongPress == null) {
_disposeLongPressRecognizer();
@ -241,6 +271,13 @@ class _RenderInkSplashes extends RenderProxyBox {
onHighlightChanged(false);
}
void _handleDoubleTap() {
_lastSplash?.confirm();
_lastSplash = null;
if (onDoubleTap != null)
onDoubleTap();
}
void _handleLongPress() {
_lastSplash?.confirm();
_lastSplash = null;
@ -269,18 +306,26 @@ class _InkSplashes extends OneChildRenderObjectWidget {
Key key,
Widget child,
this.onTap,
this.onDoubleTap,
this.onLongPress,
this.onHighlightChanged
}) : super(key: key, child: child);
final GestureTapCallback onTap;
final GestureTapCallback onDoubleTap;
final GestureLongPressCallback onLongPress;
final _HighlightChangedCallback onHighlightChanged;
_RenderInkSplashes createRenderObject() => new _RenderInkSplashes(onTap: onTap, onLongPress: onLongPress, onHighlightChanged: onHighlightChanged);
_RenderInkSplashes createRenderObject() => new _RenderInkSplashes(
onTap: onTap,
onDoubleTap: onDoubleTap,
onLongPress: onLongPress,
onHighlightChanged: onHighlightChanged
);
void updateRenderObject(_RenderInkSplashes renderObject, _InkSplashes oldWidget) {
renderObject.onTap = onTap;
renderObject.onDoubleTap = onDoubleTap;
renderObject.onLongPress = onLongPress;
renderObject.onHighlightChanged = onHighlightChanged;
}