Flutter Animation - Move upward when displayed using Matrix4 - flutter

Is there any inbuilt animation in flutter which has effect as the page below?
Example : Long Scrolling page and as we scroll down new content appear on screen and slightly move upward.
piedpiper

AnimatedList can help about that I think, also you can use it with StreamBuilder too if you need.
https://api.flutter.dev/flutter/widgets/AnimatedList-class.html
Or you can use AnimatedContainer for each page will come below;
https://api.flutter.dev/flutter/widgets/AnimatedContainer-class.html
Edit: Code sample;
import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ListView(
children: [Container(
color: Colors.blue,
height: 1000,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
SlideFadeTransition(
delayStart: Duration(milliseconds: 800),
offset: 2,
child: new Text('Test Text 1')),
SlideFadeTransition(
delayStart: Duration(milliseconds: 1200),
offset: 4,
child: new Text('Test Text 2'))
],
),
),
Container(
color: Colors.red,
height: 1000,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
SlideFadeTransition(
delayStart: Duration(milliseconds: 1200),
offset: 2,
child: new Text('Test Text 1')),
SlideFadeTransition(
delayStart: Duration(milliseconds: 2400),
offset: 4,
child: new Text('Test Text 2'))
],
),
),
Container(
color: Colors.orange,
height: 1000,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
SlideFadeTransition(
delayStart: Duration(milliseconds: 1200),
offset: 2,
child: new Text('Test Text 1')),
SlideFadeTransition(
delayStart: Duration(milliseconds: 2400),
offset: 4,
child: new Text('Test Text 2'))
],
),
),
]
)
)
);
}
}
enum Direction { vertical, horizontal }
class SlideFadeTransition extends StatefulWidget {
///The child on which to apply the given [SlideFadeTransition]
final Widget child;
///The offset by which to slide and [child] into view from [Direction].
///Defaults to 0.2
final double offset;
///The curve used to animate the [child] into view.
///Defaults to [Curves.easeIn]
final Curve curve;
///The direction from which to animate the [child] into view. [Direction.horizontal]
///will make the child slide on x-axis by [offset] and [Direction.vertical] on y-axis.
///Defaults to [Direction.vertical]
final Direction direction;
///The delay with which to animate the [child]. Takes in a [Duration] and
/// defaults to 0.0 seconds
final Duration delayStart;
///The total duration in which the animation completes. Defaults to 800 milliseconds
final Duration animationDuration;
SlideFadeTransition({
#required this.child,
this.offset = 0.2,
this.curve = Curves.easeIn,
this.direction = Direction.vertical,
this.delayStart = const Duration(seconds: 0),
this.animationDuration = const Duration(milliseconds: 800),
});
#override
_SlideFadeTransitionState createState() => _SlideFadeTransitionState();
}
class _SlideFadeTransitionState extends State<SlideFadeTransition>
with SingleTickerProviderStateMixin {
Animation<Offset> _animationSlide;
AnimationController _animationController;
Animation<double> _animationFade;
#override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this,
duration: widget.animationDuration,
);
//configure the animation controller as per the direction
if (widget.direction == Direction.vertical) {
_animationSlide =
Tween<Offset>(begin: Offset(0, widget.offset), end: Offset(0, 0))
.animate(CurvedAnimation(
curve: widget.curve,
parent: _animationController,
));
} else {
_animationSlide =
Tween<Offset>(begin: Offset(widget.offset, 0), end: Offset(0, 0))
.animate(CurvedAnimation(
curve: widget.curve,
parent: _animationController,
));
}
_animationFade =
Tween<double>(begin: -1.0, end: 1.0).animate(CurvedAnimation(
curve: widget.curve,
parent: _animationController,
));
Timer(widget.delayStart, () {
_animationController.forward();
});
}
#override
Widget build(BuildContext context) {
return FadeTransition(
opacity: _animationFade,
child: SlideTransition(
position: _animationSlide,
child: widget.child,
),
);}}

Related

Flutter Circle Menu onPressed Action

I'm a beginner with Flutter, I have create a circle menu with Flutter, I got 6 buttons inside it and i want to go to different page depend of what button have been clicked. I know how to navigate to another page, but the problem is I can only use 1 action ("close action"). Maybe I can put an argument on the _buildbutton for the onPressed ?
import 'package:flutter/material.dart';
import 'dart:math';
import 'package:vector_math/vector_math.dart' show radians;
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: Color(0xFFB3E5FC),
body: SizedBox.expand(child: RadialMenu())),
);
}
}
class RadialMenu extends StatefulWidget {
createState() => _RadialMenuState();
}
class _RadialMenuState extends State<RadialMenu>
with SingleTickerProviderStateMixin {
AnimationController controller;
void initState() {
super.initState();
controller =
AnimationController(duration: Duration(milliseconds: 900), vsync: this);
}
Widget build(BuildContext context) {
return RadialAnimation(controller: controller);
}
}
class RadialAnimation extends StatelessWidget {
RadialAnimation({Key key, this.controller})
: scale = Tween<double>(
begin: 1.5,
end: 0.0,
).animate(
CurvedAnimation(parent: controller, curve: Curves.fastOutSlowIn),
),
translation = Tween<double>(
begin: 0.0,
end: 100.0,
).animate(
CurvedAnimation(parent: controller, curve: Curves.linear),
),
rotation = Tween<double>(begin: 0.0, end: 360.0).animate(
CurvedAnimation(
parent: controller,
curve: Interval(
0.0,
0.7,
curve: Curves.decelerate,
)),
),
super(key: key);
final AnimationController controller;
final Animation<double> scale;
final Animation<double> translation;
final Animation<double> rotation;
build(context) {
return AnimatedBuilder(
animation: controller,
builder: (context, builder) {
return Transform.rotate(
angle: radians(rotation.value),
child: Stack(alignment: Alignment.center, children: [
_buildButton(0,
color: Color(0xFF29B6F6), icon: FontAwesomeIcons.chartBar),
_buildButton(60,
color: Color(0xFF29B6F6), icon: FontAwesomeIcons.clipboard),
_buildButton(120,
color: Color(0xFF29B6F6), icon: FontAwesomeIcons.chartBar),
_buildButton(180,
color: Color(0xFF29B6F6), icon: FontAwesomeIcons.book),
_buildButton(240,
color: Color(0xFF29B6F6),
icon: FontAwesomeIcons.arrowsAltV),
_buildButton(300,
color: Color(0xFF29B6F6), icon: FontAwesomeIcons.phoneAlt),
_buildButton(360,
color: Color(0xFF29B6F6),
icon: FontAwesomeIcons.compressAlt),
Transform.scale(
scale: scale.value - 1.5,
child: FloatingActionButton(
child: Icon(FontAwesomeIcons.timesCircle),
onPressed: _close,
backgroundColor: Color(0xFF29B6F6)),
),
Transform.scale(
scale: scale.value,
child: FloatingActionButton(
child: Icon(FontAwesomeIcons.solidDotCircle),
onPressed: _open,
backgroundColor: Color(0xFF29B6F6)),
),
]));
});
}
_buildButton(double angle, {Color color, IconData icon}) {
final double rad = radians(angle);
return Transform(
transform: Matrix4.identity()
..translate(
(translation.value) * cos(rad), (translation.value) * sin(rad)),
child: FloatingActionButton(
child: Icon(icon), backgroundColor: color, onPressed: _close));
}
_open() {
controller.forward();
}
_close() {
controller.reverse();
}
}
In your _buildButton you can an action callback like this
_buildButton(double angle, Function callback, {Color color, IconData icon}) {
// ...
}
Then by building a button you can either pass _open or _close

Flutter one time animation for each start of app(not first run)

