RenderInkWell should use gestures

After this patch, InkWell is driven by gesture recognizers, which lets us
cleanly cancel splashes when the user actually scrolls.

I've also refactored all the clients of InkWell to use InkWell to detect
gestures instead of wrapping InkWell in a GestureDetector.

Fixes #1271
This commit is contained in:
Adam Barth 2015-10-03 01:09:14 -07:00
parent 445c512d2c
commit cf88993492
21 changed files with 320 additions and 207 deletions

View file

@ -42,8 +42,8 @@ void launch(String relativeUrl, String bundle) {
activity.startActivity(intent);
}
class SkyDemo {
SkyDemo({
class FlutterDemo {
FlutterDemo({
name,
this.href,
this.bundle,
@ -60,8 +60,8 @@ class SkyDemo {
final BoxDecoration decoration;
}
List<SkyDemo> demos = [
new SkyDemo(
List<FlutterDemo> demos = [
new FlutterDemo(
name: 'Stocks',
href: '../../stocks/lib/main.dart',
bundle: 'stocks.skyx',
@ -74,7 +74,7 @@ List<SkyDemo> demos = [
)
)
),
new SkyDemo(
new FlutterDemo(
name: 'Asteroids',
href: '../../game/lib/main.dart',
bundle: 'game.skyx',
@ -87,7 +87,7 @@ List<SkyDemo> demos = [
)
)
),
new SkyDemo(
new FlutterDemo(
name: 'Fitness',
href: '../../fitness/lib/main.dart',
bundle: 'fitness.skyx',
@ -97,7 +97,7 @@ List<SkyDemo> demos = [
backgroundColor: Colors.indigo[500]
)
),
new SkyDemo(
new FlutterDemo(
name: 'Swipe Away',
href: '../../widgets/card_collection.dart',
bundle: 'cards.skyx',
@ -107,7 +107,7 @@ List<SkyDemo> demos = [
backgroundColor: Colors.redAccent[200]
)
),
new SkyDemo(
new FlutterDemo(
name: 'Interactive Text',
href: '../../rendering/interactive_flex.dart',
bundle: 'interactive_flex.skyx',
@ -120,7 +120,7 @@ List<SkyDemo> demos = [
// new SkyDemo(
// 'Touch Demo', '../../rendering/touch_demo.dart', 'Simple example showing handling of touch events at a low level'),
new SkyDemo(
new FlutterDemo(
name: 'Minedigger Game',
href: '../../mine_digger/lib/main.dart',
bundle: 'mine_digger.skyx',
@ -138,44 +138,47 @@ List<SkyDemo> demos = [
const double kCardHeight = 120.0;
const EdgeDims kListPadding = const EdgeDims.all(4.0);
class DemoList extends StatelessComponent {
Widget buildCardContents(SkyDemo demo) {
return new Container(
decoration: demo.decoration,
child: new InkWell(
child: new Container(
margin: const EdgeDims.only(top: 24.0, left: 24.0),
child: new Column([
new Text(demo.name, style: demo.textTheme.title),
new Flexible(
child: new Text(demo.description, style: demo.textTheme.subhead)
)
],
alignItems: FlexAlignItems.start
class DemoCard extends StatelessComponent {
DemoCard({ Key key, this.demo }) : super(key: key);
final FlutterDemo demo;
Widget build(BuildContext context) {
return new Container(
height: kCardHeight,
child: new Card(
child: new Container(
decoration: demo.decoration,
child: new InkWell(
onTap: () => launch(demo.href, demo.bundle),
child: new Container(
margin: const EdgeDims.only(top: 24.0, left: 24.0),
child: new Column([
new Text(demo.name, style: demo.textTheme.title),
new Flexible(
child: new Text(demo.description, style: demo.textTheme.subhead)
)
],
alignItems: FlexAlignItems.start
)
)
)
)
);
}
Widget buildDemo(BuildContext context, SkyDemo demo) {
return new GestureDetector(
key: demo.key,
onTap: () => launch(demo.href, demo.bundle),
child: new Container(
height: kCardHeight,
child: new Card(
child: buildCardContents(demo)
)
)
);
}
}
class DemoList extends StatelessComponent {
Widget _buildDemoCard(BuildContext context, FlutterDemo demo) {
return new DemoCard(key: demo.key, demo: demo);
}
Widget build(BuildContext context) {
return new ScrollableList<SkyDemo>(
return new ScrollableList<FlutterDemo>(
items: demos,
itemExtent: kCardHeight,
itemBuilder: buildDemo,
itemBuilder: _buildDemoCard,
padding: kListPadding
);
}
@ -200,7 +203,7 @@ class DemoHome extends StatelessComponent {
void main() {
runApp(new App(
title: 'Sky Demos',
title: 'Flutter Demos',
theme: _theme,
routes: {
'/': (NavigatorState navigator, Route route) => new DemoHome()

View file

@ -33,15 +33,13 @@ class DialogMenuItem extends StatelessComponent {
Function onPressed;
Widget build(BuildContext context) {
return new GestureDetector(
onTap: onPressed,
child: new Container(
height: 48.0,
child: new InkWell(
child: new Padding(
padding: const EdgeDims.symmetric(horizontal: 16.0),
child: new Row(children)
)
return new Container(
height: 48.0,
child: new InkWell(
onTap: onPressed,
child: new Padding(
padding: const EdgeDims.symmetric(horizontal: 16.0),
child: new Row(children)
)
)
);

View file

@ -65,12 +65,13 @@ class MealFragmentState extends State<MealFragment> {
icon: "navigation/close",
onPressed: config.navigator.pop),
center: new Text('New Meal'),
right: [new InkWell(
child: new GestureDetector(
right: [
// TODO(abarth): Should this be a FlatButton?
new InkWell(
onTap: _handleSave,
child: new Text('SAVE')
)
)]
]
);
}

View file

@ -136,12 +136,13 @@ class MeasurementFragmentState extends State<MeasurementFragment> {
icon: "navigation/close",
onPressed: config.navigator.pop),
center: new Text('New Measurement'),
right: [new InkWell(
child: new GestureDetector(
right: [
// TODO(abarth): Should this be a FlatButton?
new InkWell(
onTap: _handleSave,
child: new Text('SAVE')
)
)]
]
);
}

View file

@ -55,52 +55,50 @@ class StockRow extends StatelessComponent {
String changeInPrice = "${stock.percentChange.toStringAsFixed(2)}%";
if (stock.percentChange > 0) changeInPrice = "+" + changeInPrice;
return new GestureDetector(
return new InkWell(
onTap: _getTapHandler(onPressed),
onLongPress: _getLongPressHandler(onLongPressed),
child: new InkWell(
child: new Container(
padding: const EdgeDims.TRBL(16.0, 16.0, 20.0, 16.0),
decoration: new BoxDecoration(
border: new Border(
bottom: new BorderSide(color: Theme.of(context).dividerColor)
)
child: new Container(
padding: const EdgeDims.TRBL(16.0, 16.0, 20.0, 16.0),
decoration: new BoxDecoration(
border: new Border(
bottom: new BorderSide(color: Theme.of(context).dividerColor)
)
),
child: new Row([
new Container(
key: arrowKey,
child: new StockArrow(percentChange: stock.percentChange),
margin: const EdgeDims.only(right: 5.0)
),
child: new Row([
new Container(
key: arrowKey,
child: new StockArrow(percentChange: stock.percentChange),
margin: const EdgeDims.only(right: 5.0)
),
new Flexible(
child: new Row([
new Flexible(
flex: 2,
child: new Text(
stock.symbol,
key: symbolKey
)
),
new Flexible(
child: new Text(
lastSale,
style: const TextStyle(textAlign: TextAlign.right),
key: priceKey
)
),
new Flexible(
child: new Text(
changeInPrice,
style: Theme.of(context).text.caption.copyWith(textAlign: TextAlign.right)
)
),
],
alignItems: FlexAlignItems.baseline,
textBaseline: DefaultTextStyle.of(context).textBaseline
)
new Flexible(
child: new Row([
new Flexible(
flex: 2,
child: new Text(
stock.symbol,
key: symbolKey
)
),
new Flexible(
child: new Text(
lastSale,
style: const TextStyle(textAlign: TextAlign.right),
key: priceKey
)
),
new Flexible(
child: new Text(
changeInPrice,
style: Theme.of(context).text.caption.copyWith(textAlign: TextAlign.right)
)
),
],
alignItems: FlexAlignItems.baseline,
textBaseline: DefaultTextStyle.of(context).textBaseline
)
])
)
)
])
)
);
}

View file

@ -14,11 +14,26 @@ class TapGestureRecognizer extends PrimaryPointerGestureRecognizer {
: super(router: router);
GestureTapCallback onTap;
GestureTapCallback onTapDown;
GestureTapCallback onTapCancel;
void handlePrimaryPointer(sky.PointerEvent event) {
if (event.type == 'pointerup') {
if (event.type == 'pointerdown') {
if (onTapDown != null)
onTapDown();
} else if (event.type == 'pointerup') {
resolve(GestureDisposition.accepted);
onTap();
if (onTap != null)
onTap();
}
}
void rejectGesture(int pointer) {
super.rejectGesture(pointer);
if (pointer == primaryPointer) {
assert(state == GestureRecognizerState.defunct);
if (onTapCancel != null)
onTapCancel();
}
}
}

View file

@ -51,7 +51,7 @@ abstract class RenderToggleable extends RenderConstrainedBox {
);
}
void detatch() {
void detach() {
_tap.dispose();
_tap = null;
super.detach();

View file

@ -375,22 +375,20 @@ class YearPickerState extends ScrollableWidgetListState<YearPicker> {
for(int i = start; i < start + count; i++) {
int year = config.firstDate.year + i;
String label = year.toString();
Widget item = new GestureDetector(
Widget item = new InkWell(
key: new Key(label),
onTap: () {
DateTime result = new DateTime(year, config.selectedDate.month, config.selectedDate.day);
config.onChanged(result);
},
child: new InkWell(
child: new Container(
height: config.itemExtent,
decoration: year == config.selectedDate.year ? new BoxDecoration(
backgroundColor: Theme.of(context).primarySwatch[100],
shape: Shape.circle
) : null,
child: new Center(
child: new Text(label, style: style)
)
child: new Container(
height: config.itemExtent,
decoration: year == config.selectedDate.year ? new BoxDecoration(
backgroundColor: Theme.of(context).primarySwatch[100],
shape: Shape.circle
) : null,
child: new Center(
child: new Text(label, style: style)
)
)
);

View file

@ -5,6 +5,7 @@
import 'dart:async';
import 'package:sky/animation.dart';
import 'package:sky/gestures.dart';
import 'package:sky/material.dart';
import 'package:sky/src/widgets/basic.dart';
import 'package:sky/src/widgets/focus.dart';
@ -52,7 +53,7 @@ class Dialog extends StatelessComponent {
final List<Widget> actions;
/// An (optional) callback that is called when the dialog is dismissed.
final Function onDismiss;
final GestureTapCallback onDismiss;
Color _getColor(BuildContext context) {
switch (Theme.of(context).brightness) {

View file

@ -10,7 +10,6 @@ import 'package:sky/painting.dart';
import 'package:sky/src/widgets/basic.dart';
import 'package:sky/src/widgets/button_state.dart';
import 'package:sky/src/widgets/framework.dart';
import 'package:sky/src/widgets/gesture_detector.dart';
import 'package:sky/src/widgets/icon.dart';
import 'package:sky/src/widgets/ink_well.dart';
import 'package:sky/src/widgets/theme.dart';
@ -76,14 +75,12 @@ class DrawerItemState extends ButtonState<DrawerItem> {
)
);
return new GestureDetector(
onTap: config.onPressed,
child: new Container(
height: 48.0,
decoration: new BoxDecoration(backgroundColor: _getBackgroundColor(themeData)),
child: new InkWell(
child: new Row(flexChildren)
)
return new Container(
height: 48.0,
decoration: new BoxDecoration(backgroundColor: _getBackgroundColor(themeData)),
child: new InkWell(
onTap: config.onPressed,
child: new Row(flexChildren)
)
);
}

View file

@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:sky/gestures.dart';
import 'package:sky/material.dart';
import 'package:sky/src/widgets/basic.dart';
import 'package:sky/src/widgets/framework.dart';
@ -13,7 +14,7 @@ class FlatButton extends MaterialButton {
Key key,
Widget child,
bool enabled: true,
Function onPressed
GestureTapCallback onPressed
}) : super(key: key,
child: child,
enabled: enabled,

View file

@ -6,7 +6,6 @@ import 'package:sky/gestures.dart';
import 'package:sky/src/widgets/basic.dart';
import 'package:sky/src/widgets/button_state.dart';
import 'package:sky/src/widgets/framework.dart';
import 'package:sky/src/widgets/gesture_detector.dart';
import 'package:sky/src/widgets/icon.dart';
import 'package:sky/src/widgets/ink_well.dart';
import 'package:sky/src/widgets/material.dart';
@ -46,17 +45,15 @@ class FloatingActionButtonState extends ButtonState<FloatingActionButton> {
type: MaterialType.circle,
level: highlight ? 3 : 2,
child: new ClipOval(
child: new GestureDetector(
onTap: config.onPressed,
child: new Container(
width: _kSize,
height: _kSize,
child: new InkWell(
child: new Center(
child: new IconTheme(
data: new IconThemeData(color: iconThemeColor),
child: config.child
)
child: new Container(
width: _kSize,
height: _kSize,
child: new InkWell(
onTap: config.onPressed,
child: new Center(
child: new IconTheme(
data: new IconThemeData(color: iconThemeColor),
child: config.child
)
)
)

View file

@ -14,6 +14,8 @@ class GestureDetector extends StatefulComponent {
Key key,
this.child,
this.onTap,
this.onTapDown,
this.onTapCancel,
this.onShowPress,
this.onLongPress,
this.onVerticalDragStart,
@ -33,6 +35,9 @@ class GestureDetector extends StatefulComponent {
final Widget child;
final GestureTapCallback onTap;
final GestureTapCallback onTapDown;
final GestureTapCallback onTapCancel;
final GestureShowPressCallback onShowPress;
final GestureLongPressCallback onLongPress;
@ -97,11 +102,14 @@ class GestureDetectorState extends State<GestureDetector> {
}
void _syncTap() {
if (config.onTap == null) {
if (config.onTap == null && config.onTapDown == null && config.onTapCancel == null) {
_tap = _ensureDisposed(_tap);
} else {
_tap ??= new TapGestureRecognizer(router: _router);
_tap.onTap = config.onTap;
_tap
..onTap = config.onTap
..onTapDown = config.onTapDown
..onTapCancel = config.onTapCancel;
}
}

View file

@ -4,6 +4,7 @@
import 'dart:sky' as sky;
import 'package:sky/gestures.dart';
import 'package:sky/src/widgets/basic.dart';
import 'package:sky/src/widgets/icon.dart';
import 'package:sky/src/widgets/framework.dart';
@ -13,8 +14,8 @@ class IconButton extends StatelessComponent {
const IconButton({ Key key, this.icon, this.onPressed, this.color }) : super(key: key);
final String icon;
final Function onPressed;
final Color color;
final GestureTapCallback onPressed;
Widget build(BuildContext context) {
Widget child = new Icon(type: icon, size: 24);

View file

@ -2,16 +2,18 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'dart:math' as math;
import 'dart:sky' as sky;
import 'package:sky/animation.dart';
import 'package:sky/gestures.dart';
import 'package:sky/rendering.dart';
import 'package:sky/src/widgets/basic.dart';
import 'package:sky/src/widgets/framework.dart';
const int _kSplashInitialOpacity = 0x30;
const double _kSplashCancelledVelocity = 0.7;
const double _kSplashCanceledVelocity = 0.7;
const double _kSplashConfirmedVelocity = 0.7;
const double _kSplashInitialSize = 0.0;
const double _kSplashUnconfirmedVelocity = 0.2;
@ -25,7 +27,7 @@ double _getSplashTargetSize(Size bounds, Point position) {
}
class InkSplash {
InkSplash(this.pointer, this.position, this.well) {
InkSplash(this.position, this.well) {
_targetRadius = _getSplashTargetSize(well.size, position);
_radius = new AnimatedValue<double>(
_kSplashInitialSize, end: _targetRadius, curve: easeOut);
@ -33,11 +35,12 @@ class InkSplash {
_performance = new ValueAnimation<double>(
variable: _radius,
duration: new Duration(milliseconds: (_targetRadius / _kSplashUnconfirmedVelocity).floor())
)..addListener(_handleRadiusChange)
..play();
)..addListener(_handleRadiusChange);
// Wait kTapTimeout to avoid creating tiny splashes during scrolls.
_startTimer = new Timer(kTapTimeout, _play);
}
final int pointer;
final Point position;
final RenderInkWell well;
@ -45,20 +48,39 @@ class InkSplash {
double _pinnedRadius;
AnimatedValue<double> _radius;
AnimationPerformance _performance;
Timer _startTimer;
bool _cancelStartTimer() {
if (_startTimer != null) {
_startTimer.cancel();
_startTimer = null;
return true;
}
return false;
}
void _play() {
_cancelStartTimer();
_performance.play();
}
void _updateVelocity(double velocity) {
int duration = (_targetRadius / velocity).floor();
_performance
..duration = new Duration(milliseconds: duration)
..play();
_performance.duration = new Duration(milliseconds: duration);
_play();
}
void confirm() {
if (_cancelStartTimer())
return;
_updateVelocity(_kSplashConfirmedVelocity);
_pinnedRadius = null;
}
void cancel() {
_updateVelocity(_kSplashCancelledVelocity);
if (_cancelStartTimer())
return;
_updateVelocity(_kSplashCanceledVelocity);
_pinnedRadius = _radius.value;
}
@ -77,38 +99,95 @@ class InkSplash {
}
class RenderInkWell extends RenderProxyBox {
RenderInkWell({ RenderBox child }) : super(child);
RenderInkWell({
RenderBox child,
GestureTapCallback onTap,
GestureLongPressCallback onLongPress
}) : super(child) {
this.onTap = onTap;
this.onLongPress = onLongPress;
}
GestureTapCallback get onTap => _onTap;
GestureTapCallback _onTap;
void set onTap (GestureTapCallback value) {
_onTap = value;
_syncTapRecognizer();
}
GestureTapCallback get onLongPress => _onLongPress;
GestureTapCallback _onLongPress;
void set onLongPress (GestureTapCallback value) {
_onLongPress = value;
_syncLongPressRecognizer();
}
final List<InkSplash> _splashes = new List<InkSplash>();
TapGestureRecognizer _tap;
LongPressGestureRecognizer _longPress;
void handleEvent(sky.Event event, BoxHitTestEntry entry) {
// TODO(abarth): We should trigger these effects based on gestures.
// https://github.com/flutter/engine/issues/1271
if (event is sky.PointerEvent) {
switch (event.type) {
case 'pointerdown':
_startSplash(event.pointer, entry.localPosition);
break;
case 'pointerup':
_confirmSplash(event.pointer);
break;
}
if (event.type == 'pointerdown' && (_tap != null || _longPress != null)) {
_tap?.addPointer(event);
_longPress?.addPointer(event);
_splashes.add(new InkSplash(entry.localPosition, this));
}
}
void _startSplash(int pointer, Point position) {
_splashes.add(new InkSplash(pointer, position, this));
markNeedsPaint();
void attach() {
super.attach();
_syncTapRecognizer();
_syncLongPressRecognizer();
}
void _forEachSplash(int pointer, Function callback) {
_splashes.where((splash) => splash.pointer == pointer)
.forEach(callback);
void detach() {
_disposeTapRecognizer();
_disposeLongPressRecognizer();
super.detach();
}
void _confirmSplash(int pointer) {
_forEachSplash(pointer, (splash) { splash.confirm(); });
markNeedsPaint();
void _syncTapRecognizer() {
if (onTap == null) {
_disposeTapRecognizer();
} else {
_tap ??= new TapGestureRecognizer(router: FlutterBinding.instance.pointerRouter)
..onTap = _handleTap
..onTapCancel = _handleTapCancel;
}
}
void _disposeTapRecognizer() {
_tap?.dispose();
_tap = null;
}
void _syncLongPressRecognizer() {
if (onLongPress == null) {
_disposeLongPressRecognizer();
} else {
_longPress ??= new LongPressGestureRecognizer(router: FlutterBinding.instance.pointerRouter)
..onLongPress = _handleLongPress;
}
}
void _disposeLongPressRecognizer() {
_longPress?.dispose();
_longPress = null;
}
void _handleTap() {
_splashes.last?.confirm();
onTap();
}
void _handleTapCancel() {
_splashes.last?.cancel();
}
void _handleLongPress() {
_splashes.last?.confirm();
onLongPress();
}
void paint(PaintingContext context, Offset offset) {
@ -126,7 +205,20 @@ class RenderInkWell extends RenderProxyBox {
}
class InkWell extends OneChildRenderObjectWidget {
InkWell({ Key key, Widget child }) : super(key: key, child: child);
InkWell({
Key key,
Widget child,
this.onTap,
this.onLongPress
}) : super(key: key, child: child);
RenderInkWell createRenderObject() => new RenderInkWell();
final GestureTapCallback onTap;
final GestureLongPressCallback onLongPress;
RenderInkWell createRenderObject() => new RenderInkWell(onTap: onTap, onLongPress: onLongPress);
void updateRenderObject(RenderInkWell renderObject, InkWell oldWidget) {
renderObject.onTap = onTap;
renderObject.onLongPress = onLongPress;
}
}

View file

@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:sky/gestures.dart';
import 'package:sky/src/widgets/basic.dart';
import 'package:sky/src/widgets/button_state.dart';
import 'package:sky/src/widgets/framework.dart';
import 'package:sky/src/widgets/gesture_detector.dart';
import 'package:sky/src/widgets/ink_well.dart';
import 'package:sky/src/widgets/material.dart';
@ -22,7 +22,7 @@ abstract class MaterialButton extends StatefulComponent {
final Widget child;
final bool enabled;
final Function onPressed;
final GestureTapCallback onPressed;
}
abstract class MaterialButtonState<T extends MaterialButton> extends ButtonState<T> {
@ -37,17 +37,17 @@ abstract class MaterialButtonState<T extends MaterialButton> extends ButtonState
child: config.child // TODO(ianh): figure out a way to compell the child to have gray text when disabled...
)
);
return new GestureDetector(
onTap: config.enabled ? config.onPressed : null,
child: new Container(
height: 36.0,
constraints: new BoxConstraints(minWidth: 88.0),
margin: new EdgeDims.all(8.0),
child: new Material(
type: MaterialType.button,
child: config.enabled ? new InkWell(child: contents) : contents,
level: level,
color: getColor(context)
return new Container(
height: 36.0,
constraints: new BoxConstraints(minWidth: 88.0),
margin: new EdgeDims.all(8.0),
child: new Material(
type: MaterialType.button,
level: level,
color: getColor(context),
child: new InkWell(
onTap: config.enabled ? config.onPressed : null,
child: contents
)
)
);

View file

@ -11,7 +11,7 @@ import 'package:sky/painting.dart';
import 'package:sky/src/widgets/basic.dart';
import 'package:sky/src/widgets/focus.dart';
import 'package:sky/src/widgets/framework.dart';
import 'package:sky/src/widgets/gesture_detector.dart';
import 'package:sky/src/widgets/ink_well.dart';
import 'package:sky/src/widgets/navigator.dart';
import 'package:sky/src/widgets/popup_menu_item.dart';
import 'package:sky/src/widgets/scrollable.dart';
@ -94,7 +94,7 @@ class PopupMenuState extends State<PopupMenu> {
children.add(new FadeTransition(
performance: config.performance,
opacity: new AnimatedValue<double>(0.0, end: 1.0, interval: new Interval(start, end)),
child: new GestureDetector(
child: new InkWell(
onTap: () { config.navigator.pop(config.items[i].value); },
child: config.items[i]
))

View file

@ -4,7 +4,6 @@
import 'package:sky/src/widgets/basic.dart';
import 'package:sky/src/widgets/framework.dart';
import 'package:sky/src/widgets/ink_well.dart';
import 'package:sky/src/widgets/theme.dart';
const double _kMenuItemHeight = 48.0;
@ -21,15 +20,13 @@ class PopupMenuItem extends StatelessComponent {
final dynamic value;
Widget build(BuildContext context) {
return new InkWell(
child: new Container(
height: _kMenuItemHeight,
child: new DefaultTextStyle(
style: Theme.of(context).text.subhead,
child: new Baseline(
baseline: _kMenuItemHeight - _kBaselineOffsetFromBottom,
child: child
)
return new Container(
height: _kMenuItemHeight,
child: new DefaultTextStyle(
style: Theme.of(context).text.subhead,
child: new Baseline(
baseline: _kMenuItemHeight - _kBaselineOffsetFromBottom,
child: child
)
)
);

View file

@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:sky/gestures.dart';
import 'package:sky/material.dart';
import 'package:sky/src/widgets/basic.dart';
import 'package:sky/src/widgets/framework.dart';
@ -13,7 +14,7 @@ class RaisedButton extends MaterialButton {
Key key,
Widget child,
bool enabled: true,
Function onPressed
GestureTapCallback onPressed
}) : super(key: key,
child: child,
enabled: enabled,

View file

@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:sky/animation.dart';
import 'package:sky/painting.dart';
import 'package:sky/gestures.dart';
import 'package:sky/material.dart';
import 'package:sky/painting.dart';
import 'package:sky/src/widgets/animated_component.dart';
import 'package:sky/src/widgets/basic.dart';
import 'package:sky/src/widgets/framework.dart';
@ -28,7 +28,7 @@ class SnackBarAction extends StatelessComponent {
}
final String label;
final Function onPressed;
final GestureTapCallback onPressed;
Widget build(BuildContext) {
return new GestureDetector(

View file

@ -7,9 +7,10 @@ import 'dart:sky' as sky;
import 'package:newton/newton.dart';
import 'package:sky/animation.dart';
import 'package:sky/gestures.dart';
import 'package:sky/material.dart';
import 'package:sky/painting.dart';
import 'package:sky/rendering.dart';
import 'package:sky/material.dart';
import 'package:sky/src/widgets/basic.dart';
import 'package:sky/src/widgets/framework.dart';
import 'package:sky/src/widgets/gesture_detector.dart';
@ -307,6 +308,7 @@ class TabLabel {
class Tab extends StatelessComponent {
Tab({
Key key,
this.onSelected,
this.label,
this.color,
this.selected: false,
@ -315,6 +317,7 @@ class Tab extends StatelessComponent {
assert(label.text != null || label.icon != null);
}
final GestureTapCallback onSelected;
final TabLabel label;
final Color color;
final bool selected;
@ -359,7 +362,10 @@ class Tab extends StatelessComponent {
padding: _kTabLabelPadding
);
return new InkWell(child: centeredLabel);
return new InkWell(
onTap: onSelected,
child: centeredLabel
);
}
}
@ -458,7 +464,7 @@ class TabBarState extends ScrollableState<TabBar> {
.clamp(scrollBehavior.minScrollOffset, scrollBehavior.maxScrollOffset);
}
void _handleTap(int tabIndex) {
void _handleTabSelected(int tabIndex) {
if (tabIndex != config.selectedIndex) {
if (_tabWidths != null) {
if (config.isScrollable)
@ -471,14 +477,12 @@ class TabBarState extends ScrollableState<TabBar> {
}
Widget _toTab(TabLabel label, int tabIndex, Color color, Color selectedColor) {
return new GestureDetector(
onTap: () => _handleTap(tabIndex),
child: new Tab(
label: label,
color: color,
selected: tabIndex == config.selectedIndex,
selectedColor: selectedColor
)
return new Tab(
onSelected: () => _handleTabSelected(tabIndex),
label: label,
color: color,
selected: tabIndex == config.selectedIndex,
selectedColor: selectedColor
);
}