I am trying to animate an image (or logo) from center of the screen to top of the screen.
For that, I had a look at SlideTransition. It works, but for portrait orientation only. When the device is rotated, the image goes outside of the screen. (maybe I did not understand the Offset property correctly!) If I make it work for horizontal, the output of the portrait gets changed.
Let me show you the output:
Portrait:
Before animation
After animation (As wanted)
Horizontal:
Before animation
After animation (Problem HERE)
So, as you can see, in horizontal mode, the Logo goes out of the screen.
Here is the code:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'SlideTransition Demo',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
AnimationController _controller;
Animation<Offset> _offsetAnimation;
#override
void initState() {
_controller = AnimationController(vsync: this, duration: Duration(milliseconds: 300));
_offsetAnimation = _controller
.drive(CurveTween(curve: Curves.easeInOut))
.drive(Tween<Offset>(begin: Offset(0.0, 0.0), end: Offset(0.0, -1.0)));
super.initState();
}
#override
void dispose() {
_controller?.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('SlideTransition Demo'),
),
body: Stack(
children: <Widget>[
Align(
alignment: Alignment.center,
child: SlideTransition(
position: _offsetAnimation,
child: FlutterLogo(
size: 200,
),
),
),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () {
if (_controller.status == AnimationStatus.completed) {
_controller.reverse();
} else if (_controller.status == AnimationStatus.dismissed) {
_controller.forward();
}
},
child: Icon(Icons.play_arrow),
),
);
}
}
The SlideTransition does the fractional translation. And this translation depends on the size of the child of the SlideTransition widget.
Tween(begin: Offset(0.0, 0.0), end: Offset(0.0, -1.0))
This tween translates(moves) the FlutterLogo in y-axis for 200(height of the FlutterLogo widget)
Tween(begin: Offset(0.0, 0.0), end: Offset(0.0, -0.5))
This tween translates(moves) the FlutterLogo in y-axis for 100(half-height of the FlutterLogo widget)
If you just want to animate the image from center to topCenter, then you can use AlignTransition.
Example:
class FirstPage extends StatefulWidget {
#override
FirstPageState createState() => FirstPageState();
}
class FirstPageState extends State<FirstPage> with TickerProviderStateMixin {
AnimationController _animationController;
AlignmentGeometryTween _tween;
#override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this,
duration: Duration(seconds: 3),
);
_tween = AlignmentGeometryTween(
begin: Alignment.center,
end: Alignment.topCenter,
);
}
TickerFuture _play() {
_animationController.reset();
return _animationController.animateTo(
1.0,
curve: Curves.easeInOut,
);
}
#override
void dispose() {
_animationController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(title: Text("Demo")),
body: AlignTransition(
alignment: _tween.animate(_animationController),
child: FlutterLogo(
size: 200,
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.play_arrow),
onPressed: _play,
),
),
);
}
}
Demo: DartPad
You can copy paste run full code below
You can use OrientationBuilder and modify Offset to what you need
code snippet
body: OrientationBuilder(
builder: (context, orientation) {
if (orientation == Orientation.portrait) {
_offsetAnimation = _controller
.drive(CurveTween(curve: Curves.easeInOut))
.drive(Tween<Offset>(
begin: Offset(0.0, 0.0), end: Offset(0.0, -1.0)));
} else {
_offsetAnimation = _controller
.drive(CurveTween(curve: Curves.easeInOut))
.drive(Tween<Offset>(
begin: Offset(0.0, 0.0), end: Offset(-1.0, 0.0)));
}
working demo
full code
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'SlideTransition Demo',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage>
with SingleTickerProviderStateMixin {
AnimationController _controller;
Animation<Offset> _offsetAnimation;
Animation<Offset> _offsetAnimationLandscape;
#override
void initState() {
_controller =
AnimationController(vsync: this, duration: Duration(milliseconds: 300));
super.initState();
}
#override
void dispose() {
_controller?.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('SlideTransition Demo'),
),
body: OrientationBuilder(
builder: (context, orientation) {
if (orientation == Orientation.portrait) {
_offsetAnimation = _controller
.drive(CurveTween(curve: Curves.easeInOut))
.drive(Tween<Offset>(
begin: Offset(0.0, 0.0), end: Offset(0.0, -1.0)));
} else {
_offsetAnimation = _controller
.drive(CurveTween(curve: Curves.easeInOut))
.drive(Tween<Offset>(
begin: Offset(0.0, 0.0), end: Offset(-1.0, 0.0)));
}
return Stack(
children: <Widget>[
Align(
alignment: Alignment.center,
child: SlideTransition(
position: _offsetAnimation,
child: FlutterLogo(
size: 200,
),
),
),
],
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: () {
if (_controller.status == AnimationStatus.completed) {
_controller.reverse();
} else if (_controller.status == AnimationStatus.dismissed) {
_controller.forward();
}
},
child: Icon(Icons.play_arrow),
),
);
}
}
Related
I need to add widgets using slide animation into tree on an action done on onpressed
the parent widget will be a listview in which the childrens will be added in slide down animation
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Home(),
);
}
}
class Home extends StatefulWidget {
#override
State<StatefulWidget> createState() => HomeState();
}
class HomeState extends State<Home> with SingleTickerProviderStateMixin {
AnimationController controller;
Animation<Offset> offset;
#override
void initState() {
super.initState();
controller =
AnimationController(vsync: this, duration: Duration(seconds: 1));
offset = Tween<Offset>(begin: Offset.zero, end: Offset(0.0, 1.0))
.animate(controller);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
Center(
child: RaisedButton(
child: Text('Show / Hide'),
onPressed: () {
switch (controller.status) {
case AnimationStatus.completed:
controller.reverse();
break;
case AnimationStatus.dismissed:
controller.forward();
break;
default:
}
},
),
),
Align(
alignment: Alignment.bottomCenter,
child: SlideTransition(
position: offset,
child: Padding(
padding: EdgeInsets.all(50.0),
child: CircularProgressIndicator(),
),
),
)
],
),
);
}
}
I have an ExpansionTile that have different titles in expanded\collapsed state.
class _ExpandablePaneState extends State<ExpandablePane>
with SingleTickerProviderStateMixin {
bool isExpanded = false;
AnimationController _controller;
Animation<double> _iconTurns;
static final Animatable<double> _easeInTween =
CurveTween(curve: Curves.easeIn);
static final Animatable<double> _halfTween =
Tween<double>(begin: 0.0, end: 0.5);
Duration _kExpand = Duration(milliseconds: 250);
Widget _myAnimatedWidget;
#override
void initState() {
super.initState();
_controller = AnimationController(duration: _kExpand, vsync: this);
_iconTurns = _controller.drive(_halfTween.chain(_easeInTween));
_controller.value = 0.0;
_myAnimatedWidget = widget.collapsedTitle;
}
#override
Widget build(BuildContext context) {
return Theme(
data: Theme.of(context).copyWith(dividerColor: Colors.transparent),
child: ExpansionTile(
onExpansionChanged: (value) {
if (value) {
_controller.forward();
} else {
_controller.reverse();
}
setState(() {
isExpanded = value;
_myAnimatedWidget =
isExpanded ? widget.expandedTitle : widget.collapsedTitle;
});
},
title: Expanded(
child: Stack(children: [
AnimatedSwitcher(
duration: Duration(milliseconds: 2500),
transitionBuilder: (child, animation) => ScaleTransition(
child: child,
scale: animation,
),
child: _myAnimatedWidget,
),
Positioned.fill(
child: Align(
alignment: Alignment.centerRight,
child: RotationTransition(
turns: _iconTurns,
child: const Icon(Icons.expand_more),
),
),
)
]),
),
children: widget.content,
),
);
}
}
I want to make an animation between these states, how I can achieve it?
I tried AnimatedSwitcher, but it didn't work. I'm totally don't see an animation.
You can copy paste run full code below
You can wrap _myAnimatedWidget with Container and provide key: ValueKey<bool>(isExpanded)
From official example https://api.flutter.dev/flutter/widgets/AnimatedSwitcher-class.html
This key causes the AnimatedSwitcher to interpret this as a "new"
child each time the count changes, so that it will begin its animation
when the count changes.
I also remove Expanded in title
code snippet
child: Container(
key: ValueKey<bool>(isExpanded), child: _myAnimatedWidget),
working demo
full code
import 'package:flutter/material.dart';
class ExpandablePane extends StatefulWidget {
Widget expandedTitle;
Widget collapsedTitle;
List<Widget> content;
ExpandablePane({this.expandedTitle, this.collapsedTitle, this.content});
#override
_ExpandablePaneState createState() => _ExpandablePaneState();
}
class _ExpandablePaneState extends State<ExpandablePane>
with SingleTickerProviderStateMixin {
bool isExpanded = false;
AnimationController _controller;
Animation<double> _iconTurns;
static final Animatable<double> _easeInTween =
CurveTween(curve: Curves.easeIn);
static final Animatable<double> _halfTween =
Tween<double>(begin: 0.0, end: 0.5);
Duration _kExpand = Duration(milliseconds: 250);
Widget _myAnimatedWidget;
#override
void initState() {
super.initState();
_controller = AnimationController(duration: _kExpand, vsync: this);
_iconTurns = _controller.drive(_halfTween.chain(_easeInTween));
_controller.value = 0.0;
_myAnimatedWidget = widget.collapsedTitle;
}
#override
Widget build(BuildContext context) {
return Theme(
data: Theme.of(context).copyWith(dividerColor: Colors.transparent),
child: ExpansionTile(
onExpansionChanged: (value) {
if (value) {
_controller.forward();
} else {
_controller.reverse();
}
setState(() {
isExpanded = value;
_myAnimatedWidget =
isExpanded ? widget.expandedTitle : widget.collapsedTitle;
});
},
title: Stack(children: [
AnimatedSwitcher(
duration: Duration(milliseconds: 2500),
transitionBuilder: (child, animation) => ScaleTransition(
child: child,
scale: animation,
),
child: Container(
key: ValueKey<bool>(isExpanded), child: _myAnimatedWidget),
),
Positioned.fill(
child: Align(
alignment: Alignment.centerRight,
child: RotationTransition(
turns: _iconTurns,
child: const Icon(Icons.expand_more),
),
),
)
]),
children: widget.content,
),
);
}
}
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(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
ExpandablePane(
expandedTitle: Text("expand"),
collapsedTitle: Text("collapsed"),
content: [Text("1"), Text("2"), Text("3")],
),
],
),
),
);
}
}
I'm trying to animate an alert dialog in flutter so that when it pop ups it shows an animation like this below.
How can I achieve following look and behaviour from Pokemon Go in an alertDialog?
I would really like to have this animation in my app.
Thanks for your Answers!
Try this, modify any variable to meet your requirement:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
AnimationController _controller;
Animation<Offset> _animation;
double _width = 20;
double _height = 200;
Color _color = Colors.transparent;
#override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 1),
vsync: this,
);
_animation = Tween<Offset>(
begin: const Offset(0.0, 1.0),
end: const Offset(0.0, -2.0),
).animate(CurvedAnimation(
parent: _controller,
curve: Curves.easeInCubic,
));
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
appBar: AppBar(
actions: [
IconButton(
icon: Icon(Icons.send),
onPressed: () {
setState(() {
_color = Colors.white;
});
_controller.forward().then((_) {
_width = 200;
setState(() {});
});
},
),
],
),
body: Stack(
children: [
Align(
alignment: Alignment.bottomCenter,
child: SlideTransition(
position: _animation,
child: AnimatedContainer(
width: _width,
height: _height,
decoration: BoxDecoration(
color: _color,
borderRadius: BorderRadius.circular(10),
),
duration: Duration(seconds: 1),
curve: Curves.fastOutSlowIn,
),
),
),
],
),
);
}
}
I have this code which uses an AnimationController and Tween to rotate a container along Z-axis when Start/Stop Button is pressed. The problem is it forwards the animation only first time after that no matter how many times I press the button it does nothing and the container remains stationary .
curvesanimation.dart which has all the animation logic
import 'package:flutter/material.dart';
import 'dart:math' as math;
class CurvedAnimationExample extends StatefulWidget {
CurvedAnimationExample({Key key}) : super(key: key);
#override
CurvedAnimationExampleState createState() => CurvedAnimationExampleState();
}
class CurvedAnimationExampleState extends State<CurvedAnimationExample>
with SingleTickerProviderStateMixin {
Animation<double> _animation;
AnimationController _animationController;
#override
void initState() {
super.initState();
_animationController =
AnimationController(vsync: this, duration: Duration(seconds: 5))
..addListener(() {
setState(() {});
});
final Animation curve =
CurvedAnimation(parent: _animationController, curve: Curves.easeIn);
_animation = Tween<double>(begin: 0, end: math.pi * 2).animate(curve);
}
void startStopAnimation() {
print(_animationController.isAnimating.toString());
if (_animationController.isAnimating)
_animationController.stop();
else {
_animationController.forward();
}
}
#override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Transform(
alignment: Alignment.center,
transform: Matrix4.identity()..rotateZ(_animation.value),
child: Container(
color: Colors.pinkAccent,
width: 200,
height: 200,
),
),
RaisedButton(
child: const Text("Start/Stop Animation"),
elevation: 15,
color: Colors.blueGrey,
onPressed: () => startStopAnimation(),
)
],
));
}
#override
void dispose() {
_animationController.dispose();
print("Curved Animation Example : dispose is called");
super.dispose();
}
}
main.dart file
import 'package:flutter/material.dart';
import 'curvesanimations.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomeScaffold(
title: "Curved Animation Example",
),
);
}
}
class MyHomeScaffold extends StatelessWidget {
final String title;
MyHomeScaffold({this.title});
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: AppBar(
title: Text(title),
),
body: CurvedAnimationExample(),
),
);
}
}
This is because your animation has already reached its endpoint. You can't keep going forward once the end point is reached. An easy fix is to check if the animation is at the endpoint already and reset it to the beginning if it is:
void startStopAnimation() {
print(_animationController.isAnimating.toString());
if (_animationController.isAnimating)
_animationController.stop();
else {
print("forward");
if(_animationController.isCompleted) {//Check if animation is at endpoint
_animationController.value = 0;
}
_animationController.forward();
}
}
in this sample code i want to make container with for example 100.0 size of height and move that into screen by animation to show that, like with simple bottom sheet, this sample code work but its not what i want to have
problem is moving that into screen with size of container witch that
is 100.0 on our sample
start animation from bottom height of container into screen
for example:
in this sample code animate move container to top of screen not size of that
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Page(),
);
}
}
class Page extends StatefulWidget {
#override
State<StatefulWidget> createState() => _PageState();
}
class _PageState extends State<Page> with SingleTickerProviderStateMixin {
Tween<Offset> tween = Tween<Offset>(
begin: Offset(0.0, 10000.0),
end: Offset(0.0, 0.0),
);
Animation<Offset> animation;
AnimationController animationController;
GlobalKey _widgetKey = GlobalKey();
#override
void initState() {
super.initState();
animationController = AnimationController(
vsync: this,
duration: Duration(seconds: 2),
);
animation = tween.animate(animationController);
Future<void>.delayed(Duration(seconds: 1), () {
final Size screenSize = MediaQuery.of(context).size;
final RenderBox widgetRenderBox =
_widgetKey.currentContext.findRenderObject();
final Size widgetSize = widgetRenderBox.size;
final double offset = (screenSize.height / 2 / widgetSize.height).ceilToDouble();
tween = Tween<Offset>(
begin: Offset(0.0, offset),
end: Offset(0.0, 0.0),
);
animation = tween.animate(animationController);
this.setState((){
animationController.forward();
});
});
}
#override
void dispose() {
animationController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('ddddddd'),),
body: Stack(
alignment: Alignment.center,
fit: StackFit.loose,
children: <Widget>[
SlideTransition(
position: animation,
child: Container(
key: _widgetKey,
height:100.0,
width: 300.0,
color:Colors.indigo,
child:Center(
child:Text('ddddddddddddd'),
)
)
),
],
),
);
}
}
I hope I did not misunderstood your question.
The Stack should have the size of the whole screen. Wrap it with SizedBox for example.
No need to center the text. You should rather align it inside the Container itself.
The container should have the width of the screen.
The height of the container is not necessary and therefore we don't need to figure it out.
This works for me:
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: Page(),
);
}
}
class Page extends StatefulWidget {
const Page({Key? key}) : super(key: key);
#override
State<StatefulWidget> createState() => _PageState();
}
class _PageState extends State<Page> with SingleTickerProviderStateMixin {
late final AnimationController _animationController =
AnimationController(vsync: this, duration: const Duration(seconds: 2));
late final Animation<Offset> _animation =
Tween<Offset>(begin: const Offset(0, 1), end: const Offset(0, 0))
.animate(_animationController);
#override
void initState() {
super.initState();
delayedStart();
}
void delayedStart() async {
await Future.delayed(const Duration(seconds: 2), () {
_animationController.forward();
});
}
#override
void dispose() {
_animationController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('ddddddd'),
),
body: SizedBox(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Stack(
alignment: Alignment.bottomCenter,
fit: StackFit.loose,
children: <Widget>[
SlideTransition(
position: _animation,
child: Container(
alignment: Alignment.center,
height: 100,
width: MediaQuery.of(context).size.width,
color: Colors.indigo,
child: const Text('ddddddddddddd')
)
),
],
),
),
);
}
}