I want to use some animations on my main screen but only when app start, because while using the app users have to navigate back to main screen oftenly and those animations makes loading time and became annoying to see again and again.. So, is there any way to make animatoins active only the first start(not first run) of app then make them inactive till restart the app?
Here is a simple code; not same but similar logic with the animations I've used, my code is too complex to copy-paste so I'm sending a sample code which I send to another question as answer;
import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ListView(
children: [Container(
color: Colors.blue,
height: 1000,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
SlideFadeTransition(
delayStart: Duration(milliseconds: 800),
offset: 2,
child: new Text('Test Text 1')),
SlideFadeTransition(
delayStart: Duration(milliseconds: 1200),
offset: 4,
child: new Text('Test Text 2'))
],
),
),
Container(
color: Colors.red,
height: 1000,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
SlideFadeTransition(
delayStart: Duration(milliseconds: 1200),
offset: 2,
child: new Text('Test Text 1')),
SlideFadeTransition(
delayStart: Duration(milliseconds: 2400),
offset: 4,
child: new Text('Test Text 2'))
],
),
),
Container(
color: Colors.orange,
height: 1000,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
SlideFadeTransition(
delayStart: Duration(milliseconds: 1200),
offset: 2,
child: new Text('Test Text 1')),
SlideFadeTransition(
delayStart: Duration(milliseconds: 2400),
offset: 4,
child: new Text('Test Text 2'))
],
),
),
]
)
)
);
}
}
enum Direction { vertical, horizontal }
class SlideFadeTransition extends StatefulWidget {
///The child on which to apply the given [SlideFadeTransition]
final Widget child;
///The offset by which to slide and [child] into view from [Direction].
///Defaults to 0.2
final double offset;
///The curve used to animate the [child] into view.
///Defaults to [Curves.easeIn]
final Curve curve;
///The direction from which to animate the [child] into view. [Direction.horizontal]
///will make the child slide on x-axis by [offset] and [Direction.vertical] on y-axis.
///Defaults to [Direction.vertical]
final Direction direction;
///The delay with which to animate the [child]. Takes in a [Duration] and
/// defaults to 0.0 seconds
final Duration delayStart;
///The total duration in which the animation completes. Defaults to 800 milliseconds
final Duration animationDuration;
SlideFadeTransition({
#required this.child,
this.offset = 0.2,
this.curve = Curves.easeIn,
this.direction = Direction.vertical,
this.delayStart = const Duration(seconds: 0),
this.animationDuration = const Duration(milliseconds: 800),
});
#override
_SlideFadeTransitionState createState() => _SlideFadeTransitionState();
}
class _SlideFadeTransitionState extends State<SlideFadeTransition>
with SingleTickerProviderStateMixin {
Animation<Offset> _animationSlide;
AnimationController _animationController;
Animation<double> _animationFade;
#override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this,
duration: widget.animationDuration,
);
//configure the animation controller as per the direction
if (widget.direction == Direction.vertical) {
_animationSlide =
Tween<Offset>(begin: Offset(0, widget.offset), end: Offset(0, 0))
.animate(CurvedAnimation(
curve: widget.curve,
parent: _animationController,
));
} else {
_animationSlide =
Tween<Offset>(begin: Offset(widget.offset, 0), end: Offset(0, 0))
.animate(CurvedAnimation(
curve: widget.curve,
parent: _animationController,
));
}
_animationFade =
Tween<double>(begin: -1.0, end: 1.0).animate(CurvedAnimation(
curve: widget.curve,
parent: _animationController,
));
Timer(widget.delayStart, () {
_animationController.forward();
});
}
#override
Widget build(BuildContext context) {
return FadeTransition(
opacity: _animationFade,
child: SlideTransition(
position: _animationSlide,
child: widget.child,
),
);}}
You can copy paste run full code below
Step 1: You can define a bool firstRun = true
Step 2: When Navigate to other page set firstRun to false
Step 3: In _SlideFadeTransitionState 's build method check if not firstRun and return widget.child directly
return firstRun? FadeTransition(...widget.child) : widget.child
working demo
code snippet
bool firstRun = true;
...
RaisedButton(
child: Text('Open route'),
onPressed: () {
firstRun = false;
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondRoute()),
);
},
)
...
#override
Widget build(BuildContext context) {
print('first run ${firstRun}');
return firstRun? FadeTransition(
opacity: _animationFade,
child: SlideTransition(
position: _animationSlide,
child: widget.child,
),
) : widget.child;
}
full code
import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
bool firstRun = true;
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
void initState() {
// TODO: implement initState
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ListView(children: [
Container(
color: Colors.blue,
height: 1000,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
SlideFadeTransition(
delayStart: Duration(milliseconds: 800),
offset: 2,
child: new Text('Test Text 1')),
SlideFadeTransition(
delayStart: Duration(milliseconds: 1200),
offset: 4,
child: new Text('Test Text 2'))
],
),
),
Container(
color: Colors.red,
height: 1000,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
SlideFadeTransition(
delayStart: Duration(milliseconds: 1200),
offset: 2,
child: new Text('Test Text 1')),
SlideFadeTransition(
delayStart: Duration(milliseconds: 2400),
offset: 4,
child: new Text('Test Text 2'))
],
),
),
Container(
color: Colors.orange,
height: 1000,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
SlideFadeTransition(
delayStart: Duration(milliseconds: 1200),
offset: 2,
child: new Text('Test Text 1')),
SlideFadeTransition(
delayStart: Duration(milliseconds: 2400),
offset: 4,
child: new Text('Test Text 2'))
],
),
),
RaisedButton(
child: Text('Open route'),
onPressed: () {
firstRun = false;
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondRoute()),
);
},
),
])));
}
}
enum Direction { vertical, horizontal }
class SlideFadeTransition extends StatefulWidget {
///The child on which to apply the given [SlideFadeTransition]
final Widget child;
///The offset by which to slide and [child] into view from [Direction].
///Defaults to 0.2
final double offset;
///The curve used to animate the [child] into view.
///Defaults to [Curves.easeIn]
final Curve curve;
///The direction from which to animate the [child] into view. [Direction.horizontal]
///will make the child slide on x-axis by [offset] and [Direction.vertical] on y-axis.
///Defaults to [Direction.vertical]
final Direction direction;
///The delay with which to animate the [child]. Takes in a [Duration] and
/// defaults to 0.0 seconds
final Duration delayStart;
///The total duration in which the animation completes. Defaults to 800 milliseconds
final Duration animationDuration;
SlideFadeTransition({
#required this.child,
this.offset = 0.2,
this.curve = Curves.easeIn,
this.direction = Direction.vertical,
this.delayStart = const Duration(seconds: 0),
this.animationDuration = const Duration(milliseconds: 800),
});
#override
_SlideFadeTransitionState createState() => _SlideFadeTransitionState();
}
class _SlideFadeTransitionState extends State<SlideFadeTransition>
with SingleTickerProviderStateMixin {
Animation<Offset> _animationSlide;
AnimationController _animationController;
Animation<double> _animationFade;
#override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this,
duration: widget.animationDuration,
);
//configure the animation controller as per the direction
if (widget.direction == Direction.vertical) {
_animationSlide =
Tween<Offset>(begin: Offset(0, widget.offset), end: Offset(0, 0))
.animate(CurvedAnimation(
curve: widget.curve,
parent: _animationController,
));
} else {
_animationSlide =
Tween<Offset>(begin: Offset(widget.offset, 0), end: Offset(0, 0))
.animate(CurvedAnimation(
curve: widget.curve,
parent: _animationController,
));
}
_animationFade =
Tween<double>(begin: -1.0, end: 1.0).animate(CurvedAnimation(
curve: widget.curve,
parent: _animationController,
));
Timer(widget.delayStart, () {
_animationController.forward();
});
}
#override
Widget build(BuildContext context) {
print('first run ${firstRun}');
return firstRun? FadeTransition(
opacity: _animationFade,
child: SlideTransition(
position: _animationSlide,
child: widget.child,
),
) : widget.child;
}
}
class SecondRoute extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Second Route"),
),
body: Center(
child: RaisedButton(
onPressed: () {
// Navigate back to first route when tapped.
},
child: Text('Go back!'),
),
),
);
}
}

How to add two different animation with different duration in Flutter?

I have the following code animate any widget
import 'dart:async';
import 'package:flutter/material.dart';
class AnimElasticOut extends StatefulWidget {
final Widget child;
final int delay;
final Key key;
final startAnimation;
AnimElasticOut(
{#required this.key,
#required this.child,
this.delay,
this.startAnimation});
#override
AnimElasticOutState createState() => AnimElasticOutState();
}
class AnimElasticOutState extends State<AnimElasticOut>
with TickerProviderStateMixin {
AnimationController controller;
Animation<double> animation;
int duration = 1000;
#override
void initState() {
super.initState();
controller = AnimationController(
vsync: this, duration: Duration(milliseconds: duration));
animation = CurvedAnimation(
parent: controller,
curve: Curves.elasticOut,
);
if (widget.startAnimation != null) {
if (widget.delay == null) {
controller.forward();
} else {
Timer(Duration(milliseconds: widget.delay), () {
controller.forward();
});
}
}
}
#override
void dispose() {
super.dispose();
controller.dispose();
}
#override
Widget build(BuildContext context) {
return ScaleTransition(
child: widget.child,
scale: animation,
);
}
}
Sample usage
AnimElasticOut(
key: counterElasticOutKey,
child: Container(
height: 35,
padding: EdgeInsets.only(
left: 5, top: 5, bottom: 5, right: 15),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Icon(
Icons.add_alarm,
color: Colors.amber,
),
SizedBox(
width: 10,
),
Counter(
key: counterKey,
),
],
),
),
),
I start the animation using the following code.
counterElasticOutKey.currentState.controller.forward(from: 0);
Now it animates the widget nicely.
I also have another code to reverse the animation.
counterElasticOutKey.currentState.controller.reverse();
What I want is to use another animation while reversing. Also, some other duration.
For example, I want Curves.easeInOutCubic as an animation curve with a duration of milliseconds: 500
How can I do this?
There is a property reverseDuration in AnimationController & reverseCurve in Animation<T>
controller = AnimationController(
vsync: this,
duration: Duration(milliseconds: 1000),
reverseDuration: Duration(milliseconds: 250),
);
animation = CurvedAnimation(
parent: controller,
curve: Curves.elasticOut,
reverseCurve: Curves.easeInQuad,
);

