mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Merge pull request #211 from mpcomplete/rm.builder.3
Make SnackBar animate itself. This introduces a bit of a regression - the FloatingActionButton no longer animates. I'm not sure yet how to animated the FAB along with the SnackBar. Maybe some notion of anchoring a node to another. Also remove last use of AnimationBuilder.
This commit is contained in:
commit
24d40f858f
@ -52,7 +52,6 @@ dart_pkg("sky") {
|
||||
"lib/theme/view_configuration.dart",
|
||||
"lib/widgets/animated_component.dart",
|
||||
"lib/widgets/animated_container.dart",
|
||||
"lib/widgets/animation_builder.dart",
|
||||
"lib/widgets/basic.dart",
|
||||
"lib/widgets/block_viewport.dart",
|
||||
"lib/widgets/button_base.dart",
|
||||
|
||||
@ -3,9 +3,6 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:sky/editing/input.dart';
|
||||
import 'package:sky/animation/animated_value.dart';
|
||||
import 'package:sky/widgets/animated_component.dart';
|
||||
import 'package:sky/widgets/animation_builder.dart';
|
||||
import 'package:sky/theme/colors.dart' as colors;
|
||||
import 'package:sky/widgets/basic.dart';
|
||||
import 'package:sky/widgets/drawer.dart';
|
||||
@ -35,7 +32,7 @@ typedef void ModeUpdater(StockMode mode);
|
||||
|
||||
const Duration _kSnackbarSlideDuration = const Duration(milliseconds: 200);
|
||||
|
||||
class StockHome extends AnimatedComponent {
|
||||
class StockHome extends StatefulComponent {
|
||||
|
||||
StockHome(this.navigator, this.stocks, this.stockMode, this.modeUpdater);
|
||||
|
||||
@ -54,7 +51,8 @@ class StockHome extends AnimatedComponent {
|
||||
bool _isSearching = false;
|
||||
String _searchQuery;
|
||||
|
||||
AnimationBuilder _snackbarTransform;
|
||||
SnackBarStatus _snackBarStatus = SnackBarStatus.inactive;
|
||||
bool _isSnackBarShowing = false;
|
||||
|
||||
void _handleSearchBegin() {
|
||||
navigator.pushState(this, (_) {
|
||||
@ -140,6 +138,7 @@ class StockHome extends AnimatedComponent {
|
||||
Drawer buildDrawer() {
|
||||
if (_drawerStatus == DrawerStatus.inactive)
|
||||
return null;
|
||||
assert(_drawerShowing); // TODO(mpcomplete): this is always true
|
||||
return new Drawer(
|
||||
level: 3,
|
||||
showing: _drawerShowing,
|
||||
@ -264,40 +263,34 @@ class StockHome extends AnimatedComponent {
|
||||
|
||||
void _handleUndo() {
|
||||
setState(() {
|
||||
_snackbarTransform = null;
|
||||
_isSnackBarShowing = false;
|
||||
});
|
||||
}
|
||||
|
||||
Widget buildSnackBar() {
|
||||
if (_snackbarTransform == null)
|
||||
if (_snackBarStatus == SnackBarStatus.inactive)
|
||||
return null;
|
||||
return _snackbarTransform.build(
|
||||
new SnackBar(
|
||||
content: new Text("Stock purchased!"),
|
||||
actions: [new SnackBarAction(label: "UNDO", onPressed: _handleUndo)]
|
||||
));
|
||||
return new SnackBar(
|
||||
showing: _isSnackBarShowing,
|
||||
content: new Text("Stock purchased!"),
|
||||
actions: [new SnackBarAction(label: "UNDO", onPressed: _handleUndo)],
|
||||
onStatusChanged: (status) { setState(() { _snackBarStatus = status; }); }
|
||||
);
|
||||
}
|
||||
|
||||
void _handleStockPurchased() {
|
||||
setState(() {
|
||||
_snackbarTransform = new AnimationBuilder()
|
||||
..position = new AnimatedValue<Point>(const Point(0.0, 45.0), end: Point.origin);
|
||||
var performance = _snackbarTransform.createPerformance(
|
||||
[_snackbarTransform.position], duration: _kSnackbarSlideDuration);
|
||||
watch(performance); // TODO(mpcomplete): need to unwatch
|
||||
performance.play();
|
||||
_isSnackBarShowing = true;
|
||||
_snackBarStatus = SnackBarStatus.active;
|
||||
});
|
||||
}
|
||||
|
||||
Widget buildFloatingActionButton() {
|
||||
var widget = new FloatingActionButton(
|
||||
return new FloatingActionButton(
|
||||
child: new Icon(type: 'content/add', size: 24),
|
||||
backgroundColor: colors.RedAccent[200],
|
||||
onPressed: _handleStockPurchased
|
||||
);
|
||||
if (_snackbarTransform != null)
|
||||
widget = _snackbarTransform.build(widget);
|
||||
return widget;
|
||||
}
|
||||
|
||||
void addMenuToOverlays(List<Widget> overlays) {
|
||||
|
||||
@ -1,115 +0,0 @@
|
||||
// Copyright 2015 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:vector_math/vector_math.dart';
|
||||
|
||||
import 'package:sky/animation/animated_value.dart';
|
||||
import 'package:sky/animation/animation_performance.dart';
|
||||
import 'package:sky/painting/box_painter.dart';
|
||||
import 'package:sky/theme/shadows.dart';
|
||||
import 'package:sky/widgets/basic.dart';
|
||||
|
||||
// This class builds a Container object from a collection of optionally-
|
||||
// animated properties. Use syncFields to update the Container's properties,
|
||||
// which will optionally animate them using an AnimationPerformance.
|
||||
class AnimationBuilder {
|
||||
|
||||
AnimationBuilder();
|
||||
|
||||
AnimatedValue<double> opacity;
|
||||
AnimatedValue<Point> position;
|
||||
AnimatedValue<double> shadow;
|
||||
AnimatedColor backgroundColor;
|
||||
|
||||
// These don't animate, but are used to build the AnimationBuilder anyway.
|
||||
double borderRadius;
|
||||
Shape shape;
|
||||
|
||||
Map<AnimatedVariable, AnimationPerformance> _variableToPerformance =
|
||||
new Map<AnimatedVariable, AnimationPerformance>();
|
||||
|
||||
AnimationPerformance createPerformance(List<AnimatedValue> variables,
|
||||
{ Duration duration }) {
|
||||
AnimationPerformance performance = new AnimationPerformance()
|
||||
..duration = duration
|
||||
..variable = new AnimatedList(variables);
|
||||
for (AnimatedVariable variable in variables)
|
||||
_variableToPerformance[variable] = performance;
|
||||
return performance;
|
||||
}
|
||||
|
||||
Widget build(Widget child) {
|
||||
Widget current = child;
|
||||
if (shadow != null || backgroundColor != null ||
|
||||
borderRadius != null || shape != null) {
|
||||
current = new DecoratedBox(
|
||||
decoration: new BoxDecoration(
|
||||
borderRadius: borderRadius,
|
||||
shape: shape,
|
||||
boxShadow: shadow != null ? _computeShadow(shadow.value) : null,
|
||||
backgroundColor: backgroundColor != null ? backgroundColor.value : null),
|
||||
child: current);
|
||||
}
|
||||
|
||||
if (position != null) {
|
||||
Matrix4 transform = new Matrix4.identity();
|
||||
transform.translate(position.value.x, position.value.y);
|
||||
current = new Transform(transform: transform, child: current);
|
||||
}
|
||||
|
||||
if (opacity != null) {
|
||||
current = new Opacity(opacity: opacity.value, child: current);
|
||||
}
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
void updateFields({
|
||||
AnimatedValue<double> shadow,
|
||||
AnimatedColor backgroundColor,
|
||||
double borderRadius,
|
||||
Shape shape
|
||||
}) {
|
||||
_updateField(this.shadow, shadow);
|
||||
_updateField(this.backgroundColor, backgroundColor);
|
||||
this.borderRadius = borderRadius;
|
||||
this.shape = shape;
|
||||
}
|
||||
|
||||
void _updateField(AnimatedValue variable, AnimatedValue sourceVariable) {
|
||||
if (variable == null)
|
||||
return; // TODO(mpcomplete): Should we handle transition from null?
|
||||
|
||||
AnimationPerformance performance = _variableToPerformance[variable];
|
||||
if (performance == null) {
|
||||
// If there's no performance, no need to animate.
|
||||
if (sourceVariable != null)
|
||||
variable.value = sourceVariable.value;
|
||||
return;
|
||||
}
|
||||
|
||||
if (variable.value != sourceVariable.value) {
|
||||
variable
|
||||
..begin = variable.value
|
||||
..end = sourceVariable.value;
|
||||
performance
|
||||
..progress = 0.0
|
||||
..play();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<BoxShadow> _computeShadow(double level) {
|
||||
if (level < 1.0) // shadows[1] is the first shadow
|
||||
return null;
|
||||
|
||||
int level1 = level.floor();
|
||||
int level2 = level.ceil();
|
||||
double t = level - level1.toDouble();
|
||||
|
||||
List<BoxShadow> shadow = new List<BoxShadow>();
|
||||
for (int i = 0; i < shadows[level1].length; ++i)
|
||||
shadow.add(lerpBoxShadow(shadows[level1][i], shadows[level2][i], t));
|
||||
return shadow;
|
||||
}
|
||||
@ -6,6 +6,8 @@ import 'dart:sky' as sky;
|
||||
|
||||
import 'package:sky/animation/animated_value.dart';
|
||||
import 'package:sky/animation/animation_performance.dart';
|
||||
import 'package:sky/animation/curves.dart';
|
||||
import 'package:sky/base/lerp.dart';
|
||||
import 'package:sky/theme/shadows.dart';
|
||||
import 'package:sky/theme/colors.dart' as colors;
|
||||
import 'package:sky/widgets/animated_component.dart';
|
||||
|
||||
@ -49,7 +49,7 @@ class Material extends Component {
|
||||
|
||||
Widget build() {
|
||||
return new AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 1000),
|
||||
duration: const Duration(milliseconds: 200),
|
||||
decoration: new BoxDecoration(
|
||||
backgroundColor: _backgroundColor,
|
||||
borderRadius: edges[type],
|
||||
|
||||
@ -2,13 +2,27 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:sky/animation/animated_value.dart';
|
||||
import 'package:sky/animation/animation_performance.dart';
|
||||
import 'package:sky/painting/text_style.dart';
|
||||
import 'package:sky/theme/typography.dart' as typography;
|
||||
import 'package:sky/widgets/animated_component.dart';
|
||||
import 'package:sky/widgets/basic.dart';
|
||||
import 'package:sky/widgets/default_text_style.dart';
|
||||
import 'package:sky/widgets/material.dart';
|
||||
import 'package:sky/widgets/theme.dart';
|
||||
|
||||
import 'package:vector_math/vector_math.dart';
|
||||
|
||||
enum SnackBarStatus {
|
||||
active,
|
||||
inactive,
|
||||
}
|
||||
|
||||
typedef void SnackBarStatusChangedCallback(SnackBarStatus status);
|
||||
|
||||
const Duration _kSlideInDuration = const Duration(milliseconds: 200);
|
||||
|
||||
class SnackBarAction extends Component {
|
||||
SnackBarAction({Key key, this.label, this.onPressed }) : super(key: key) {
|
||||
assert(label != null);
|
||||
@ -29,18 +43,64 @@ class SnackBarAction extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
class SnackBar extends Component {
|
||||
class SnackBar extends AnimatedComponent {
|
||||
|
||||
SnackBar({
|
||||
Key key,
|
||||
this.content,
|
||||
this.actions
|
||||
this.actions,
|
||||
this.showing,
|
||||
this.onStatusChanged
|
||||
}) : super(key: key) {
|
||||
assert(content != null);
|
||||
}
|
||||
|
||||
final Widget content;
|
||||
final List<SnackBarAction> actions;
|
||||
Widget content;
|
||||
List<SnackBarAction> actions;
|
||||
bool showing;
|
||||
SnackBarStatusChangedCallback onStatusChanged;
|
||||
|
||||
void syncFields(SnackBar source) {
|
||||
content = source.content;
|
||||
actions = source.actions;
|
||||
onStatusChanged = source.onStatusChanged;
|
||||
if (showing != source.showing) {
|
||||
showing = source.showing;
|
||||
showing ? _show() : _hide();
|
||||
}
|
||||
}
|
||||
|
||||
AnimatedValue<Point> _position;
|
||||
AnimationPerformance _performance;
|
||||
|
||||
void initState() {
|
||||
_position = new AnimatedValue<Point>(new Point(0.0, 50.0), end: Point.origin);
|
||||
_performance = new AnimationPerformance()
|
||||
..duration = _kSlideInDuration
|
||||
..variable = _position
|
||||
..addListener(_checkStatusChanged);
|
||||
watch(_performance);
|
||||
if (showing)
|
||||
_show();
|
||||
}
|
||||
|
||||
void _show() {
|
||||
_performance.play();
|
||||
}
|
||||
|
||||
void _hide() {
|
||||
_performance.reverse();
|
||||
}
|
||||
|
||||
SnackBarStatus _lastStatus;
|
||||
void _checkStatusChanged() {
|
||||
SnackBarStatus status = _status;
|
||||
if (_lastStatus != null && status != _lastStatus && onStatusChanged != null)
|
||||
onStatusChanged(status);
|
||||
_lastStatus = status;
|
||||
}
|
||||
|
||||
SnackBarStatus get _status => _performance.isDismissed ? SnackBarStatus.inactive : SnackBarStatus.active;
|
||||
|
||||
Widget build() {
|
||||
List<Widget> children = [
|
||||
@ -53,18 +113,22 @@ class SnackBar extends Component {
|
||||
)
|
||||
)
|
||||
)
|
||||
];
|
||||
if (actions != null)
|
||||
children.addAll(actions);
|
||||
return new Material(
|
||||
level: 2,
|
||||
color: const Color(0xFF323232),
|
||||
type: MaterialType.canvas,
|
||||
child: new Container(
|
||||
margin: const EdgeDims.symmetric(horizontal: 24.0),
|
||||
child: new DefaultTextStyle(
|
||||
style: new TextStyle(color: Theme.of(this).accentColor),
|
||||
child: new Flex(children)
|
||||
]..addAll(actions);
|
||||
|
||||
Matrix4 transform = new Matrix4.identity();
|
||||
transform.translate(_position.value.x, _position.value.y);
|
||||
return new Transform(
|
||||
transform: transform,
|
||||
child: new Material(
|
||||
level: 2,
|
||||
color: const Color(0xFF323232),
|
||||
type: MaterialType.canvas,
|
||||
child: new Container(
|
||||
margin: const EdgeDims.symmetric(horizontal: 24.0),
|
||||
child: new DefaultTextStyle(
|
||||
style: new TextStyle(color: Theme.of(this).accentColor),
|
||||
child: new Flex(children)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user