mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Add new initState method to widget, rename animated_container to animation_builder and refactor it. This fixes the background color of the settings page.
R=abarth@chromium.org, abarth, mpcomplete Review URL: https://codereview.chromium.org/1233703003 .
This commit is contained in:
parent
688a084e86
commit
09a82d4935
@ -50,7 +50,7 @@ dart_pkg("sky") {
|
||||
"lib/theme/typography.dart",
|
||||
"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",
|
||||
|
||||
@ -24,17 +24,20 @@ import 'measurement.dart';
|
||||
|
||||
class HomeFragment extends StatefulComponent {
|
||||
|
||||
HomeFragment(this.navigator, this.userData) {
|
||||
// if (debug)
|
||||
// new Timer(new Duration(seconds: 1), dumpState);
|
||||
_drawerController = new DrawerController(_handleDrawerStatusChanged);
|
||||
}
|
||||
HomeFragment(this.navigator, this.userData);
|
||||
|
||||
Navigator navigator;
|
||||
List<Measurement> userData;
|
||||
|
||||
FitnessMode _fitnessMode = FitnessMode.measure;
|
||||
|
||||
void initState {
|
||||
// if (debug)
|
||||
// new Timer(new Duration(seconds: 1), dumpState);
|
||||
_drawerController = new DrawerController(_handleDrawerStatusChanged);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
void syncFields(HomeFragment source) {
|
||||
navigator = source.navigator;
|
||||
userData = source.userData;
|
||||
|
||||
@ -16,7 +16,9 @@ import 'fitness_types.dart';
|
||||
class FitnessApp extends App {
|
||||
|
||||
NavigationState _navigationState;
|
||||
FitnessApp() {
|
||||
FitnessApp();
|
||||
|
||||
void initState() {
|
||||
_navigationState = new NavigationState([
|
||||
new Route(
|
||||
name: '/',
|
||||
@ -27,6 +29,7 @@ class FitnessApp extends App {
|
||||
builder: (navigator, route) => new SettingsFragment(navigator, backupSetting, settingsUpdater)
|
||||
),
|
||||
]);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
void onBack() {
|
||||
|
||||
@ -19,7 +19,9 @@ import 'stock_types.dart';
|
||||
class StocksApp extends App {
|
||||
|
||||
NavigationState _navigationState;
|
||||
StocksApp() {
|
||||
StocksApp();
|
||||
|
||||
void initState() {
|
||||
_navigationState = new NavigationState([
|
||||
new Route(
|
||||
name: '/',
|
||||
@ -30,6 +32,7 @@ class StocksApp extends App {
|
||||
builder: (navigator, route) => new StockSettings(navigator, optimismSetting, backupSetting, settingsUpdater)
|
||||
),
|
||||
]);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
void onBack() {
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
import 'package:sky/editing/input.dart';
|
||||
import 'package:sky/animation/animation_performance.dart';
|
||||
import 'package:sky/widgets/animated_component.dart';
|
||||
import 'package:sky/widgets/animated_container.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';
|
||||
@ -58,7 +58,7 @@ class StockHome extends AnimatedComponent {
|
||||
bool _isSearching = false;
|
||||
String _searchQuery;
|
||||
|
||||
AnimatedContainer _snackbarTransform;
|
||||
AnimationBuilder _snackbarTransform;
|
||||
|
||||
void _handleSearchBegin() {
|
||||
navigator.pushState("/search", (_) {
|
||||
@ -282,7 +282,7 @@ class StockHome extends AnimatedComponent {
|
||||
|
||||
void _handleStockPurchased() {
|
||||
setState(() {
|
||||
_snackbarTransform = new AnimatedContainer()
|
||||
_snackbarTransform = new AnimationBuilder()
|
||||
..position = new AnimatedType<Point>(const Point(0.0, 45.0), end: Point.origin);
|
||||
var performance = _snackbarTransform.createPerformance(
|
||||
[_snackbarTransform.position], duration: _kSnackbarSlideDuration);
|
||||
|
||||
@ -78,7 +78,7 @@ class StockSettings extends StatefulComponent {
|
||||
Widget buildToolBar() {
|
||||
return new ToolBar(
|
||||
left: new IconButton(
|
||||
icon: 'navigation/arrow_back_white',
|
||||
icon: 'navigation/arrow_back',
|
||||
onPressed: navigator.pop),
|
||||
center: new Text('Settings')
|
||||
);
|
||||
|
||||
@ -10,7 +10,7 @@ import 'package:sky/animation/scroll_behavior.dart';
|
||||
import 'package:sky/base/lerp.dart';
|
||||
import 'package:sky/painting/text_style.dart';
|
||||
import 'package:sky/theme/colors.dart';
|
||||
import 'package:sky/widgets/animated_container.dart';
|
||||
import 'package:sky/widgets/animation_builder.dart';
|
||||
import 'package:sky/widgets/basic.dart';
|
||||
import 'package:sky/widgets/block_viewport.dart';
|
||||
import 'package:sky/widgets/card.dart';
|
||||
@ -78,7 +78,7 @@ class CardCollectionApp extends App {
|
||||
List<int> visibleCardIndices;
|
||||
|
||||
CardCollectionApp() {
|
||||
_activeCardTransform = new AnimatedContainer()
|
||||
_activeCardTransform = new AnimationBuilder()
|
||||
..position = new AnimatedType<Point>(Point.origin)
|
||||
..opacity = new AnimatedType<double>(1.0, end: 0.0);
|
||||
|
||||
@ -91,7 +91,7 @@ class CardCollectionApp extends App {
|
||||
}
|
||||
|
||||
int _activeCardIndex = -1;
|
||||
AnimatedContainer _activeCardTransform;
|
||||
AnimationBuilder _activeCardTransform;
|
||||
AnimationPerformance _activeCardAnimation;
|
||||
double _activeCardWidth;
|
||||
double _activeCardDragX = 0.0;
|
||||
|
||||
@ -20,17 +20,20 @@ class Input extends StatefulComponent {
|
||||
this.placeholder,
|
||||
this.onChanged,
|
||||
this.focused})
|
||||
: super(key: key) {
|
||||
_editableValue = new EditableString(
|
||||
text: _value,
|
||||
onUpdated: _handleTextUpdated
|
||||
);
|
||||
}
|
||||
: super(key: key);
|
||||
|
||||
String placeholder;
|
||||
ValueChanged onChanged;
|
||||
bool focused = false;
|
||||
|
||||
void initState() {
|
||||
_editableValue = new EditableString(
|
||||
text: _value,
|
||||
onUpdated: _handleTextUpdated
|
||||
);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
void syncFields(Input source) {
|
||||
placeholder = source.placeholder;
|
||||
onChanged = source.onChanged;
|
||||
|
||||
@ -335,7 +335,7 @@ Let's walk through the differences in `MyDialog` caused by its being stateful:
|
||||
components created by the parent component are retained in the widget
|
||||
hierchy. Old _stateful_ components, however, cannot simply be discarded
|
||||
because they contain state that needs to be preserved. Instead, the old
|
||||
stateful components are retained in the widget hiearchy and asked to
|
||||
stateful components are retained in the widget hierarchy and asked to
|
||||
`syncFields` with the new instance of the component created by the parent in
|
||||
its `build` function.
|
||||
|
||||
@ -386,6 +386,22 @@ subscriptions to event streams from outside the widget hierachy. When overriding
|
||||
either `didMount` or `didUnmount`, a component should call its superclass's
|
||||
`didMount` or `didUnmount` function.
|
||||
|
||||
initState
|
||||
---------
|
||||
|
||||
The framework calls the `initState` function on stateful components before
|
||||
building them. The default implementation of initState does nothing. If your
|
||||
component requires non-trivial work to initialize its state, you should
|
||||
override initState and do it there rather than doing it in the stateful
|
||||
component's constructor. If the component doesn't need to be built (for
|
||||
example, if it was constructed just to have its fields synchronized with
|
||||
an existing stateful component) you'll avoid unnecessary work. Also, some
|
||||
operations that involve interacting with the widget hierarchy cannot be
|
||||
done in a component's constructor.
|
||||
|
||||
When overriding `initState`, a component should call its superclass's
|
||||
`initState` function.
|
||||
|
||||
Keys
|
||||
----
|
||||
|
||||
|
||||
@ -11,23 +11,23 @@ import '../painting/box_painter.dart';
|
||||
import '../theme/shadows.dart';
|
||||
import 'basic.dart';
|
||||
|
||||
// This class builds a Container object from a collection of optionally-
|
||||
// animated properties. Use syncFields to update the Container's properties,
|
||||
// This class builds a Builder object from a collection of optionally-
|
||||
// animated properties. Use syncFields to update the Builder's properties,
|
||||
// which will optionally animate them using an AnimationPerformance.
|
||||
class AnimatedContainer {
|
||||
class AnimationBuilder {
|
||||
AnimatedType<double> opacity;
|
||||
AnimatedType<Point> position;
|
||||
AnimatedType<double> shadow;
|
||||
AnimatedColor backgroundColor;
|
||||
|
||||
// These don't animate, but are used to build the Container anyway.
|
||||
// These don't animate, but are used to build the AnimationBuilder anyway.
|
||||
double borderRadius;
|
||||
Shape shape;
|
||||
|
||||
Map<AnimatedVariable, AnimationPerformance> _variableToPerformance =
|
||||
new Map<AnimatedVariable, AnimationPerformance>();
|
||||
|
||||
AnimatedContainer();
|
||||
AnimationBuilder();
|
||||
|
||||
AnimationPerformance createPerformance(List<AnimatedType> variables,
|
||||
{Duration duration}) {
|
||||
@ -65,7 +65,7 @@ class AnimatedContainer {
|
||||
return current;
|
||||
}
|
||||
|
||||
void syncFields(AnimatedContainer source) {
|
||||
void syncFields(AnimationBuilder source) {
|
||||
_syncField(position, source.position);
|
||||
_syncField(shadow, source.shadow);
|
||||
_syncField(backgroundColor, source.backgroundColor);
|
||||
|
||||
122
sdk/lib/widgets/animation_builder.dart
Normal file
122
sdk/lib/widgets/animation_builder.dart
Normal file
@ -0,0 +1,122 @@
|
||||
// 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 '../animation/animation_performance.dart';
|
||||
import '../animation/curves.dart';
|
||||
import '../base/lerp.dart';
|
||||
import '../painting/box_painter.dart';
|
||||
import '../theme/shadows.dart';
|
||||
import '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 {
|
||||
AnimatedType<double> opacity;
|
||||
AnimatedType<Point> position;
|
||||
AnimatedType<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>();
|
||||
|
||||
AnimationBuilder();
|
||||
|
||||
AnimationPerformance createPerformance(List<AnimatedType> 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({ AnimatedType<double> shadow,
|
||||
AnimatedColor backgroundColor,
|
||||
double borderRadius,
|
||||
Shape shape }) {
|
||||
_updateField(this.shadow, shadow);
|
||||
_updateField(this.backgroundColor, backgroundColor);
|
||||
this.borderRadius = borderRadius;
|
||||
this.shape = shape;
|
||||
}
|
||||
|
||||
void _updateField(AnimatedType variable, AnimatedType 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AnimatedColor extends AnimatedType<Color> {
|
||||
AnimatedColor(Color begin, {Color end, Curve curve: linear})
|
||||
: super(begin, end: end, curve: curve);
|
||||
|
||||
void setFraction(double t) {
|
||||
value = lerpColor(begin, end, t);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@ -8,7 +8,7 @@ import '../animation/animation_performance.dart';
|
||||
import '../animation/curves.dart';
|
||||
import '../theme/shadows.dart';
|
||||
import 'animated_component.dart';
|
||||
import 'animated_container.dart';
|
||||
import 'animation_builder.dart';
|
||||
import 'basic.dart';
|
||||
import 'theme.dart';
|
||||
|
||||
@ -37,19 +37,19 @@ typedef void DrawerStatusChangeHandler (bool showing);
|
||||
|
||||
class DrawerController {
|
||||
DrawerController(this.onStatusChange) {
|
||||
container = new AnimatedContainer()
|
||||
builder = new AnimationBuilder()
|
||||
..position = new AnimatedType<Point>(
|
||||
new Point(-_kWidth, 0.0), end: Point.origin, curve: _kAnimationCurve);
|
||||
performance = container.createPerformance([container.position],
|
||||
performance = builder.createPerformance([builder.position],
|
||||
duration: _kBaseSettleDuration)
|
||||
..addListener(_checkValue);
|
||||
}
|
||||
final DrawerStatusChangeHandler onStatusChange;
|
||||
|
||||
AnimationPerformance performance;
|
||||
AnimatedContainer container;
|
||||
AnimationBuilder builder;
|
||||
|
||||
double get xPosition => container.position.value.x;
|
||||
double get xPosition => builder.position.value.x;
|
||||
|
||||
bool _oldClosedState = true;
|
||||
void _checkValue() {
|
||||
@ -132,7 +132,7 @@ class Drawer extends AnimatedComponent {
|
||||
onGestureTap: controller.handleMaskTap
|
||||
);
|
||||
|
||||
Widget content = controller.container.build(
|
||||
Widget content = controller.builder.build(
|
||||
new Container(
|
||||
decoration: new BoxDecoration(
|
||||
backgroundColor: Theme.of(this).canvasColor,
|
||||
|
||||
@ -29,5 +29,5 @@ class FlatButton extends MaterialButton {
|
||||
}
|
||||
}
|
||||
|
||||
int get level => null;
|
||||
int get level => 0;
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
import '../animation/animation_performance.dart';
|
||||
import '../painting/box_painter.dart';
|
||||
import 'animated_component.dart';
|
||||
import 'animated_container.dart';
|
||||
import 'animation_builder.dart';
|
||||
import 'basic.dart';
|
||||
import 'default_text_style.dart';
|
||||
import 'theme.dart';
|
||||
@ -27,29 +27,44 @@ class Material extends AnimatedComponent {
|
||||
Material({
|
||||
String key,
|
||||
this.child,
|
||||
MaterialType type: MaterialType.card,
|
||||
int level: 0,
|
||||
Color color: null
|
||||
this.type: MaterialType.card,
|
||||
this.level: 0,
|
||||
this.color
|
||||
}) : super(key: key) {
|
||||
if (level == null) level = 0;
|
||||
_container = new AnimatedContainer()
|
||||
assert(level != null);
|
||||
}
|
||||
|
||||
Widget child;
|
||||
MaterialType type;
|
||||
int level;
|
||||
Color color;
|
||||
|
||||
AnimationBuilder _builder;
|
||||
|
||||
void initState() {
|
||||
_builder = new AnimationBuilder()
|
||||
..shadow = new AnimatedType<double>(level.toDouble())
|
||||
..backgroundColor = _getBackgroundColor(type, color)
|
||||
..borderRadius = edges[type]
|
||||
..shape = type == MaterialType.circle ? Shape.circle : Shape.rectangle;
|
||||
watchPerformance(_container.createPerformance(
|
||||
[_container.shadow], duration: _kAnimateShadowDuration));
|
||||
watchPerformance(_container.createPerformance(
|
||||
[_container.backgroundColor], duration: _kAnimateColorDuration));
|
||||
watchPerformance(_builder.createPerformance(
|
||||
[_builder.shadow], duration: _kAnimateShadowDuration));
|
||||
watchPerformance(_builder.createPerformance(
|
||||
[_builder.backgroundColor], duration: _kAnimateColorDuration));
|
||||
super.initState();
|
||||
}
|
||||
|
||||
Widget child;
|
||||
|
||||
AnimatedContainer _container;
|
||||
|
||||
void syncFields(Material source) {
|
||||
child = source.child;
|
||||
_container.syncFields(source._container);
|
||||
type = source.type;
|
||||
level = source.level;
|
||||
color = source.color;
|
||||
_builder.updateFields(
|
||||
shadow: new AnimatedType<double>(level.toDouble()),
|
||||
backgroundColor: _getBackgroundColor(type, color),
|
||||
borderRadius: edges[type],
|
||||
shape: type == MaterialType.circle ? Shape.circle : Shape.rectangle
|
||||
);
|
||||
super.syncFields(source);
|
||||
}
|
||||
|
||||
@ -70,7 +85,7 @@ class Material extends AnimatedComponent {
|
||||
}
|
||||
|
||||
Widget build() {
|
||||
return _container.build(
|
||||
return _builder.build(
|
||||
new DefaultTextStyle(style: Theme.of(this).text.body1, child: child)
|
||||
);
|
||||
}
|
||||
|
||||
@ -135,7 +135,6 @@ abstract class Scrollable extends StatefulComponent {
|
||||
|
||||
void _startSimulation({ double velocity: 0.0 }) {
|
||||
_stopSimulation();
|
||||
print("velocity=$velocity");
|
||||
Simulation simulation = scrollBehavior.release(scrollOffset, velocity);
|
||||
if (simulation != null)
|
||||
_animation.start(simulation);
|
||||
|
||||
@ -521,6 +521,7 @@ abstract class StatefulComponent extends Component {
|
||||
StatefulComponent({ String key }) : super(key: key);
|
||||
|
||||
bool _disqualifiedFromEverAppearingAgain = false;
|
||||
bool _isStateInitialized = false;
|
||||
|
||||
void didMount() {
|
||||
assert(!_disqualifiedFromEverAppearingAgain);
|
||||
@ -534,6 +535,11 @@ abstract class StatefulComponent extends Component {
|
||||
|
||||
void _sync(Widget old, dynamic slot) {
|
||||
assert(!_disqualifiedFromEverAppearingAgain);
|
||||
// TODO(ianh): _sync should only be called once when old == null
|
||||
if (old == null && !_isStateInitialized) {
|
||||
initState();
|
||||
_isStateInitialized = true;
|
||||
}
|
||||
super._sync(old, slot);
|
||||
}
|
||||
|
||||
@ -567,6 +573,11 @@ abstract class StatefulComponent extends Component {
|
||||
// extending StatefulComponent directly.
|
||||
void syncFields(Component source);
|
||||
|
||||
// Stateful components can override initState if they want
|
||||
// to do non-trivial work to initialize state. This is
|
||||
// always called before build().
|
||||
void initState() { }
|
||||
|
||||
void setState(Function fn()) {
|
||||
assert(!_disqualifiedFromEverAppearingAgain);
|
||||
fn();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user