Merge pull request #1777 from vlidholt/master
Fitness demo, initial version
This commit is contained in:
commit
c30b3cc69b
@ -20,7 +20,10 @@ assets:
|
||||
- packages/flutter_gallery_assets/icon-snow.png
|
||||
- packages/flutter_gallery_assets/kangaroo_valley_safari.png
|
||||
- packages/flutter_gallery_assets/top_10_australian_beaches.png
|
||||
- packages/flutter_gallery_assets/jumpingjack.json
|
||||
- packages/flutter_gallery_assets/jumpingjack.png
|
||||
material-design-icons:
|
||||
- name: action/accessibility
|
||||
- name: action/account_circle
|
||||
- name: action/alarm
|
||||
- name: action/android
|
||||
@ -29,6 +32,8 @@ material-design-icons:
|
||||
- name: action/home
|
||||
- name: action/hourglass_empty
|
||||
- name: action/language
|
||||
- name: av/play_arrow
|
||||
- name: av/stop
|
||||
- name: communication/call
|
||||
- name: communication/email
|
||||
- name: communication/location_on
|
||||
@ -39,6 +44,8 @@ material-design-icons:
|
||||
- name: content/create
|
||||
- name: image/brightness_5
|
||||
- name: image/brightness_7
|
||||
- name: image/flash_on
|
||||
- name: image/timer
|
||||
- name: navigation/arrow_back
|
||||
- name: navigation/arrow_drop_down
|
||||
- name: navigation/arrow_forward
|
||||
|
491
examples/material_gallery/lib/demo/fitness_demo.dart
Normal file
491
examples/material_gallery/lib/demo/fitness_demo.dart
Normal file
@ -0,0 +1,491 @@
|
||||
// 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 'dart:async';
|
||||
import 'dart:math' as math;
|
||||
import 'dart:ui' as ui;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_sprites/flutter_sprites.dart';
|
||||
|
||||
ImageMap _images;
|
||||
SpriteSheet _sprites;
|
||||
|
||||
class FitnessDemo extends StatelessComponent {
|
||||
FitnessDemo({ Key key }) : super(key: key);
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
return new Scaffold(
|
||||
toolBar: new ToolBar(
|
||||
center: new Text("Fitness")
|
||||
),
|
||||
body: new _FitnessDemoContents()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _FitnessDemoContents extends StatefulComponent {
|
||||
_FitnessDemoContents({ Key key }) : super(key: key);
|
||||
_FitnessDemoContentsState createState() => new _FitnessDemoContentsState();
|
||||
}
|
||||
|
||||
class _FitnessDemoContentsState extends State<_FitnessDemoContents> {
|
||||
|
||||
Future _loadAssets(AssetBundle bundle) async {
|
||||
_images = new ImageMap(bundle);
|
||||
await _images.load(<String>[
|
||||
'packages/flutter_gallery_assets/jumpingjack.png',
|
||||
]);
|
||||
|
||||
String json = await DefaultAssetBundle.of(context).loadString('packages/flutter_gallery_assets/jumpingjack.json');
|
||||
_sprites = new SpriteSheet(_images['packages/flutter_gallery_assets/jumpingjack.png'], json);
|
||||
}
|
||||
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
AssetBundle bundle = DefaultAssetBundle.of(context);
|
||||
_loadAssets(bundle).then((_) {
|
||||
setState(() {
|
||||
assetsLoaded = true;
|
||||
workoutAnimation = new _WorkoutAnimationNode(
|
||||
onPerformedJumpingJack: () {
|
||||
setState(() {
|
||||
count += 1;
|
||||
});
|
||||
},
|
||||
onSecondPassed: (int seconds) {
|
||||
setState(() {
|
||||
time = seconds;
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
bool assetsLoaded = false;
|
||||
int count = 0;
|
||||
int time = 0;
|
||||
int get kcal => (count * 0.2).toInt();
|
||||
|
||||
_WorkoutAnimationNode workoutAnimation;
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
if (!assetsLoaded)
|
||||
return new Container();
|
||||
|
||||
Color buttonColor;
|
||||
String buttonText;
|
||||
VoidCallback onButtonPressed;
|
||||
|
||||
if (workoutAnimation.workingOut) {
|
||||
buttonColor = Colors.red[500];
|
||||
buttonText = "STOP WORKOUT";
|
||||
onButtonPressed = endWorkout;
|
||||
} else {
|
||||
buttonColor = Theme.of(context).primaryColor;
|
||||
buttonText = "START WORKOUT";
|
||||
onButtonPressed = startWorkout;
|
||||
}
|
||||
|
||||
return new Material(
|
||||
child: new Column(
|
||||
justifyContent: FlexJustifyContent.center,
|
||||
children: <Widget>[
|
||||
new Flexible(
|
||||
child: new Container(
|
||||
decoration: new BoxDecoration(backgroundColor: Colors.grey[800]),
|
||||
child: new SpriteWidget(workoutAnimation, SpriteBoxTransformMode.scaleToFit)
|
||||
)
|
||||
),
|
||||
new Padding(
|
||||
padding: new EdgeDims.only(top: 20.0),
|
||||
child: new Text("JUMPING JACKS", style: Theme.of(context).text.title)
|
||||
),
|
||||
new Padding(
|
||||
padding: new EdgeDims.only(top: 20.0, bottom: 20.0),
|
||||
child: new Row(
|
||||
justifyContent: FlexJustifyContent.center,
|
||||
children: <Widget>[
|
||||
_createInfoPanelCell("action/accessibility", "$count", "COUNT"),
|
||||
_createInfoPanelCell("image/timer", _formatSeconds(time), "TIME"),
|
||||
_createInfoPanelCell("image/flash_on", "$kcal", "KCAL")
|
||||
]
|
||||
)
|
||||
),
|
||||
new Padding(
|
||||
padding: new EdgeDims.only(bottom: 16.0),
|
||||
child: new SizedBox(
|
||||
width: 300.0,
|
||||
height: 72.0,
|
||||
child: new RaisedButton (
|
||||
onPressed: onButtonPressed,
|
||||
color: buttonColor,
|
||||
child: new Text(
|
||||
buttonText,
|
||||
style: new TextStyle(color: Colors.white, fontSize: 20.0)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
Widget _createInfoPanelCell(String icon, String value, String description) {
|
||||
Color color;
|
||||
if (workoutAnimation.workingOut)
|
||||
color = Colors.black87;
|
||||
else
|
||||
color = Theme.of(context).disabledColor;
|
||||
|
||||
return new Container(
|
||||
width: 100.0,
|
||||
child: new Center(
|
||||
child: new Column(
|
||||
children: <Widget>[
|
||||
new Icon(icon: icon, size: IconSize.s48, color: color),
|
||||
new Text(value, style: new TextStyle(fontSize: 24.0, color: color)),
|
||||
new Text(description, style: new TextStyle(color: color))
|
||||
]
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
String _formatSeconds(int seconds) {
|
||||
int minutes = seconds ~/ 60;
|
||||
String secondsStr = "${seconds % 60}".padLeft(2, "0");
|
||||
return "$minutes:$secondsStr";
|
||||
}
|
||||
|
||||
void startWorkout() {
|
||||
setState(() {
|
||||
count = 0;
|
||||
time = 0;
|
||||
workoutAnimation.start();
|
||||
});
|
||||
}
|
||||
|
||||
void endWorkout() {
|
||||
setState(() {
|
||||
workoutAnimation.stop();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
typedef void _SecondPassedCallback(int seconds);
|
||||
|
||||
class _WorkoutAnimationNode extends NodeWithSize {
|
||||
_WorkoutAnimationNode({
|
||||
this.onPerformedJumpingJack,
|
||||
this.onSecondPassed
|
||||
}) : super(const Size(1024.0, 1024.0)) {
|
||||
reset();
|
||||
|
||||
_progress = new _ProgressCircle(const Size(800.0, 800.0));
|
||||
_progress.pivot = const Point(0.5, 0.5);
|
||||
_progress.position = const Point(512.0, 512.0);
|
||||
addChild(_progress);
|
||||
|
||||
_jumpingJack = new _JumpingJack((){
|
||||
onPerformedJumpingJack();
|
||||
});
|
||||
_jumpingJack.scale = 0.5;
|
||||
_jumpingJack.position = const Point(512.0, 550.0);
|
||||
addChild(_jumpingJack);
|
||||
}
|
||||
|
||||
final VoidCallback onPerformedJumpingJack;
|
||||
final _SecondPassedCallback onSecondPassed;
|
||||
|
||||
int seconds;
|
||||
|
||||
bool workingOut;
|
||||
|
||||
static const int _kTargetMillis = 1000 * 30;
|
||||
int _startTimeMillis;
|
||||
_ProgressCircle _progress;
|
||||
_JumpingJack _jumpingJack;
|
||||
|
||||
void reset() {
|
||||
seconds = 0;
|
||||
workingOut = false;
|
||||
}
|
||||
|
||||
void start() {
|
||||
reset();
|
||||
_startTimeMillis = new DateTime.now().millisecondsSinceEpoch;
|
||||
workingOut = true;
|
||||
_jumpingJack.animateJumping();
|
||||
}
|
||||
|
||||
void stop() {
|
||||
workingOut = false;
|
||||
_jumpingJack.neutralPose();
|
||||
}
|
||||
|
||||
void update(double dt) {
|
||||
if (workingOut) {
|
||||
int millis = new DateTime.now().millisecondsSinceEpoch - _startTimeMillis;
|
||||
int newSeconds = (millis) ~/ 1000;
|
||||
if (newSeconds != seconds) {
|
||||
seconds = newSeconds;
|
||||
onSecondPassed(seconds);
|
||||
}
|
||||
|
||||
_progress.value = millis / _kTargetMillis;
|
||||
} else {
|
||||
_progress.value = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _ProgressCircle extends NodeWithSize {
|
||||
_ProgressCircle(Size size, [this.value = 0.0]) : super(size);
|
||||
|
||||
static const _kTwoPI = math.PI * 2.0;
|
||||
static const _kEpsilon = .0000001;
|
||||
static const _kSweep = _kTwoPI - _kEpsilon;
|
||||
|
||||
double value;
|
||||
|
||||
void paint(Canvas canvas) {
|
||||
applyTransformForPivot(canvas);
|
||||
|
||||
Paint circlePaint = new Paint()
|
||||
..color = Colors.white30
|
||||
..strokeWidth = 24.0
|
||||
..style = ui.PaintingStyle.stroke;
|
||||
|
||||
canvas.drawCircle(
|
||||
new Point(size.width / 2.0, size.height / 2.0),
|
||||
size.width / 2.0,
|
||||
circlePaint
|
||||
);
|
||||
|
||||
Paint pathPaint = new Paint()
|
||||
..color = Colors.purple[500]
|
||||
..strokeWidth = 25.0
|
||||
..style = ui.PaintingStyle.stroke;
|
||||
|
||||
double angle = value.clamp(0.0, 1.0) * _kSweep;
|
||||
Path path = new Path()
|
||||
..arcTo(Point.origin & size, -math.PI / 2.0, angle, false);
|
||||
canvas.drawPath(path, pathPaint);
|
||||
}
|
||||
}
|
||||
|
||||
class _JumpingJack extends Node {
|
||||
_JumpingJack(VoidCallback onPerformedJumpingJack) {
|
||||
left = new _JumpingJackSide(false, onPerformedJumpingJack);
|
||||
right = new _JumpingJackSide(true, null);
|
||||
addChild(left);
|
||||
addChild(right);
|
||||
}
|
||||
|
||||
void animateJumping() {
|
||||
left.animateJumping();
|
||||
right.animateJumping();
|
||||
}
|
||||
|
||||
void neutralPose() {
|
||||
left.neutralPosition(true);
|
||||
right.neutralPosition(true);
|
||||
}
|
||||
|
||||
_JumpingJackSide left;
|
||||
_JumpingJackSide right;
|
||||
}
|
||||
|
||||
class _JumpingJackSide extends Node {
|
||||
_JumpingJackSide(bool right, this.onPerformedJumpingJack) {
|
||||
// Torso and head
|
||||
torso = _createPart('torso.png', const Point(512.0, 512.0));
|
||||
addChild(torso);
|
||||
|
||||
head = _createPart('head.png', const Point(512.0, 160.0));
|
||||
torso.addChild(head);
|
||||
|
||||
if (right) {
|
||||
torso.opacity = 0.0;
|
||||
head.opacity = 0.0;
|
||||
torso.scaleX = -1.0;
|
||||
}
|
||||
|
||||
// Left side movable parts
|
||||
upperArm = _createPart('upper-arm.png', const Point(445.0, 220.0));
|
||||
torso.addChild(upperArm);
|
||||
lowerArm = _createPart('lower-arm.png', const Point(306.0, 200.0));
|
||||
upperArm.addChild(lowerArm);
|
||||
hand = _createPart('hand.png', const Point(215.0, 127.0));
|
||||
lowerArm.addChild(hand);
|
||||
upperLeg = _createPart('upper-leg.png', const Point(467.0, 492.0));
|
||||
torso.addChild(upperLeg);
|
||||
lowerLeg = _createPart('lower-leg.png', const Point(404.0, 660.0));
|
||||
upperLeg.addChild(lowerLeg);
|
||||
foot = _createPart('foot.png', const Point(380.0, 835.0));
|
||||
lowerLeg.addChild(foot);
|
||||
|
||||
torso.setPivotAndPosition(Point.origin);
|
||||
|
||||
neutralPosition(false);
|
||||
}
|
||||
|
||||
_JumpingJackPart torso;
|
||||
_JumpingJackPart head;
|
||||
_JumpingJackPart upperArm;
|
||||
_JumpingJackPart lowerArm;
|
||||
_JumpingJackPart hand;
|
||||
_JumpingJackPart lowerLeg;
|
||||
_JumpingJackPart upperLeg;
|
||||
_JumpingJackPart foot;
|
||||
|
||||
final VoidCallback onPerformedJumpingJack;
|
||||
|
||||
_JumpingJackPart _createPart(String textureName, Point pivotPosition) {
|
||||
return new _JumpingJackPart(_sprites[textureName], pivotPosition);
|
||||
}
|
||||
|
||||
void animateJumping() {
|
||||
actions.stopAll();
|
||||
actions.run(new ActionSequence([
|
||||
_createPoseAction(null, 0, 0.5),
|
||||
new ActionCallFunction(_animateJumpingLoop)
|
||||
]));
|
||||
}
|
||||
|
||||
void _animateJumpingLoop() {
|
||||
actions.run(new ActionRepeatForever(
|
||||
new ActionSequence(<Action>[
|
||||
_createPoseAction(0, 1, 0.30),
|
||||
_createPoseAction(1, 2, 0.30),
|
||||
_createPoseAction(2, 1, 0.30),
|
||||
_createPoseAction(1, 0, 0.30),
|
||||
new ActionCallFunction(() {
|
||||
if (onPerformedJumpingJack != null)
|
||||
onPerformedJumpingJack();
|
||||
})
|
||||
])
|
||||
));
|
||||
}
|
||||
|
||||
void neutralPosition(bool animate) {
|
||||
actions.stopAll();
|
||||
if (animate) {
|
||||
actions.run(_createPoseAction(null, 1, 0.5));
|
||||
} else {
|
||||
List<double> d = _dataForPose(1);
|
||||
upperArm.rotation = d[0];
|
||||
lowerArm.rotation = d[1];
|
||||
hand.rotation = d[2];
|
||||
upperLeg.rotation = d[3];
|
||||
lowerLeg.rotation = d[4];
|
||||
foot.rotation = d[5];
|
||||
torso.position = new Point(0.0, d[6]);
|
||||
}
|
||||
}
|
||||
|
||||
ActionInterval _createPoseAction(int startPose, int endPose, double duration) {
|
||||
List<double> d0 = _dataForPose(startPose);
|
||||
List<double> d1 = _dataForPose(endPose);
|
||||
|
||||
List<ActionTween> tweens = <ActionTween>[
|
||||
_tweenRotation(upperArm, d0[0], d1[0], duration),
|
||||
_tweenRotation(lowerArm, d0[1], d1[1], duration),
|
||||
_tweenRotation(hand, d0[2], d1[2], duration),
|
||||
_tweenRotation(upperLeg, d0[3], d1[3], duration),
|
||||
_tweenRotation(lowerLeg, d0[4], d1[4], duration),
|
||||
_tweenRotation(foot, d0[5], d1[5], duration),
|
||||
new ActionTween(
|
||||
(Point a) => torso.position = a,
|
||||
new Point(0.0, d0[6]),
|
||||
new Point(0.0, d1[6]),
|
||||
duration
|
||||
)
|
||||
];
|
||||
|
||||
return new ActionGroup(tweens);
|
||||
}
|
||||
|
||||
ActionTween _tweenRotation(_JumpingJackPart part, double r0, double r1, double duration) {
|
||||
return new ActionTween(
|
||||
(double a) => part.rotation = a,
|
||||
r0,
|
||||
r1,
|
||||
duration
|
||||
);
|
||||
}
|
||||
|
||||
List<double> _dataForPose(int pose) {
|
||||
if (pose == null)
|
||||
return _dataForCurrentPose();
|
||||
|
||||
if (pose == 0) {
|
||||
return <double>[
|
||||
-80.0, // Upper arm rotation
|
||||
-30.0, // Lower arm rotation
|
||||
-10.0, // Hand rotation
|
||||
-15.0, // Upper leg rotation
|
||||
5.0, // Lower leg rotation
|
||||
15.0, // Foot rotation
|
||||
0.0 // Torso y offset
|
||||
];
|
||||
} else if (pose == 1) {
|
||||
return <double>[
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
-70.0
|
||||
];
|
||||
} else {
|
||||
return <double>[
|
||||
40.0,
|
||||
30.0,
|
||||
10.0,
|
||||
20.0,
|
||||
-20.0,
|
||||
15.0,
|
||||
40.0
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
List<double> _dataForCurrentPose() {
|
||||
return <double>[
|
||||
upperArm.rotation,
|
||||
lowerArm.rotation,
|
||||
hand.rotation,
|
||||
upperLeg.rotation,
|
||||
lowerLeg.rotation,
|
||||
foot.rotation,
|
||||
torso.position.y
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
class _JumpingJackPart extends Sprite {
|
||||
_JumpingJackPart(Texture texture, this.pivotPosition) : super(texture);
|
||||
final Point pivotPosition;
|
||||
|
||||
void setPivotAndPosition(Point newPosition) {
|
||||
pivot = new Point(pivotPosition.x / 1024.0, pivotPosition.y / 1024.0);
|
||||
position = newPosition;
|
||||
|
||||
for (Node child in children) {
|
||||
_JumpingJackPart subPart = child;
|
||||
subPart.setPivotAndPosition(
|
||||
new Point(
|
||||
subPart.pivotPosition.x - pivotPosition.x,
|
||||
subPart.pivotPosition.y - pivotPosition.y
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -30,6 +30,7 @@ import '../demo/time_picker_demo.dart';
|
||||
import '../demo/two_level_list_demo.dart';
|
||||
import '../demo/typography_demo.dart';
|
||||
import '../demo/weathers_demo.dart';
|
||||
import '../demo/fitness_demo.dart';
|
||||
|
||||
class GalleryHome extends StatefulComponent {
|
||||
GalleryHome({ Key key }) : super(key: key);
|
||||
@ -65,7 +66,8 @@ class GalleryHomeState extends State<GalleryHome> {
|
||||
image: 'assets/section_animation.png',
|
||||
colors: Colors.purple,
|
||||
demos: <GalleryDemo>[
|
||||
new GalleryDemo(title: 'Weathers', builder: () => new WeathersDemo())
|
||||
new GalleryDemo(title: 'Weathers', builder: () => new WeathersDemo()),
|
||||
new GalleryDemo(title: 'Fitness', builder: () => new FitnessDemo())
|
||||
]
|
||||
),
|
||||
new GallerySection(
|
||||
|
Loading…
x
Reference in New Issue
Block a user