How to place widgets around a circle using flutter?

I'm new in flutter, I need to create an interface like this
I've created a circular button in this way:
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => _buttonCallBack(),
child: ClipOval(
child: FractionallySizedBox(
heightFactor : 0.4,
widthFactor : 0.6,
child : Container(
color: Colors.lightGreen,
child : Center(
child: Text(
"Start",
style: new TextStyle(fontSize: 36.0),
)
)
)
),
),
);
But now I don't how to show buttons around the circle. Anyone has any ideas ?
If you're trying a circular menu you can follow this :
Add vector_math: ^2.0.8 and font_awesome_flutter: 8.4.0 to pubspec.yaml
Here is the full source of the example :
import 'package:flutter/material.dart';
import 'dart:math';
import 'package:vector_math/vector_math.dart' show radians, Vector3;
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'FlutterBase',
home: Scaffold(
body: SizedBox.expand(child: RadialMenu())
)
);
}
}
class RadialMenu extends StatefulWidget {
createState() => _RadialMenuState();
}
class _RadialMenuState extends State<RadialMenu> with SingleTickerProviderStateMixin {
AnimationController controller;
#override
void initState() {
super.initState();
controller = AnimationController(duration: Duration(milliseconds: 900), vsync: this);
// ..addListener(() => setState(() {}));
}
#override
Widget build(BuildContext context) {
return RadialAnimation(controller: controller);
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
}
class RadialAnimation extends StatelessWidget {
RadialAnimation({ Key key, this.controller }) :
translation = Tween<double>(
begin: 0.0,
end: 100.0,
).animate(
CurvedAnimation(
parent: controller,
curve: Curves.elasticOut
),
),
scale = Tween<double>(
begin: 1.5,
end: 0.0,
).animate(
CurvedAnimation(
parent: controller,
curve: Curves.fastOutSlowIn
),
),
rotation = Tween<double>(
begin: 0.0,
end: 360.0,
).animate(
CurvedAnimation(
parent: controller,
curve: Interval(
0.0, 0.7,
curve: Curves.decelerate,
),
),
),
super(key: key);
final AnimationController controller;
final Animation<double> rotation;
final Animation<double> translation;
final Animation<double> scale;
#override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: controller,
builder: (context, widget) {
return Transform.rotate(
angle: radians(rotation.value),
child: Stack(
alignment: Alignment.center,
children: <Widget>[
_buildButton(0, color: Colors.red, icon: FontAwesomeIcons.thumbtack),
_buildButton(45, color: Colors.green, icon:FontAwesomeIcons.sprayCan),
_buildButton(90, color: Colors.orange, icon: FontAwesomeIcons.fire),
_buildButton(135, color: Colors.blue, icon:FontAwesomeIcons.kiwiBird),
_buildButton(180, color: Colors.black, icon:FontAwesomeIcons.cat),
_buildButton(225, color: Colors.indigo, icon:FontAwesomeIcons.paw),
_buildButton(270, color: Colors.pink, icon: FontAwesomeIcons.bong),
_buildButton(315, color: Colors.yellow, icon:FontAwesomeIcons.bolt),
Transform.scale(
scale: scale.value - 1,
child: FloatingActionButton(child: Icon(FontAwesomeIcons.timesCircle), onPressed: _close, backgroundColor: Colors.red),
),
Transform.scale(
scale: scale.value,
child: FloatingActionButton(child: Icon(FontAwesomeIcons.solidDotCircle), onPressed: _open),
)
])
);
});
}
_open() {
controller.forward();
}
_close() {
controller.reverse();
}
_buildButton(double angle, { Color color, IconData icon }) {
final double rad = radians(angle);
return Transform(
transform: Matrix4.identity()..translate(
(translation.value) * cos(rad),
(translation.value) * sin(rad)
),
child: FloatingActionButton(
child: Icon(icon), backgroundColor: color, onPressed: _close, elevation: 0)
);
}
}

How can I make a "show up" text animation in Flutter?

I want to achieve the kind of behavior shown in the material.io page:
The text does a nice transition when is being shown for the first time.
How can I do that in Flutter?
You can compose widgets like this:
import 'dart:async';
import 'package:flutter/material.dart';
class ShowUp extends StatefulWidget {
final Widget child;
final int delay;
ShowUp({#required this.child, this.delay});
#override
_ShowUpState createState() => _ShowUpState();
}
class _ShowUpState extends State<ShowUp> with TickerProviderStateMixin {
AnimationController _animController;
Animation<Offset> _animOffset;
#override
void initState() {
super.initState();
_animController =
AnimationController(vsync: this, duration: Duration(milliseconds: 500));
final curve =
CurvedAnimation(curve: Curves.decelerate, parent: _animController);
_animOffset =
Tween<Offset>(begin: const Offset(0.0, 0.35), end: Offset.zero)
.animate(curve);
if (widget.delay == null) {
_animController.forward();
} else {
Timer(Duration(milliseconds: widget.delay), () {
_animController.forward();
});
}
}
#override
void dispose() {
super.dispose();
_animController.dispose();
}
#override
Widget build(BuildContext context) {
return FadeTransition(
child: SlideTransition(
position: _animOffset,
child: widget.child,
),
opacity: _animController,
);
}
}
Then you can use it like this:
int delayAmount = 500;
...........
...........
...........
Column(
children: <Widget>[
ShowUp(
child: Text("The first texto to be shown"),
delay: delayAmount,
),
ShowUp(
child: Text("The text below the first"),
delay: delayAmount + 200,
),
ShowUp(
child: Column(
children: <Widget>[
Text("Texts together 1"),
Text("Texts together 2"),
Text("Texts together 3"),
],
),
delay: delayAmount + 400,
),
],
),
Note that this "ShowUp" widgets can animate anything, not just texts.
There's an existing package called show_up_animation for it which is based on the implementation below.
This is a generalized widget to provide this animation. All you have to do is to wrap your widget(yes, any widget) inside SlideFadeTransition widget and voila!
It gives a lot of control. For example, you can control the amount, speed, direction, delay and even the curve of the animation.
///Wrapper class to implement slide and fade animations at the same time to
///a given element. Wrap the widget that you wish to appear with slide-fade
///transition in this class.
import 'dart:async';
import 'package:flutter/material.dart';
enum Direction { vertical, horizontal }
class SlideFadeTransition extends StatefulWidget {
///The child on which to apply the given [SlideFadeTransition]
final Widget child;
///The offset by which to slide and [child] into view from [Direction].
///Defaults to 0.2
final double offset;
///The curve used to animate the [child] into view.
///Defaults to [Curves.easeIn]
final Curve curve;
///The direction from which to animate the [child] into view. [Direction.horizontal]
///will make the child slide on x-axis by [offset] and [Direction.vertical] on y-axis.
///Defaults to [Direction.vertical]
final Direction direction;
///The delay with which to animate the [child]. Takes in a [Duration] and
/// defaults to 0.0 seconds
final Duration delayStart;
///The total duration in which the animation completes. Defaults to 800 milliseconds
final Duration animationDuration;
SlideFadeTransition({
#required this.child,
this.offset = 0.2,
this.curve = Curves.easeIn,
this.direction = Direction.vertical,
this.delayStart = const Duration(seconds: 0),
this.animationDuration = const Duration(milliseconds: 800),
});
#override
_SlideFadeTransitionState createState() => _SlideFadeTransitionState();
}
class _SlideFadeTransitionState extends State<SlideFadeTransition>
with SingleTickerProviderStateMixin {
Animation<Offset> _animationSlide;
AnimationController _animationController;
Animation<double> _animationFade;
#override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this,
duration: widget.animationDuration,
);
//configure the animation controller as per the direction
if (widget.direction == Direction.vertical) {
_animationSlide =
Tween<Offset>(begin: Offset(0, widget.offset), end: Offset(0, 0))
.animate(CurvedAnimation(
curve: widget.curve,
parent: _animationController,
));
} else {
_animationSlide =
Tween<Offset>(begin: Offset(widget.offset, 0), end: Offset(0, 0))
.animate(CurvedAnimation(
curve: widget.curve,
parent: _animationController,
));
}
_animationFade =
Tween<double>(begin: -1.0, end: 1.0).animate(CurvedAnimation(
curve: widget.curve,
parent: _animationController,
));
Timer(widget.delayStart, () {
_animationController.forward();
});
}
#override
Widget build(BuildContext context) {
return FadeTransition(
opacity: _animationFade,
child: SlideTransition(
position: _animationSlide,
child: widget.child,
),
);
}
}
To use it, you just have to wrap your text widget or any widget for that matter with SlideFadeTransition widget. Below is a complete working example.
import 'dart:async';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Show up Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Show up Demo'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SlideFadeTransition(
child: Text(
'You have pushed the button this many times:',
),
),
SlideFadeTransition(
delayStart: Duration(milliseconds: 800),
child: Text(
'0',
style: Theme.of(context).textTheme.display1,
),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {});
},
tooltip: 'Animate',
child: Icon(Icons.add),
),
);
}
}
///Wrapper class to implement slide and fade animations at the same time to
///a given element. Wrap the widget that you wish to appear with slide-fade
///transition in this class.
enum Direction { vertical, horizontal }
class SlideFadeTransition extends StatefulWidget {
///The child on which to apply the given [SlideFadeTransition]
final Widget child;
///The offset by which to slide and [child] into view from [Direction].
///Defaults to 1.0
final double offset;
///The curve used to animate the [child] into view.
///Defaults to [Curves.easeIn]
final Curve curve;
///The direction from which to animate the [child] into view. [Direction.horizontal]
///will make the child slide on x-axis by [offset] and [Direction.vertical] on y-axis.
///Defaults to [Direction.vertical]
final Direction direction;
///The delay with which to animate the [child]. Takes in a [Duration] and
/// defaults to 0.0 seconds
final Duration delayStart;
///The total duration in which the animation completes. Defaults to 800 milliseconds
final Duration animationDuration;
SlideFadeTransition({
#required this.child,
this.offset = 1.0,
this.curve = Curves.easeIn,
this.direction = Direction.vertical,
this.delayStart = const Duration(seconds: 0),
this.animationDuration = const Duration(milliseconds: 800),
});
#override
_SlideFadeTransitionState createState() => _SlideFadeTransitionState();
}
class _SlideFadeTransitionState extends State<SlideFadeTransition>
with SingleTickerProviderStateMixin {
Animation<Offset> _animationSlide;
AnimationController _animationController;
Animation<double> _animationFade;
#override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this,
duration: widget.animationDuration,
);
//configure the animation controller as per the direction
if (widget.direction == Direction.vertical) {
_animationSlide =
Tween<Offset>(begin: Offset(0, widget.offset), end: Offset(0, 0))
.animate(CurvedAnimation(
curve: widget.curve,
parent: _animationController,
));
} else {
_animationSlide =
Tween<Offset>(begin: Offset(widget.offset, 0), end: Offset(0, 0))
.animate(CurvedAnimation(
curve: widget.curve,
parent: _animationController,
));
}
_animationFade =
Tween<double>(begin: -1.0, end: 1.0).animate(CurvedAnimation(
curve: widget.curve,
parent: _animationController,
));
Timer(widget.delayStart, () {
_animationController.forward();
});
}
#override
void dispose() {
_animationController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return FadeTransition(
opacity: _animationFade,
child: SlideTransition(
position: _animationSlide,
child: widget.child,
),
);
}
}
I built a package to do this simply.
delayed_widget
Example:
DelayedWidget(
delayDuration: Duration(milliseconds: 200),// Not required
animationDuration: Duration(seconds: 1),// Not required
animation: DelayedAnimations.SLIDE_FROM_BOTTOM,// Not required
child: Container(
width: 200,
height: 200,
color: Colors.red,
))