| // 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:ui' as ui show Image; |
| import 'package:flutter/material.dart'; |
| import 'package:flutter/services.dart'; |
| import 'package:flutter_sprites/flutter_sprites.dart'; |
| |
| ImageMap _images; |
| SpriteSheet _sprites; |
| |
| enum WeatherType { |
| sun, |
| rain, |
| snow |
| } |
| |
| class WeatherDemo extends StatefulComponent { |
| WeatherDemo({ Key key }) : super(key: key); |
| _WeatherDemoState createState() => new _WeatherDemoState(); |
| } |
| |
| class _WeatherDemoState extends State<WeatherDemo> { |
| |
| Future<Null> _loadAssets(AssetBundle bundle) async { |
| _images = new ImageMap(bundle); |
| await _images.load(<String>[ |
| 'packages/flutter_gallery_assets/clouds-0.png', |
| 'packages/flutter_gallery_assets/clouds-1.png', |
| 'packages/flutter_gallery_assets/ray.png', |
| 'packages/flutter_gallery_assets/sun.png', |
| 'packages/flutter_gallery_assets/weathersprites.json', |
| 'packages/flutter_gallery_assets/weathersprites.png', |
| 'packages/flutter_gallery_assets/icon-sun.png', |
| 'packages/flutter_gallery_assets/icon-rain.png', |
| 'packages/flutter_gallery_assets/icon-snow.png' |
| ]); |
| |
| String json = await DefaultAssetBundle.of(context).loadString('packages/flutter_gallery_assets/weathersprites.json'); |
| _sprites = new SpriteSheet(_images['packages/flutter_gallery_assets/weathersprites.png'], json); |
| } |
| |
| void initState() { |
| super.initState(); |
| |
| AssetBundle bundle = DefaultAssetBundle.of(context); |
| _loadAssets(bundle).then((_) { |
| setState(() { |
| assetsLoaded = true; |
| weatherWorld = new WeatherWorld(); |
| }); |
| }); |
| } |
| |
| bool assetsLoaded = false; |
| |
| WeatherWorld weatherWorld; |
| |
| Widget build(BuildContext context) { |
| if (!assetsLoaded) { |
| return new Scaffold( |
| toolBar: new ToolBar( |
| center: new Text("Weather") |
| ), |
| body: new Container( |
| decoration: new BoxDecoration( |
| backgroundColor: const Color(0xff4aaafb) |
| ) |
| ) |
| ); |
| } |
| |
| return new Scaffold( |
| toolBar: new ToolBar( |
| center: new Text("Weather") |
| ), |
| body: new Material( |
| child: new Stack( |
| children: <Widget>[ |
| new SpriteWidget(weatherWorld), |
| new Align( |
| alignment: new FractionalOffset(0.5, 0.8), |
| child: new Row( |
| justifyContent: FlexJustifyContent.center, |
| children: <Widget>[ |
| new WeatherButton( |
| onPressed: () { |
| setState(() { |
| weatherWorld.weatherType = WeatherType.sun; |
| }); |
| }, |
| selected: weatherWorld.weatherType == WeatherType.sun, |
| icon: "packages/flutter_gallery_assets/icon-sun.png" |
| ), |
| new WeatherButton( |
| onPressed: () { |
| setState(() { |
| weatherWorld.weatherType = WeatherType.rain; |
| }); |
| }, |
| selected: weatherWorld.weatherType == WeatherType.rain, |
| icon: "packages/flutter_gallery_assets/icon-rain.png" |
| ), |
| new WeatherButton( |
| onPressed: () { |
| setState(() { |
| weatherWorld.weatherType = WeatherType.snow; |
| }); |
| }, |
| selected: weatherWorld.weatherType == WeatherType.snow, |
| icon: "packages/flutter_gallery_assets/icon-snow.png" |
| ) |
| ] |
| ) |
| ) |
| ] |
| ) |
| ) |
| ); |
| } |
| } |
| |
| const double _kWeatherButtonSize = 56.0; |
| const double _kWeatherIconSize = 36.0; |
| |
| class WeatherButton extends StatelessComponent { |
| WeatherButton({ this.icon, this.selected, this.onPressed, Key key }) : super(key: key); |
| |
| final String icon; |
| final bool selected; |
| final VoidCallback onPressed; |
| |
| Widget build(BuildContext context) { |
| Color color; |
| if (selected) |
| color = Theme.of(context).primaryColor; |
| else |
| color = const Color(0x33000000); |
| |
| return new Padding( |
| padding: const EdgeDims.all(15.0), |
| child: new Material( |
| color: color, |
| type: MaterialType.circle, |
| elevation: 0, |
| child: new Container( |
| width: _kWeatherButtonSize, |
| height: _kWeatherButtonSize, |
| child: new InkWell( |
| onTap: onPressed, |
| child: new Center( |
| child: new AssetImage( |
| name: icon, |
| width: _kWeatherIconSize, |
| height: _kWeatherIconSize |
| ) |
| ) |
| ) |
| ) |
| ) |
| ); |
| } |
| } |
| |
| const List<Color> _kBackgroundColorsTop = const <Color>[ |
| const Color(0xff5ebbd5), |
| const Color(0xff0b2734), |
| const Color(0xffcbced7) |
| ]; |
| |
| const List<Color> _kBackgroundColorsBottom = const <Color>[ |
| const Color(0xff4aaafb), |
| const Color(0xff4c5471), |
| const Color(0xffe0e3ec) |
| ]; |
| |
| class WeatherWorld extends NodeWithSize { |
| WeatherWorld() : super(const Size(2048.0, 2048.0)) { |
| _background = new GradientNode( |
| this.size, |
| _kBackgroundColorsTop[0], |
| _kBackgroundColorsBottom[0] |
| ); |
| addChild(_background); |
| |
| _cloudsSharp = new CloudLayer( |
| image: _images['packages/flutter_gallery_assets/clouds-0.png'], |
| rotated: false, |
| dark: false, |
| loopTime: 20.0 |
| ); |
| addChild(_cloudsSharp); |
| |
| _cloudsDark = new CloudLayer( |
| image: _images['packages/flutter_gallery_assets/clouds-1.png'], |
| rotated: true, |
| dark: true, |
| loopTime: 40.0 |
| ); |
| addChild(_cloudsDark); |
| |
| _cloudsSoft = new CloudLayer( |
| image: _images['packages/flutter_gallery_assets/clouds-1.png'], |
| rotated: false, |
| dark: false, |
| loopTime: 60.0 |
| ); |
| addChild(_cloudsSoft); |
| |
| _sun = new Sun(); |
| _sun.position = const Point(1024.0, 1024.0); |
| _sun.scale = 1.5; |
| addChild(_sun); |
| |
| _rain = new Rain(); |
| addChild(_rain); |
| |
| _snow = new Snow(); |
| addChild(_snow); |
| } |
| |
| GradientNode _background; |
| CloudLayer _cloudsSharp; |
| CloudLayer _cloudsSoft; |
| CloudLayer _cloudsDark; |
| Sun _sun; |
| Rain _rain; |
| Snow _snow; |
| |
| WeatherType get weatherType => _weatherType; |
| |
| WeatherType _weatherType = WeatherType.sun; |
| |
| void set weatherType(WeatherType weatherType) { |
| if (weatherType == _weatherType) |
| return; |
| |
| _weatherType = weatherType; |
| |
| // Fade the background |
| _background.actions.stopAll(); |
| |
| _background.actions.run(new ActionTween( |
| (Color a) => _background.colorTop = a, |
| _background.colorTop, |
| _kBackgroundColorsTop[weatherType.index], |
| 1.0 |
| )); |
| |
| _background.actions.run(new ActionTween( |
| (Color a) => _background.colorBottom = a, |
| _background.colorBottom, |
| _kBackgroundColorsBottom[weatherType.index], |
| 1.0 |
| )); |
| |
| _cloudsDark.active = weatherType != WeatherType.sun; |
| _sun.active = weatherType == WeatherType.sun; |
| _rain.active = weatherType == WeatherType.rain; |
| _snow.active = weatherType == WeatherType.snow; |
| } |
| |
| void spriteBoxPerformedLayout() { |
| _sun.position = spriteBox.visibleArea.topLeft + const Offset(350.0, 180.0); |
| } |
| } |
| |
| class GradientNode extends NodeWithSize { |
| GradientNode(Size size, this.colorTop, this.colorBottom) : super(size); |
| |
| Color colorTop; |
| Color colorBottom; |
| |
| void paint(Canvas canvas) { |
| applyTransformForPivot(canvas); |
| |
| Rect rect = Point.origin & size; |
| Paint gradientPaint = new Paint()..shader = new LinearGradient( |
| begin: const Offset(0.0, 0.0), |
| end: const Offset(0.0, 1.0), |
| colors: <Color>[colorTop, colorBottom], |
| stops: <double>[0.0, 1.0] |
| ).createShader(rect); |
| |
| canvas.drawRect(rect, gradientPaint); |
| } |
| } |
| |
| class CloudLayer extends Node { |
| CloudLayer({ ui.Image image, bool dark, bool rotated, double loopTime }) { |
| _sprites.add(_createSprite(image, dark, rotated)); |
| _sprites[0].position = const Point(1024.0, 1024.0); |
| addChild(_sprites[0]); |
| |
| _sprites.add(_createSprite(image, dark, rotated)); |
| _sprites[1].position = const Point(3072.0, 1024.0); |
| addChild(_sprites[1]); |
| |
| actions.run(new ActionRepeatForever( |
| new ActionTween( |
| (Point a) => position = a, |
| Point.origin, |
| const Point(-2048.0, 0.0), |
| loopTime) |
| )); |
| } |
| |
| List<Sprite> _sprites = <Sprite>[]; |
| |
| Sprite _createSprite(ui.Image image, bool dark, bool rotated) { |
| Sprite sprite = new Sprite.fromImage(image); |
| |
| if (rotated) |
| sprite.scaleX = -1.0; |
| |
| if (dark) { |
| sprite.colorOverlay = const Color(0xff000000); |
| sprite.opacity = 0.0; |
| } |
| |
| return sprite; |
| } |
| |
| void set active(bool active) { |
| double opacity; |
| if (active) opacity = 1.0; |
| else opacity = 0.0; |
| |
| for (Sprite sprite in _sprites) { |
| sprite.actions.stopAll(); |
| sprite.actions.run(new ActionTween( |
| (double a) => sprite.opacity = a, |
| sprite.opacity, |
| opacity, |
| 1.0 |
| )); |
| } |
| } |
| } |
| |
| const double _kNumSunRays = 50.0; |
| |
| class Sun extends Node { |
| Sun() { |
| _sun = new Sprite.fromImage(_images['packages/flutter_gallery_assets/sun.png']); |
| _sun.scale = 4.0; |
| _sun.transferMode = TransferMode.plus; |
| addChild(_sun); |
| |
| _rays = <Ray>[]; |
| for (int i = 0; i < _kNumSunRays; i += 1) { |
| Ray ray = new Ray(); |
| addChild(ray); |
| _rays.add(ray); |
| } |
| } |
| |
| Sprite _sun; |
| List<Ray> _rays; |
| |
| void set active(bool active) { |
| actions.stopAll(); |
| |
| double targetOpacity; |
| if (!active) targetOpacity = 0.0; |
| else targetOpacity = 1.0; |
| |
| actions.run( |
| new ActionTween( |
| (double a) => _sun.opacity = a, |
| _sun.opacity, |
| targetOpacity, |
| 2.0 |
| ) |
| ); |
| |
| if (active) { |
| for (Ray ray in _rays) { |
| actions.run(new ActionSequence([ |
| new ActionDelay(1.5), |
| new ActionTween( |
| (double a) => ray.opacity = a, |
| ray.opacity, |
| ray.maxOpacity, |
| 1.5 |
| ) |
| ])); |
| } |
| } else { |
| for (Ray ray in _rays) { |
| actions.run(new ActionTween( |
| (double a) => ray.opacity = a, |
| ray.opacity, |
| 0.0, |
| 0.2 |
| )); |
| } |
| } |
| } |
| } |
| |
| class Ray extends Sprite { |
| double _rotationSpeed; |
| double maxOpacity; |
| |
| Ray() : super.fromImage(_images['packages/flutter_gallery_assets/ray.png']) { |
| pivot = const Point(0.0, 0.5); |
| transferMode = TransferMode.plus; |
| rotation = randomDouble() * 360.0; |
| maxOpacity = randomDouble() * 0.2; |
| opacity = maxOpacity; |
| scaleX = 2.5 + randomDouble(); |
| scaleY = 0.3; |
| _rotationSpeed = randomSignedDouble() * 2.0; |
| |
| // Scale animation |
| double scaleTime = randomSignedDouble() * 2.0 + 4.0; |
| |
| actions.run(new ActionRepeatForever( |
| new ActionSequence([ |
| new ActionTween((double a) => scaleX = a, scaleX, scaleX * 0.5, scaleTime), |
| new ActionTween((double a) => scaleX = a, scaleX * 0.5, scaleX, scaleTime) |
| ]) |
| )); |
| } |
| |
| void update(double dt) { |
| rotation += dt * _rotationSpeed; |
| } |
| } |
| |
| class Rain extends Node { |
| Rain() { |
| _addParticles(1.0); |
| _addParticles(1.5); |
| _addParticles(2.0); |
| } |
| |
| List<ParticleSystem> _particles = <ParticleSystem>[]; |
| |
| void _addParticles(double distance) { |
| ParticleSystem particles = new ParticleSystem( |
| _sprites['raindrop.png'], |
| transferMode: TransferMode.srcATop, |
| posVar: const Point(1300.0, 0.0), |
| direction: 90.0, |
| directionVar: 0.0, |
| speed: 1000.0 / distance, |
| speedVar: 100.0 / distance, |
| startSize: 1.2 / distance, |
| startSizeVar: 0.2 / distance, |
| endSize: 1.2 / distance, |
| endSizeVar: 0.2 / distance, |
| life: 1.5 * distance, |
| lifeVar: 1.0 * distance |
| ); |
| particles.position = const Point(1024.0, -200.0); |
| particles.rotation = 10.0; |
| particles.opacity = 0.0; |
| |
| _particles.add(particles); |
| addChild(particles); |
| } |
| |
| void set active(bool active) { |
| actions.stopAll(); |
| for (ParticleSystem system in _particles) { |
| if (active) { |
| actions.run( |
| new ActionTween( |
| (double a) => system.opacity = a, |
| system.opacity, |
| 1.0, |
| 2.0 |
| )); |
| } else { |
| actions.run( |
| new ActionTween( |
| (double a) => system.opacity = a, |
| system.opacity, |
| 0.0, |
| 0.5 |
| )); |
| } |
| } |
| } |
| } |
| |
| class Snow extends Node { |
| Snow() { |
| _addParticles(_sprites['flake-0.png'], 1.0); |
| _addParticles(_sprites['flake-1.png'], 1.0); |
| _addParticles(_sprites['flake-2.png'], 1.0); |
| |
| _addParticles(_sprites['flake-3.png'], 1.5); |
| _addParticles(_sprites['flake-4.png'], 1.5); |
| _addParticles(_sprites['flake-5.png'], 1.5); |
| |
| _addParticles(_sprites['flake-6.png'], 2.0); |
| _addParticles(_sprites['flake-7.png'], 2.0); |
| _addParticles(_sprites['flake-8.png'], 2.0); |
| } |
| |
| List<ParticleSystem> _particles = <ParticleSystem>[]; |
| |
| void _addParticles(Texture texture, double distance) { |
| ParticleSystem particles = new ParticleSystem( |
| texture, |
| transferMode: TransferMode.srcATop, |
| posVar: const Point(1300.0, 0.0), |
| direction: 90.0, |
| directionVar: 0.0, |
| speed: 150.0 / distance, |
| speedVar: 50.0 / distance, |
| startSize: 1.0 / distance, |
| startSizeVar: 0.3 / distance, |
| endSize: 1.2 / distance, |
| endSizeVar: 0.2 / distance, |
| life: 20.0 * distance, |
| lifeVar: 10.0 * distance, |
| emissionRate: 2.0, |
| startRotationVar: 360.0, |
| endRotationVar: 360.0, |
| radialAccelerationVar: 10.0 / distance, |
| tangentialAccelerationVar: 10.0 / distance |
| ); |
| particles.position = const Point(1024.0, -50.0); |
| particles.opacity = 0.0; |
| |
| _particles.add(particles); |
| addChild(particles); |
| } |
| |
| void set active(bool active) { |
| actions.stopAll(); |
| for (ParticleSystem system in _particles) { |
| if (active) { |
| actions.run( |
| new ActionTween((double a) => system.opacity = a, system.opacity, 1.0, 2.0 |
| )); |
| } else { |
| actions.run( |
| new ActionTween((double a) => system.opacity = a, system.opacity, 0.0, 0.5 |
| )); |
| } |
| } |
| } |
| } |