mirror of
https://github.com/flutter/flutter
synced 2024-09-19 08:11:56 +00:00
Move example fn widgets into sky/framework/components
Moving these files into sky/framework will make them easier to use from the SDK. Also, this CL also splits up the giant "widgets" library into smaller libraries, one per component. TBR=eseidel@chromium.org Review URL: https://codereview.chromium.org/993033003
This commit is contained in:
parent
0f99e272d8
commit
5d27d7b223
|
@ -1,47 +0,0 @@
|
|||
part of widgets;
|
||||
|
||||
class Box extends Component {
|
||||
|
||||
static Style _style = new Style('''
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-radius: 4px;
|
||||
border: 1px solid gray;
|
||||
margin: 10px;'''
|
||||
);
|
||||
|
||||
static Style _titleStyle = new Style('''
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
font-size: 10px;
|
||||
padding: 8px 8px 4px 8px;'''
|
||||
);
|
||||
|
||||
static Style _contentStyle = new Style('''
|
||||
flex: 1;
|
||||
padding: 4px 8px 8px 8px;'''
|
||||
);
|
||||
|
||||
String title;
|
||||
List<Node> children;
|
||||
|
||||
Box({String key, this.title, this.children }) : super(key: key);
|
||||
|
||||
Node build() {
|
||||
return new Container(
|
||||
style: _style,
|
||||
children: [
|
||||
new Container(
|
||||
key: 'Title',
|
||||
style: _titleStyle,
|
||||
children: [new Text(title)]
|
||||
),
|
||||
new Container(
|
||||
key: 'Content',
|
||||
style: _contentStyle,
|
||||
children: children
|
||||
),
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
part of widgets;
|
||||
|
||||
class Button extends ButtonBase {
|
||||
|
||||
static Style _style = new Style('''
|
||||
transform: translateX(0);
|
||||
display: inline-flex;
|
||||
border-radius: 4px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border: 1px solid blue;
|
||||
-webkit-user-select: none;
|
||||
margin: 5px;'''
|
||||
);
|
||||
|
||||
static Style _highlightStyle = new Style('''
|
||||
transform: translateX(0);
|
||||
display: inline-flex;
|
||||
border-radius: 4px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border: 1px solid blue;
|
||||
-webkit-user-select: none;
|
||||
margin: 5px;
|
||||
background-color: orange;'''
|
||||
);
|
||||
|
||||
Node content;
|
||||
|
||||
Button({ Object key, this.content }) : super(key: key);
|
||||
|
||||
Node build() {
|
||||
return new Container(
|
||||
key: 'Button',
|
||||
style: _highlight ? _highlightStyle : _style,
|
||||
children: [super.build(), content]
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
part of widgets;
|
||||
|
||||
abstract class ButtonBase extends MaterialComponent {
|
||||
|
||||
bool _highlight = false;
|
||||
|
||||
ButtonBase({ Object key }) : super(key: key) {
|
||||
events.listen('pointerdown', _handlePointerDown);
|
||||
events.listen('pointerup', _handlePointerUp);
|
||||
events.listen('pointercancel', _handlePointerCancel);
|
||||
}
|
||||
|
||||
void _handlePointerDown(_) {
|
||||
setState(() {
|
||||
_highlight = true;
|
||||
});
|
||||
}
|
||||
void _handlePointerUp(_) {
|
||||
setState(() {
|
||||
_highlight = false;
|
||||
});
|
||||
}
|
||||
void _handlePointerCancel(_) {
|
||||
setState(() {
|
||||
_highlight = false;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
part of widgets;
|
||||
|
||||
class Checkbox extends ButtonBase {
|
||||
|
||||
bool checked;
|
||||
ValueChanged onChanged;
|
||||
|
||||
static Style _style = new Style('''
|
||||
transform: translateX(0);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
-webkit-user-select: none;
|
||||
cursor: pointer;
|
||||
width: 30px;
|
||||
height: 30px;'''
|
||||
);
|
||||
|
||||
static Style _containerStyle = new Style('''
|
||||
border: solid 2px;
|
||||
border-color: rgba(90, 90, 90, 0.25);
|
||||
width: 10px;
|
||||
height: 10px;'''
|
||||
);
|
||||
|
||||
static Style _containerHighlightStyle = new Style('''
|
||||
border: solid 2px;
|
||||
border-color: rgba(90, 90, 90, 0.25);
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 10px;
|
||||
background-color: orange;
|
||||
border-color: orange;'''
|
||||
);
|
||||
|
||||
static Style _uncheckedStyle = new Style('''
|
||||
top: 0px;
|
||||
left: 0px;'''
|
||||
);
|
||||
|
||||
static Style _checkedStyle = new Style('''
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
transform: translate(2px, -15px) rotate(45deg);
|
||||
width: 10px;
|
||||
height: 20px;
|
||||
border-style: solid;
|
||||
border-top: none;
|
||||
border-left: none;
|
||||
border-right-width: 2px;
|
||||
border-bottom-width: 2px;
|
||||
border-color: #0f9d58;'''
|
||||
);
|
||||
|
||||
Checkbox({ Object key, this.onChanged, this.checked }) : super(key: key);
|
||||
|
||||
Node build() {
|
||||
return new Container(
|
||||
style: _style,
|
||||
children: [
|
||||
super.build(),
|
||||
new Container(
|
||||
style: _highlight ? _containerHighlightStyle : _containerStyle,
|
||||
children: [
|
||||
new Container(
|
||||
style: checked ? _checkedStyle : _uncheckedStyle
|
||||
)
|
||||
]
|
||||
)
|
||||
]
|
||||
)..events.listen('click', _handleClick);
|
||||
}
|
||||
|
||||
void _handleClick(sky.Event e) {
|
||||
onChanged(!checked);
|
||||
}
|
||||
}
|
|
@ -1,159 +0,0 @@
|
|||
part of widgets;
|
||||
|
||||
const double _kWidth = 256.0;
|
||||
const double _kMinFlingVelocity = 0.4;
|
||||
const double _kBaseSettleDurationMS = 246.0;
|
||||
const double _kMaxSettleDurationMS = 600.0;
|
||||
const Cubic _kAnimationCurve = easeOut;
|
||||
|
||||
class DrawerAnimation extends Animation {
|
||||
|
||||
Stream<double> get onPositionChanged => onValueChanged;
|
||||
|
||||
bool get _isMostlyClosed => value <= -_kWidth / 2;
|
||||
|
||||
DrawerAnimation() {
|
||||
value = -_kWidth;
|
||||
}
|
||||
|
||||
void toggle(_) => _isMostlyClosed ? _open() : _close();
|
||||
|
||||
void handleMaskTap(_) => _close();
|
||||
|
||||
void handlePointerDown(_) => stop();
|
||||
|
||||
void handlePointerMove(sky.PointerEvent event) {
|
||||
assert(!isAnimating);
|
||||
value = math.min(0.0, math.max(value + event.dx, -_kWidth));
|
||||
}
|
||||
|
||||
void handlePointerUp(_) {
|
||||
if (!isAnimating)
|
||||
_settle();
|
||||
}
|
||||
|
||||
void handlePointerCancel(_) {
|
||||
if (!isAnimating)
|
||||
_settle();
|
||||
}
|
||||
|
||||
void _open() => _animateToPosition(0.0);
|
||||
|
||||
void _close() => _animateToPosition(-_kWidth);
|
||||
|
||||
void _settle() => _isMostlyClosed ? _close() : _open();
|
||||
|
||||
void _animateToPosition(double targetPosition) {
|
||||
double distance = (targetPosition - value).abs();
|
||||
if (distance != 0) {
|
||||
double targetDuration = distance / _kWidth * _kBaseSettleDurationMS;
|
||||
double duration = math.min(targetDuration, _kMaxSettleDurationMS);
|
||||
animateTo(targetPosition, duration, curve: _kAnimationCurve);
|
||||
}
|
||||
}
|
||||
|
||||
void handleFlingStart(event) {
|
||||
double direction = event.velocityX.sign;
|
||||
double velocityX = event.velocityX.abs() / 1000;
|
||||
if (velocityX < _kMinFlingVelocity)
|
||||
return;
|
||||
|
||||
double targetPosition = direction < 0.0 ? -_kWidth : 0.0;
|
||||
double distance = (targetPosition - value).abs();
|
||||
double duration = distance / velocityX;
|
||||
|
||||
animateTo(targetPosition, duration, curve: linear);
|
||||
}
|
||||
}
|
||||
|
||||
class Drawer extends Component {
|
||||
|
||||
static Style _style = new Style('''
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
box-shadpw: ${Shadow[3]};'''
|
||||
);
|
||||
|
||||
static Style _maskStyle = new Style('''
|
||||
background-color: black;
|
||||
will-change: opacity;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;'''
|
||||
);
|
||||
|
||||
static Style _contentStyle = new Style('''
|
||||
background-color: ${Grey[50]};
|
||||
will-change: transform;
|
||||
position: absolute;
|
||||
z-index: 3;
|
||||
width: 256px;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;'''
|
||||
);
|
||||
|
||||
DrawerAnimation animation;
|
||||
List<Node> children;
|
||||
|
||||
Drawer({
|
||||
Object key,
|
||||
this.animation,
|
||||
this.children
|
||||
}) : super(key: key);
|
||||
|
||||
double _position = -_kWidth;
|
||||
|
||||
bool _listening = false;
|
||||
|
||||
void _ensureListening() {
|
||||
if (_listening)
|
||||
return;
|
||||
|
||||
_listening = true;
|
||||
animation.onPositionChanged.listen((position) {
|
||||
setState(() {
|
||||
_position = position;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Node build() {
|
||||
_ensureListening();
|
||||
|
||||
bool isClosed = _position <= -_kWidth;
|
||||
String inlineStyle = 'display: ${isClosed ? 'none' : ''}';
|
||||
String maskInlineStyle = 'opacity: ${(_position / _kWidth + 1) * 0.25}';
|
||||
String contentInlineStyle = 'transform: translateX(${_position}px)';
|
||||
|
||||
Container mask = new Container(
|
||||
key: 'Mask',
|
||||
style: _maskStyle,
|
||||
inlineStyle: maskInlineStyle
|
||||
)..events.listen('gesturetap', animation.handleMaskTap)
|
||||
..events.listen('gestureflingstart', animation.handleFlingStart);
|
||||
|
||||
Container content = new Container(
|
||||
key: 'Content',
|
||||
style: _contentStyle,
|
||||
inlineStyle: contentInlineStyle,
|
||||
children: children
|
||||
);
|
||||
|
||||
return new Container(
|
||||
style: _style,
|
||||
inlineStyle: inlineStyle,
|
||||
children: [ mask, content ]
|
||||
)..events.listen('pointerdown', animation.handlePointerDown)
|
||||
..events.listen('pointermove', animation.handlePointerMove)
|
||||
..events.listen('pointerup', animation.handlePointerUp)
|
||||
..events.listen('pointercancel', animation.handlePointerCancel);
|
||||
|
||||
}
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
part of widgets;
|
||||
|
||||
class DrawerHeader extends Component {
|
||||
|
||||
static Style _style = new Style('''
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 140px;
|
||||
-webkit-user-select: none;
|
||||
background-color: ${BlueGrey[50]};
|
||||
border-bottom: 1px solid #D1D9E1;
|
||||
padding-bottom: 7px;
|
||||
margin-bottom: 8px;'''
|
||||
);
|
||||
|
||||
static Style _spacerStyle = new Style('''
|
||||
flex: 1'''
|
||||
);
|
||||
|
||||
static Style _labelStyle = new Style('''
|
||||
padding: 0 16px;
|
||||
font-family: 'Roboto Medium', 'Helvetica';
|
||||
color: #212121;'''
|
||||
);
|
||||
|
||||
List<Node> children;
|
||||
|
||||
DrawerHeader({ Object key, this.children }) : super(key: key);
|
||||
|
||||
Node build() {
|
||||
return new Container(
|
||||
style: _style,
|
||||
children: [
|
||||
new Container(
|
||||
key: 'Spacer',
|
||||
style: _spacerStyle
|
||||
),
|
||||
new Container(
|
||||
key: 'Label',
|
||||
style: _labelStyle,
|
||||
children: children
|
||||
)
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,146 +0,0 @@
|
|||
part of widgets;
|
||||
|
||||
abstract class FixedHeightScrollable extends Component {
|
||||
|
||||
// TODO(rafaelw): This component really shouldn't have an opinion
|
||||
// about how it is sized. The owning component should decide whether
|
||||
// it's explicitly sized or flexible or whatever...
|
||||
static Style _style = new Style('''
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
flex: 1;
|
||||
will-change: transform;'''
|
||||
);
|
||||
|
||||
static Style _scrollAreaStyle = new Style('''
|
||||
position:relative;
|
||||
will-change: transform;'''
|
||||
);
|
||||
|
||||
double minOffset;
|
||||
double maxOffset;
|
||||
|
||||
double _scrollOffset = 0.0;
|
||||
FlingCurve _flingCurve;
|
||||
int _flingAnimationId;
|
||||
double _height = 0.0;
|
||||
double _itemHeight;
|
||||
|
||||
FixedHeightScrollable({
|
||||
Object key,
|
||||
this.minOffset,
|
||||
this.maxOffset
|
||||
}) : super(key: key) {}
|
||||
|
||||
List<Node> buildItems(int start, int count);
|
||||
|
||||
void didMount() {
|
||||
var root = getRoot();
|
||||
var item = root.firstChild.firstChild;
|
||||
sky.ClientRect scrollRect = root.getBoundingClientRect();
|
||||
sky.ClientRect itemRect = item.getBoundingClientRect();
|
||||
assert(scrollRect.height > 0);
|
||||
assert(itemRect.height > 0);
|
||||
|
||||
setState(() {
|
||||
_height = scrollRect.height;
|
||||
_itemHeight = itemRect.height;
|
||||
});
|
||||
}
|
||||
|
||||
Node build() {
|
||||
var itemNumber = 0;
|
||||
var drawCount = 1;
|
||||
var transformStyle = '';
|
||||
|
||||
if (_height > 0.0) {
|
||||
drawCount = (_height / _itemHeight).round() + 1;
|
||||
double alignmentDelta = -_scrollOffset % _itemHeight;
|
||||
if (alignmentDelta != 0.0) {
|
||||
alignmentDelta -= _itemHeight;
|
||||
}
|
||||
|
||||
double drawStart = _scrollOffset + alignmentDelta;
|
||||
itemNumber = (drawStart / _itemHeight).floor();
|
||||
|
||||
transformStyle =
|
||||
'transform: translateY(${(alignmentDelta).toStringAsFixed(2)}px)';
|
||||
}
|
||||
|
||||
return new Container(
|
||||
style: _style,
|
||||
children: [
|
||||
new Container(
|
||||
style: _scrollAreaStyle,
|
||||
inlineStyle: transformStyle,
|
||||
children: buildItems(itemNumber, drawCount)
|
||||
)
|
||||
]
|
||||
)
|
||||
..events.listen('gestureflingstart', _handleFlingStart)
|
||||
..events.listen('gestureflingcancel', _handleFlingCancel)
|
||||
..events.listen('gesturescrollupdate', _handleScrollUpdate)
|
||||
..events.listen('wheel', _handleWheel);
|
||||
}
|
||||
|
||||
void didUnmount() {
|
||||
_stopFling();
|
||||
}
|
||||
|
||||
bool _scrollBy(double scrollDelta) {
|
||||
var newScrollOffset = _scrollOffset + scrollDelta;
|
||||
if (minOffset != null && newScrollOffset < minOffset) {
|
||||
newScrollOffset = minOffset;
|
||||
} else if (maxOffset != null && newScrollOffset > maxOffset) {
|
||||
newScrollOffset = maxOffset;
|
||||
}
|
||||
if (newScrollOffset == _scrollOffset) {
|
||||
return false;
|
||||
}
|
||||
|
||||
setState(() {
|
||||
_scrollOffset = newScrollOffset;
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
void _scheduleFlingUpdate() {
|
||||
_flingAnimationId = sky.window.requestAnimationFrame(_updateFling);
|
||||
}
|
||||
|
||||
void _stopFling() {
|
||||
if (_flingAnimationId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
sky.window.cancelAnimationFrame(_flingAnimationId);
|
||||
_flingCurve = null;
|
||||
_flingAnimationId = null;
|
||||
}
|
||||
|
||||
void _updateFling(double timeStamp) {
|
||||
double scrollDelta = _flingCurve.update(timeStamp);
|
||||
if (!_scrollBy(scrollDelta))
|
||||
return _stopFling();
|
||||
_scheduleFlingUpdate();
|
||||
}
|
||||
|
||||
void _handleScrollUpdate(sky.GestureEvent event) {
|
||||
_scrollBy(-event.dy);
|
||||
}
|
||||
|
||||
void _handleFlingStart(sky.GestureEvent event) {
|
||||
setState(() {
|
||||
_flingCurve = new FlingCurve(-event.velocityY, event.timeStamp);
|
||||
_scheduleFlingUpdate();
|
||||
});
|
||||
}
|
||||
|
||||
void _handleFlingCancel(sky.GestureEvent event) {
|
||||
_stopFling();
|
||||
}
|
||||
|
||||
void _handleWheel(sky.WheelEvent event) {
|
||||
_scrollBy(-event.offsetY);
|
||||
}
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
part of widgets;
|
||||
|
||||
class FloatingActionButton extends MaterialComponent {
|
||||
static final Style _style = new Style('''
|
||||
position: absolute;
|
||||
bottom: 16px;
|
||||
right: 16px;
|
||||
z-index: 5;
|
||||
transform: translateX(0);
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
background-color: ${Red[500]};
|
||||
color: white;
|
||||
box-shadow: ${Shadow[3]};
|
||||
border-radius: 28px;'''
|
||||
);
|
||||
static final Style _clipStyle = new Style('''
|
||||
transform: translateX(0);
|
||||
position: absolute;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
-webkit-clip-path: circle(28px at center);''');
|
||||
|
||||
Node content;
|
||||
|
||||
FloatingActionButton({ Object key, this.content }) : super(key: key);
|
||||
|
||||
Node build() {
|
||||
List<Node> children = [super.build()];
|
||||
|
||||
if (content != null)
|
||||
children.add(content);
|
||||
|
||||
return new Container(
|
||||
key: "Container",
|
||||
style: _style,
|
||||
children: [
|
||||
new Container(
|
||||
key: "Clip",
|
||||
style: _clipStyle,
|
||||
children: children
|
||||
)
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
part of widgets;
|
||||
|
||||
const String kAssetBase = '/sky/assets/material-design-icons';
|
||||
|
||||
class Icon extends Component {
|
||||
|
||||
Style style;
|
||||
int size;
|
||||
String type;
|
||||
|
||||
Icon({
|
||||
String key,
|
||||
this.style,
|
||||
this.size,
|
||||
this.type: ''
|
||||
}) : super(key: key);
|
||||
|
||||
Node build() {
|
||||
String category = '';
|
||||
String subtype = '';
|
||||
List<String> parts = type.split('/');
|
||||
if (parts.length == 2) {
|
||||
category = parts[0];
|
||||
subtype = parts[1];
|
||||
}
|
||||
|
||||
return new Image(
|
||||
style: style,
|
||||
width: size,
|
||||
height: size,
|
||||
src: '${kAssetBase}/${category}/2x_web/ic_${subtype}_${size}dp.png'
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,96 +0,0 @@
|
|||
part of widgets;
|
||||
|
||||
const double _kSplashSize = 400.0;
|
||||
const double _kSplashDuration = 500.0;
|
||||
|
||||
class SplashAnimation {
|
||||
AnimationGenerator _animation;
|
||||
double _offsetX;
|
||||
double _offsetY;
|
||||
|
||||
Stream<String> _styleChanged;
|
||||
|
||||
Stream<String> get onStyleChanged => _styleChanged;
|
||||
|
||||
void cancel() => _animation.cancel();
|
||||
|
||||
SplashAnimation(sky.ClientRect rect, double x, double y,
|
||||
{ Function onDone })
|
||||
: _offsetX = x - rect.left,
|
||||
_offsetY = y - rect.top {
|
||||
|
||||
_animation = new AnimationGenerator(_kSplashDuration,
|
||||
end: _kSplashSize, curve: easeOut, onDone: onDone);
|
||||
|
||||
_styleChanged = _animation.onTick.map((p) => '''
|
||||
top: ${_offsetY - p/2}px;
|
||||
left: ${_offsetX - p/2}px;
|
||||
width: ${p}px;
|
||||
height: ${p}px;
|
||||
border-radius: ${p}px;
|
||||
opacity: ${1.0 - (p / _kSplashSize)};
|
||||
''');
|
||||
}
|
||||
}
|
||||
|
||||
class InkSplash extends Component {
|
||||
|
||||
Stream<String> onStyleChanged;
|
||||
|
||||
static Style _style = new Style('''
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
overflow: hidden;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
''');
|
||||
|
||||
static Style _splashStyle = new Style('''
|
||||
position: absolute;
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
border-radius: 0;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 0;
|
||||
width: 0;
|
||||
''');
|
||||
|
||||
double _offsetX;
|
||||
double _offsetY;
|
||||
String _inlineStyle;
|
||||
|
||||
InkSplash(Stream<String> onStyleChanged)
|
||||
: onStyleChanged = onStyleChanged,
|
||||
super(stateful: true, key: onStyleChanged.hashCode);
|
||||
|
||||
bool _listening = false;
|
||||
|
||||
void _ensureListening() {
|
||||
if (_listening)
|
||||
return;
|
||||
|
||||
_listening = true;
|
||||
|
||||
onStyleChanged.listen((style) {
|
||||
setState(() {
|
||||
_inlineStyle = style;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Node build() {
|
||||
_ensureListening();
|
||||
|
||||
return new Container(
|
||||
style: _style,
|
||||
children: [
|
||||
new Container(
|
||||
inlineStyle: _inlineStyle,
|
||||
style: _splashStyle
|
||||
)
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
part of widgets;
|
||||
|
||||
abstract class MaterialComponent extends Component {
|
||||
|
||||
static const _splashesKey = const Object();
|
||||
|
||||
static Style _style = new Style('''
|
||||
transform: translateX(0);
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0'''
|
||||
);
|
||||
|
||||
LinkedHashSet<SplashAnimation> _splashes;
|
||||
|
||||
MaterialComponent({ Object key }) : super(key: key);
|
||||
|
||||
Node build() {
|
||||
List<Node> children = [];
|
||||
|
||||
if (_splashes != null) {
|
||||
children.addAll(_splashes.map((s) => new InkSplash(s.onStyleChanged)));
|
||||
}
|
||||
|
||||
return new Container(
|
||||
style: _style,
|
||||
children: children,
|
||||
key: _splashesKey
|
||||
)..events.listen('gesturescrollstart', _cancelSplashes)
|
||||
..events.listen('wheel', _cancelSplashes)
|
||||
..events.listen('pointerdown', _startSplash);
|
||||
}
|
||||
|
||||
sky.ClientRect _getBoundingRect() => (getRoot() as sky.Element).getBoundingClientRect();
|
||||
|
||||
void _startSplash(sky.PointerEvent event) {
|
||||
setState(() {
|
||||
if (_splashes == null) {
|
||||
_splashes = new LinkedHashSet<SplashAnimation>();
|
||||
}
|
||||
|
||||
var splash;
|
||||
splash = new SplashAnimation(_getBoundingRect(), event.x, event.y,
|
||||
onDone: () { _splashDone(splash); });
|
||||
|
||||
_splashes.add(splash);
|
||||
});
|
||||
}
|
||||
|
||||
void _cancelSplashes(sky.Event event) {
|
||||
if (_splashes == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() {
|
||||
var splashes = _splashes;
|
||||
_splashes = null;
|
||||
splashes.forEach((s) { s.cancel(); });
|
||||
});
|
||||
}
|
||||
|
||||
void didUnmount() {
|
||||
_cancelSplashes(null);
|
||||
}
|
||||
|
||||
void _splashDone(SplashAnimation splash) {
|
||||
if (_splashes == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() {
|
||||
_splashes.remove(splash);
|
||||
if (_splashes.length == 0) {
|
||||
_splashes = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
part of widgets;
|
||||
|
||||
class MenuDivider extends Component {
|
||||
|
||||
static Style _style = new Style('''
|
||||
margin: 8px 0;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.12);'''
|
||||
);
|
||||
|
||||
MenuDivider({ Object key }) : super(key: key);
|
||||
|
||||
Node build() {
|
||||
return new Container(
|
||||
style: _style
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
part of widgets;
|
||||
|
||||
class MenuItem extends ButtonBase {
|
||||
|
||||
static Style _style = new Style('''
|
||||
transform: translateX(0);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 48px;
|
||||
-webkit-user-select: none;'''
|
||||
);
|
||||
|
||||
static Style _highlightStyle = new Style('''
|
||||
transform: translateX(0);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 48px;
|
||||
background: rgba(153, 153, 153, 0.4);
|
||||
-webkit-user-select: none;'''
|
||||
);
|
||||
|
||||
static Style _iconStyle = new Style('''
|
||||
padding: 0px 16px;'''
|
||||
);
|
||||
|
||||
static Style _labelStyle = new Style('''
|
||||
font-family: 'Roboto Medium', 'Helvetica';
|
||||
color: #212121;
|
||||
padding: 0px 16px;
|
||||
flex: 1;'''
|
||||
);
|
||||
|
||||
List<Node> children;
|
||||
String icon;
|
||||
|
||||
MenuItem({ Object key, this.icon, this.children }) : super(key: key);
|
||||
|
||||
Node build() {
|
||||
return new Container(
|
||||
style: _highlight ? _highlightStyle : _style,
|
||||
children: [
|
||||
super.build(),
|
||||
new Icon(
|
||||
style: _iconStyle,
|
||||
size: 24,
|
||||
type: "${icon}_grey600"
|
||||
),
|
||||
new Container(
|
||||
style: _labelStyle,
|
||||
children: children
|
||||
)
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
part of widgets;
|
||||
|
||||
class Radio extends ButtonBase {
|
||||
|
||||
Object value;
|
||||
Object groupValue;
|
||||
ValueChanged onChanged;
|
||||
|
||||
static Style _style = new Style('''
|
||||
transform: translateX(0);
|
||||
display: inline-block;
|
||||
-webkit-user-select: none;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-radius: 7px;
|
||||
border: 1px solid blue;
|
||||
margin: 0 5px;'''
|
||||
);
|
||||
|
||||
static Style _highlightStyle = new Style('''
|
||||
transform: translateX(0);
|
||||
display: inline-block;
|
||||
-webkit-user-select: none;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-radius: 7px;
|
||||
border: 1px solid blue;
|
||||
margin: 0 5px;
|
||||
background-color: orange;'''
|
||||
);
|
||||
|
||||
static Style _dotStyle = new Style('''
|
||||
-webkit-user-select: none;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 5px;
|
||||
background-color: black;
|
||||
margin: 2px;'''
|
||||
);
|
||||
|
||||
Radio({
|
||||
Object key,
|
||||
this.onChanged,
|
||||
this.value,
|
||||
this.groupValue
|
||||
}) : super(key: key);
|
||||
|
||||
Node build() {
|
||||
return new Container(
|
||||
style: _highlight ? _highlightStyle : _style,
|
||||
children: value == groupValue ?
|
||||
[super.build(), new Container( style : _dotStyle )] : [super.build()]
|
||||
)..events.listen('click', _handleClick);
|
||||
}
|
||||
|
||||
void _handleClick(_) {
|
||||
onChanged(value);
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
part of widgets;
|
||||
|
||||
class Toolbar extends Component {
|
||||
|
||||
List<Node> children;
|
||||
|
||||
static final Style _style = new Style('''
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 56px;
|
||||
z-index: 1;
|
||||
background-color: ${Purple[500]};
|
||||
color: white;
|
||||
box-shadow: ${Shadow[2]};'''
|
||||
);
|
||||
|
||||
Toolbar({String key, this.children}) : super(key: key);
|
||||
|
||||
Node build() {
|
||||
return new Container(
|
||||
style: _style,
|
||||
children: children
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
library widgets;
|
||||
|
||||
import '../../../framework/animation/curves.dart';
|
||||
import '../../../framework/animation/fling-curve.dart';
|
||||
import '../../../framework/animation/generator.dart';
|
||||
import '../../../framework/fn.dart';
|
||||
import '../../../framework/theme/colors.dart';
|
||||
import '../../../framework/theme/shadows.dart';
|
||||
import 'dart:collection';
|
||||
import 'dart:async';
|
||||
import 'dart:math' as math;
|
||||
import 'dart:sky' as sky;
|
||||
|
||||
part 'box.dart';
|
||||
part 'button.dart';
|
||||
part 'buttonbase.dart';
|
||||
part 'checkbox.dart';
|
||||
part 'drawer.dart';
|
||||
part 'drawerheader.dart';
|
||||
part 'fixedheightscrollable.dart';
|
||||
part 'icon.dart';
|
||||
part 'inksplash.dart';
|
||||
part 'material.dart';
|
||||
part 'menudivider.dart';
|
||||
part 'menuitem.dart';
|
||||
part 'radio.dart';
|
||||
part 'toolbar.dart';
|
||||
part 'floating_action_button.dart';
|
||||
|
||||
typedef void ValueChanged(value);
|
|
@ -1,6 +1,6 @@
|
|||
part of stocksapp;
|
||||
|
||||
class StockRow extends MaterialComponent {
|
||||
class StockRow extends Material {
|
||||
|
||||
Stock stock;
|
||||
|
||||
|
|
|
@ -1,8 +1,16 @@
|
|||
library stocksapp;
|
||||
|
||||
import '../../framework/fn.dart';
|
||||
import '../../framework/components/drawer.dart';
|
||||
import '../../framework/components/drawer_header.dart';
|
||||
import '../../framework/components/fixed_height_scrollable.dart';
|
||||
import '../../framework/components/floating_action_button.dart';
|
||||
import '../../framework/components/icon.dart';
|
||||
import '../../framework/components/material.dart';
|
||||
import '../../framework/components/menu_divider.dart';
|
||||
import '../../framework/components/menu_item.dart';
|
||||
import '../../framework/components/toolbar.dart';
|
||||
import '../data/stocks.dart';
|
||||
import '../fn/widgets/widgets.dart';
|
||||
import 'dart:math';
|
||||
|
||||
part 'stockarrow.dart';
|
||||
|
|
Loading…
Reference in a new issue