diff --git a/lib/page/login/login_page.dart b/lib/page/login/login_page.dart index 436f6d42..c5ba2bc0 100644 --- a/lib/page/login/login_page.dart +++ b/lib/page/login/login_page.dart @@ -9,8 +9,10 @@ import 'package:gsy_github_app_flutter/redux/gsy_state.dart'; import 'package:gsy_github_app_flutter/redux/login_redux.dart'; import 'package:gsy_github_app_flutter/common/style/gsy_style.dart'; import 'package:gsy_github_app_flutter/common/utils/common_utils.dart'; +import 'package:gsy_github_app_flutter/widget/animated_background.dart'; import 'package:gsy_github_app_flutter/widget/gsy_flex_button.dart'; import 'package:gsy_github_app_flutter/widget/gsy_input_widget.dart'; +import 'package:gsy_github_app_flutter/widget/particle/particle_widget.dart'; /** * 登录页 @@ -38,81 +40,85 @@ class _LoginPageState extends State with LoginBLoC { child: Scaffold( body: new Container( color: Theme.of(context).primaryColor, - child: new Center( - ///防止overFlow的现象 - child: SafeArea( - ///同时弹出键盘不遮挡 - child: SingleChildScrollView( - child: new Card( - elevation: 5.0, - shape: new RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10.0))), - color: GSYColors.cardWhite, - margin: const EdgeInsets.only(left: 30.0, right: 30.0), - child: new Padding( - padding: new EdgeInsets.only( - left: 30.0, top: 40.0, right: 30.0, bottom: 0.0), - child: new Column( - mainAxisAlignment: MainAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - children: [ - new Image( - image: new AssetImage(GSYICons.DEFAULT_USER_ICON), - width: 90.0, - height: 90.0), - new Padding(padding: new EdgeInsets.all(10.0)), - new GSYInputWidget( - hintText: GSYLocalizations.i18n(context) - .login_username_hint_text, - iconData: GSYICons.LOGIN_USER, - onChanged: (String value) { - _userName = value; - }, - controller: userController, - ), - new Padding(padding: new EdgeInsets.all(10.0)), - new GSYInputWidget( - hintText: GSYLocalizations.i18n(context) - .login_password_hint_text, - iconData: GSYICons.LOGIN_PW, - obscureText: true, - onChanged: (String value) { - _password = value; - }, - controller: pwController, - ), - new Padding(padding: new EdgeInsets.all(30.0)), - new GSYFlexButton( - text: GSYLocalizations.i18n(context).login_text, - color: Theme.of(context).primaryColor, - textColor: GSYColors.textWhite, - onPress: loginIn, - ), - new Padding(padding: new EdgeInsets.all(15.0)), - InkWell( - onTap: () { - CommonUtils.showLanguageDialog(context); - }, - child: Text( - GSYLocalizations.i18n(context).switch_language, - style: TextStyle(color: GSYColors.subTextColor), + child: Stack(children: [ + Positioned.fill(child: AnimatedBackground()), + Positioned.fill(child: ParticlesWidget(30)), + new Center( + ///防止overFlow的现象 + child: SafeArea( + ///同时弹出键盘不遮挡 + child: SingleChildScrollView( + child: new Card( + elevation: 5.0, + shape: new RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(10.0))), + color: GSYColors.cardWhite, + margin: const EdgeInsets.only(left: 30.0, right: 30.0), + child: new Padding( + padding: new EdgeInsets.only( + left: 30.0, top: 40.0, right: 30.0, bottom: 0.0), + child: new Column( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + new Image( + image: new AssetImage(GSYICons.DEFAULT_USER_ICON), + width: 90.0, + height: 90.0), + new Padding(padding: new EdgeInsets.all(10.0)), + new GSYInputWidget( + hintText: GSYLocalizations.i18n(context) + .login_username_hint_text, + iconData: GSYICons.LOGIN_USER, + onChanged: (String value) { + _userName = value; + }, + controller: userController, ), - ), - new Padding(padding: new EdgeInsets.all(15.0)), - ], + new Padding(padding: new EdgeInsets.all(10.0)), + new GSYInputWidget( + hintText: GSYLocalizations.i18n(context) + .login_password_hint_text, + iconData: GSYICons.LOGIN_PW, + obscureText: true, + onChanged: (String value) { + _password = value; + }, + controller: pwController, + ), + new Padding(padding: new EdgeInsets.all(30.0)), + new GSYFlexButton( + text: GSYLocalizations.i18n(context).login_text, + color: Theme.of(context).primaryColor, + textColor: GSYColors.textWhite, + onPress: loginIn, + ), + new Padding(padding: new EdgeInsets.all(15.0)), + InkWell( + onTap: () { + CommonUtils.showLanguageDialog(context); + }, + child: Text( + GSYLocalizations.i18n(context).switch_language, + style: TextStyle(color: GSYColors.subTextColor), + ), + ), + new Padding(padding: new EdgeInsets.all(15.0)), + ], + ), ), ), ), ), - ), - ), + ) + ]), ), ), ); } } -mixin LoginBLoC on State { +mixin LoginBLoC on State { final TextEditingController userController = new TextEditingController(); final TextEditingController pwController = new TextEditingController(); diff --git a/lib/widget/animated_background.dart b/lib/widget/animated_background.dart new file mode 100644 index 00000000..f3ff61e0 --- /dev/null +++ b/lib/widget/animated_background.dart @@ -0,0 +1,30 @@ +import 'package:flutter/material.dart'; +import 'package:simple_animations/simple_animations/controlled_animation.dart'; +import 'package:simple_animations/simple_animations/multi_track_tween.dart'; + +class AnimatedBackground extends StatelessWidget { + @override + Widget build(BuildContext context) { + final tween = MultiTrackTween([ + Track("color1").add(Duration(seconds: 3), + ColorTween(begin: Color(0xffD38312), end: Colors.lightBlue.shade900)), + Track("color2").add(Duration(seconds: 3), + ColorTween(begin: Color(0xffA83279), end: Colors.blue.shade600)) + ]); + + return ControlledAnimation( + playback: Playback.MIRROR, + tween: tween, + duration: tween.duration, + builder: (context, animation) { + return Container( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [animation["color1"], animation["color2"]])), + ); + }, + ); + } +} \ No newline at end of file diff --git a/lib/widget/particle/particle_model.dart b/lib/widget/particle/particle_model.dart new file mode 100644 index 00000000..5d93bb7e --- /dev/null +++ b/lib/widget/particle/particle_model.dart @@ -0,0 +1,43 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:simple_animations/simple_animations/animation_progress.dart'; +import 'package:simple_animations/simple_animations/multi_track_tween.dart'; + +class ParticleModel { + Animatable tween; + double size; + AnimationProgress animationProgress; + Random random; + int defaultMilliseconds; + + ParticleModel(this.random, {this.defaultMilliseconds = 500}) { + restart(); + } + + restart({Duration time = Duration.zero}) { + final startPosition = Offset(-0.2 + 1.4 * random.nextDouble(), 1.2); + + final endPosition = Offset(-0.2 + 1.4 * random.nextDouble(), -0.2); + + final duration = + Duration(milliseconds: defaultMilliseconds + random.nextInt(1000)); + + tween = MultiTrackTween([ + Track("x").add( + duration, Tween(begin: startPosition.dx, end: endPosition.dx), + curve: Curves.easeInOutSine), + Track("y").add( + duration, Tween(begin: startPosition.dy, end: endPosition.dy), + curve: Curves.easeIn), + ]); + animationProgress = AnimationProgress(duration: duration, startTime: time); + size = 0.2 + random.nextDouble() * 0.4; + } + + maintainRestart(Duration time) { + if (animationProgress.progress(time) == 1.0) { + restart(time: time); + } + } +} diff --git a/lib/widget/particle/particle_painter.dart b/lib/widget/particle/particle_painter.dart new file mode 100644 index 00000000..3e993528 --- /dev/null +++ b/lib/widget/particle/particle_painter.dart @@ -0,0 +1,25 @@ +import 'package:flutter/material.dart'; +import 'package:gsy_github_app_flutter/widget/particle/particle_model.dart'; + +class ParticlePainter extends CustomPainter { + List particles; + Duration time; + Color color; + + ParticlePainter(this.particles, this.time, {this.color = Colors.white}); + + @override + void paint(Canvas canvas, Size size) { + final paint = Paint()..color = color.withAlpha(50); + particles.forEach((particle) { + var progress = particle.animationProgress.progress(time); + final animation = particle.tween.transform(progress); + final position = + Offset(animation["x"] * size.width, animation["y"] * size.height); + canvas.drawCircle(position, size.width * 0.2 * particle.size, paint); + }); + } + + @override + bool shouldRepaint(CustomPainter oldDelegate) => true; +} diff --git a/lib/widget/particle/particle_widget.dart b/lib/widget/particle/particle_widget.dart new file mode 100644 index 00000000..6f690999 --- /dev/null +++ b/lib/widget/particle/particle_widget.dart @@ -0,0 +1,47 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:gsy_github_app_flutter/widget/particle/particle_model.dart'; +import 'package:gsy_github_app_flutter/widget/particle/particle_painter.dart'; +import 'package:simple_animations/simple_animations/rendering.dart'; + +class ParticlesWidget extends StatefulWidget { + final int numberOfParticles; + + ParticlesWidget(this.numberOfParticles); + + @override + _ParticlesWidgetState createState() => _ParticlesWidgetState(); +} + +class _ParticlesWidgetState extends State { + final Random random = Random(); + + final List particles = []; + + @override + void initState() { + List.generate(widget.numberOfParticles, (index) { + particles.add(ParticleModel(random, defaultMilliseconds: 2000)); + }); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Rendering( + startTime: Duration(seconds: 50), + onTick: _simulateParticles, + builder: (context, time) { + _simulateParticles(time); + return CustomPaint( + painter: ParticlePainter(particles, time), + ); + }, + ); + } + + _simulateParticles(Duration time) { + particles.forEach((particle) => particle.maintainRestart(time)); + } +} diff --git a/pubspec.lock b/pubspec.lock index 461e2463..3a05835e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -608,6 +608,13 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "0.2.3" + simple_animations: + dependency: "direct main" + description: + name: simple_animations + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.3.4" sky_engine: dependency: transitive description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index 6c3ce708..3935bcb7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -44,6 +44,7 @@ dependencies: built_value: ^6.8.2 graphql: ^2.0.0 flutter_json_widget: 1.0.2 + simple_animations: 1.3.4 flutter_localizations: sdk: flutter photo_view: