Animating position across row cells in Flutter - flutter

I have a Row Widget that holds a child Widget in one of its cells (the others hold a Spacer()). Depending on the state, the cell which holds the child changes, resulting in a position change of the child Widget. I want to animate this motion to make it smooth. Is there a way of doing so with the standard animation Widgets (something like AnimatedPositioned, which won't work in this case)?

You could use AnimatedPositioned if you insert a Stack inside your row.
Otherwise, you can use an AnimatedBuilder with Transform.translate and animate the Offset on the axis X of your widget. Bellow there's a complete example, that you can run at DartPad to see the result. Hope it helps.
import 'package:flutter/material.dart';
void main(){
runApp(MaterialApp(home: MyAnimation()));
}
class MyAnimation extends StatefulWidget {
#override
_MyAnimationState createState() => _MyAnimationState();
}
class _MyAnimationState extends State<MyAnimation> with SingleTickerProviderStateMixin {
AnimationController _controller;
Animation<double> animation;
#override
void initState() {
super.initState();
_controller = AnimationController(vsync: this, duration: Duration(milliseconds: 5000));
animation = Tween(begin: 0.0, end: 300.0).animate(_controller);
_controller.forward();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Title")),
body: AnimatedBuilder(
animation: _controller,
builder: (context, child) {
return Row(
children: <Widget>[
Transform.translate(
child: Container(width: 100, height: 100, color: Colors.black),
offset: Offset(animation.value, 0),
),
Container(width: 100, height: 100, color: Colors.transparent),
Container(width: 100, height: 100, color: Colors.transparent),]
);
},
),
);
}
}

Related

Flutter Float and Fade In Animation for Widgets in Column with Loading for the First Time

I am trying to create animated screens in which for example, when the screen is loaded, each one would contain a column with widgets. What I would like to do is, when the screen is loaded, have each widget in the column float in from off screen (from bottom) in the order in which they appear.
I have been searching but cannot seem to find a solution. How may I achieve this?
I've done something that might be what you want. However, to make it so each element animates after the last one for itself and not all together I had to use the ListView.builder to get the index. You can use the Column widget if you can find out the index of the element.
Here is the code:
The home screen:
import 'package:flutter/material.dart';
import 'package:testing/fade_in_from_bottom.dart';
void main() => runApp(App());
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomePage(),
);
}
}
class HomePage extends StatelessWidget {
final List<Widget> children = <Widget>[
Container(
height: 32.0,
color: Colors.amber,
),
Container(
height: 32.0,
color: Colors.black,
),
Container(
height: 32.0,
color: Colors.purple,
),
Container(
height: 32.0,
color: Colors.green,
),
Container(
height: 32.0,
color: Colors.indigo,
),
];
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: ListView.builder(
itemCount: children.length,
itemBuilder: (BuildContext context, int index) {
return FadeInFromBottom(
key: UniqueKey(), // this is very important
index: index,
child: children[index],
);
},
),
),
],
),
);
}
}
Here is the FadeInFromBottom widget I made:
import 'package:flutter/material.dart';
class FadeInFromBottom extends StatefulWidget {
#override
_FadeInFromBottomState createState() => _FadeInFromBottomState();
final Key key;
final Duration animationDuration;
final Duration offsetDuration;
final Widget child;
final int index;
FadeInFromBottom({
#required this.key,
#required this.child,
#required this.index,
this.animationDuration = const Duration(milliseconds: 400),
this.offsetDuration = const Duration(milliseconds: 800),
}) : super(key: key); // this line is important
}
// How to add AutomaticKeepAliveClientMixin? Follow steps 1, 2 and 3:
// 1. add AutomaticKeepAliveClientMixin to FadeInFromBottom widget State
class _FadeInFromBottomState extends State<FadeInFromBottom>
with TickerProviderStateMixin, AutomaticKeepAliveClientMixin {
bool get wantKeepAlive => true; // 2. add this line
double progress = 0.0;
Animation<double> animation;
AnimationController controller;
#override
void initState() {
super.initState();
final Duration offsetDuration =
widget.offsetDuration == null ? 0.0 : widget.offsetDuration;
final int index = widget.index == null ? 0 : widget.index;
// we await the future to create the animation delay
Future.delayed(offsetDuration * index).then(
(_) {
controller = AnimationController(
duration: widget.animationDuration, vsync: this);
animation = Tween<double>(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: controller,
curve: Curves.linear,
),
)..addListener(() {
setState(() => progress = animation.value);
});
controller.forward();
},
);
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
super.build(context); // 3. add this line
return Opacity(
opacity: progress,
child: Transform.translate(
offset: Offset(
0.0,
(1.0 - progress) * 999.0,
),
child: widget.child,
),
);
}
}
Don't forget to add the key: UniqueKey() property, without it the animations will be messed up.

Flutter add animation small to big

Need to know how can i add some animation on my splash screen. I just need to add when app open the center image will show small to large.
My code
class _MyHomePageState extends State<MyHomePage> {
#override
void initState() {
super.initState();
Timer(
Duration(seconds: 3),
() => Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (BuildContext context) => HomeScreen())));
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage("assets/bg#3x.png"),
fit: BoxFit.cover,
),
),
child: Center(
child: Image.asset("assets/logo#2x.png"),
) /* add child content here */,
),
);
}
}
You can use Tween AnimationController with AnimatedBuilder to for this purpose. Here's an example you just need to replace Container with your Image or you can also wrap the Image with a Container.
Since, you want the image to increase the size at splash screen so use forward() property of AnimationController in initState.
Then replace the height and width property of your image with _animationSize.value. Adjust the size according to your need here:
Size:
_tweenSize = Tween(begin: 50, end: 200);
Duration:
_animationController = AnimationController(vsync: this, duration: Duration(milliseconds: 400));
Replace:
AnimatedBuilder(
animation: _animationSize,
builder: (context, child) {
// Put your image here and replace height, width of image with _animationSize.value
return Container(
color: Colors.red,
height: _animationSize.value,
width: _animationSize.value,
);
}),
class HomeScreen extends StatefulWidget {
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen>
with SingleTickerProviderStateMixin {
Tween<double> _tweenSize;
Animation<double> _animationSize;
AnimationController _animationController;
#override
void initState() {
_animationController =
AnimationController(vsync: this, duration: Duration(milliseconds: 400));
_tweenSize = Tween(begin: 50, end: 200);
_animationSize = _tweenSize.animate(_animationController);
_animationController.forward();
super.initState();
}
#override
void dispose() {
_animationController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: AnimatedBuilder(
animation: _animationSize,
builder: (context, child) {
// Put your image here and replace height, width of image with _animationSize.value
return Container(
color: Colors.red,
height: _animationSize.value,
width: _animationSize.value,
);
}),
),
);
}
}

Animate button rotation

I'm trying to animate rotation of icon inside custom button to 180 degree on each press. I'm bit confused from the flutter documentation. I know I should use RotationTransition but I just can not find out how.. Here is my button
SizedBox.fromSize(
size: Size(50, 50),
child: ClipOval(
child: Material(
color: Colors.blue,
child: InkWell(
onTap: () {},
splashColor: Colors.black12,
child: RotationTransition(. //<== that is where I get
child: Icon(
Icons.filter_list, //rotate this icon
color: Colors.white,
),
),
),
),
),
),
Basically, you need a Stateful widget with an AnimationController which creates controls the animation. It's important to create that controller on the initState() method, and dispose it on the dispose() method to prevent possible errors.
Once you have the controller, you can use a bool to store the rotation direction and an Animation controlled by the Animation controller to rotate the widget. First, it goes from 0 to 0.5, and then from 0.5 to 1.
The code can be like this. You can also take a look at this codepen I created for the example.
import 'package:flutter/material.dart';
void main() {
runApp(
MaterialApp(
home: MyWidget(),
),
);
}
class MyWidget extends StatefulWidget{
#override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget>
with SingleTickerProviderStateMixin{
AnimationController _controller;
Animation<double> _animation;
bool dir = true;
#override
void initState(){
_controller = AnimationController(vsync: this, duration: Duration(seconds: 1));
super.initState();
}
#override
void dispose(){
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
_animation = Tween<double>(
begin: dir ? 0:0.5,
end: dir ? 0.5:1
).animate(_controller);
return Scaffold(
body: Center(
child: RotationTransition(
turns: _animation,
child: RaisedButton(
child: Text("Rotate"),
onPressed: (){
_controller.forward(
from: 0
);
setState(() {dir = !dir;});
}
)
)
),
);
}
}

Flutter, How to make a container disappear after som time?

So I have written this code which lets an animation play where i pressed. So if i press the screen there is a short animation playing where i pressed, if you press multiple times there are multiple animations on the screen for a short period. Each time I press the screen there is a container taking up place where the animation is played, but this container keeps taking up space even when the animation is not playing. So if i press on the screen where there already is a container an animation won't be played.
How do I make the container disappear after some time so that I can press so many times I want on the screen and still have an animation to be played?
This is all the code responsible for that animation:
class HomePage extends StatefulWidget{
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage>
with SingleTickerProviderStateMixin {
final tappedPositions = <Offset>[];
AnimationController _animationController;
#override
void initState() {
_animationController = AnimationController(
vsync: this,
duration: const Duration(seconds: 2),
);
super.initState();
}
new GestureDetector(
onTapUp: (tabDetails) {
setState(() {
tappedPositions.add(tabDetails.localPosition);
});
},
child: Container(
color: Colors.transparent,
),
),
for (final position in tappedPositions)
Positioned(
top: position.dy,
left: position.dx,
child: MyAnimatedWidget(
animation: _animationController,
),
),
],
);
}
}
class MyAnimatedWidget extends StatelessWidget {
final Animation animation;
const MyAnimatedWidget({Key key, this.animation}) : super(key: key);
#override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: animation,
child: Container(
height: 80.0,
width: 80.0,
child: new FlareActor(
"assets/images/tryckplats.flr",
animation: "tryck",
),
),
builder: (context, child) {
return Transform(
alignment: Alignment.center,
child: child,
);
},
);
}
}
Instead of using a for loop and building a widget per offset in tappedPositions, use a function that inserts a new widget with offset into a list in state, then map the list of children in a stack. Now the tapped animation can be played over another, and you can delete by key from the list after the timer expires.
class _HomePageState extends State<HomePage>
with SingleTickerProviderStateMixin {
final _animatedBoxes = <Widget>[];
AnimationController _animationController;
#override
void initState() {
_animationController = AnimationController(
vsync: this,
duration: const Duration(seconds: 2),
);
super.initState();
}
Widget _buildAnimatedBox(Offset position) {
setState({
animatedBoxes.add(
Positioned(
key: Key(animatedBoxes.length)
top: position.dy,
left: position.dx,
child: MyAnimatedWidget(
animation: _animationController,
),
}
);
}
Widget build(BuildContext context)
return GestureDetector(
onTapUp: (tabDetails) => _buildAnimatedBox(tabDetails)
child: Stack(
children: _animatedBoxes
),
);
}
}

What is the best way to make an orbiting button

I would like to make something like this:
https://youtu.be/W3O0077GMlo
And I would like for the rotating circle (moon in this video) to act as a button.
What is the best way to do this performance wise?
You can use the RotationTransition inside a Stack widget to create the rotating animation. Inside the Stackset the alignment to center, and wrap your rotating widget inside an Align. Set the alignment attribute of the Align widget to Alignment.topCenter or any outer alignment.
Remember to deploy on release to your phone to make sure the animations are running smooth.
Quick standalone code example:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: SizedBox(width: 300.0, height: 300.0, child: OrbitingButton()),
),
),
);
}
}
class OrbitingButton extends StatefulWidget {
#override
_OrbitingButtonState createState() => _OrbitingButtonState();
}
class _OrbitingButtonState extends State<OrbitingButton>
with SingleTickerProviderStateMixin {
AnimationController controller;
#override
void initState() {
super.initState();
controller = AnimationController(vsync: this);
controller.repeat(min: 0.0, max: 1.0, period: Duration(seconds: 1));
}
#override
Widget build(BuildContext context) {
return Stack(
alignment: Alignment.center,
children: <Widget>[
RotationTransition(
turns: controller,
child: Align(
alignment: Alignment.topCenter,
child: Container(
color: Colors.green,
height: 30.0,
width: 30.0,
),
),
),
RaisedButton(
child: Text('Button'),
)
],
);
}
}