I am trying to achieve a pretty simple animation in Flutter but I am stuck.
The current animation looks like this (iPhone Screen recording): https://drive.google.com/file/d/10pzUadQrSr85eLLpyh7v3lt-x8XjFNec/view?usp=sharing
I am currently doing this with Positioned but I can not do it precisely as I want. Is there any chance that I can do this animation with Align Widget.
I have tried using AnimatedAlign and Tweens but none of them allow me to modify the Images position by scrolling up or down on the bottom sheet.
The Code:
late AnimationController bottomSheetController;
double bsOffset = 1;
#override
void initState() {
super.initState();
bottomSheetController = BottomSheet.createAnimationController(this);
bottomSheetController.duration = Duration(milliseconds: 200);
bottomSheetController.addListener(() {
double size = (bottomSheetController.value.toDouble() - 1) * -1;
setState(() {
if (size != 0) {
bsOffset = size;
}
});
});
}
.
.
.
void _handleFABPressed() {
showModalBottomSheet(
backgroundColor: Colors.transparent,
barrierColor: Colors.transparent,
transitionAnimationController: bottomSheetController,
context: context,
builder: (context) {
return Popover(
child: Container(
height: 400,
// color: Colors.lightBlue,
),
);
},
);
}
.
.
.
child: Stack(
alignment: Alignment.topCenter,
children: [
Positioned(
bottom: 50 - (50 * bsOffset),
left: ((200 / 2) - 20) - (((200 / 2) - 20) * (bsOffset - 1) * -1),
child: Image.asset(
'assets/images/8.png',
width: 200,
),
),
],
),
Thank You!
If I were to move the two widgets, both Align and the bottomsheet, I would do it like this:
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
late AnimationController _animationController;
late Animation<Offset> _animationOffset;
var _alignment = Alignment.bottomCenter;
void _show() {
showModalBottomSheet(
backgroundColor: Colors.transparent,
barrierColor: Colors.transparent,
transitionAnimationController: _animationController,
context: context,
builder: (context) {
return Container(
height: 400,
color: Colors.red,
);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Flutter Demo Home Page'),
),
body: Stack(
children: [
Align(
alignment: _alignment,
child: const SizedBox(
width: 100, height: 100, child: Card(color: Colors.red)))
],
),
floatingActionButton:
FloatingActionButton(onPressed: _show, child: const Icon(Icons.add)),
);
}
void _onListenerAnimation() {
setState(() {
_alignment =
Alignment(_animationOffset.value.dx, _animationOffset.value.dy);
});
}
#override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this, duration: const Duration(milliseconds: 200))
..addListener(_onListenerAnimation);
_animationOffset = Tween<Offset>(
begin: const Offset(0, 1),
end: const Offset(-1, -0.6),
).animate(_animationController);
}
#override
void dispose() {
super.dispose();
_animationController
..removeListener(_onListenerAnimation)
..dispose();
}
}
Related
I have been studing and trying to do some projects and stack in this. So, I created CustomScrollView and the SliverAppBar has to be either fully opened or fully closed. I tried to do but have problems with animation scrolling.
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 MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(
backgroundColor: Colors.deepPurple,
),
body: const MyHomePage(),
),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final _scrollController = ScrollController();
bool isClosed = false;
#override
void initState() {
super.initState();
_scrollController.addListener(() {
if (_scrollController.offset >= 5 && _scrollController.offset <= 200) {
if (isClosed) {
_scrollController.jumpTo(0);
close();
} else {
_scrollController.jumpTo(202);
close();
}
}
});
}
close() {
setState(() {
isClosed = !isClosed;
});
}
#override
Widget build(BuildContext context) {
return CustomScrollView(
controller: _scrollController,
slivers: [
SliverAppBar(
expandedHeight: 200,
flexibleSpace: SizedBox(
child: Container(
height: 200,
decoration: const BoxDecoration(
image: DecorationImage(
image: NetworkImage(
"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcS5-OgasVpm-kc2HaOUloxKVlLzLuM6Q53mfA&usqp=CAU"),
fit: BoxFit.cover)),
),
),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Container(
color: Color(index),
height: 100,
width: MediaQuery.of(context).size.width,
child: Text("$index"),
);
},
childCount: 10,
))
],
);
}
}
It works when jumpTo is used but has problems when animateTo is used. Thank You!
You need to wrap .animateTo() inside a Future delay.
The below code worked for me
Future.delayed(
Duration(milliseconds: 600),
() {
_scrollController.animateTo(
202,
duration: Duration(
milliseconds: 600),
curve: Curves.ease);
});
Or you can also wrap inside PostFrameCallback
WidgetsBinding.instance.addPostFrameCallback((_) {
_scrollController.animateTo(
202,
duration: Duration(
milliseconds: 600),
curve: Curves.ease);
}
Hero animation is the best for navigating between screen, but I need same animation between widgets. Like one card moving another place for example: Product Card moves to shoppingcart and something else. Thanks for answers!
Try this one, add_to_cart_animation:
import 'package:add_to_cart_animation/add_to_cart_animation.dart';
import 'package:add_to_cart_animation/add_to_cart_icon.dart';
import 'package:flutter/material.dart';
import 'list_item.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Add To Cart Animation',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Add To Cart Animation'),
debugShowCheckedModeBanner: false,
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
// We can detech the location of the card by this GlobalKey<CartIconKey>
GlobalKey<CartIconKey> gkCart = GlobalKey<CartIconKey>();
late Function(GlobalKey) runAddToCardAnimation;
var _cartQuantityItems = 0;
#override
Widget build(BuildContext context) {
return AddToCartAnimation(
// To send the library the location of the Cart icon
gkCart: gkCart,
rotation: true,
dragToCardCurve: Curves.easeIn,
dragToCardDuration: const Duration(milliseconds: 1000),
previewCurve: Curves.linearToEaseOut,
previewDuration: const Duration(milliseconds: 500),
previewHeight: 30,
previewWidth: 30,
opacity: 0.85,
initiaJump: false,
receiveCreateAddToCardAnimationMethod: (addToCardAnimationMethod) {
// You can run the animation by addToCardAnimationMethod, just pass trough the the global key of the image as parameter
this.runAddToCardAnimation = addToCardAnimationMethod;
},
child: Scaffold(
appBar: AppBar(
title: Text(widget.title),
centerTitle: false,
actions: [
// Improvement/Suggestion 4.4 -> Adding 'clear-cart-button'
IconButton(
icon: Icon(Icons.cleaning_services),
onPressed: () {
_cartQuantityItems = 0;
gkCart.currentState!.runClearCartAnimation();
},
),
SizedBox(width: 16),
AddToCartIcon(
key: gkCart,
icon: Icon(Icons.shopping_cart),
colorBadge: Colors.red,
),
SizedBox(
width: 16,
)
],
),
body: ListView(
children: [
AppListItem(onClick: listClick, index: 1),
AppListItem(onClick: listClick, index: 2),
AppListItem(onClick: listClick, index: 3),
AppListItem(onClick: listClick, index: 4),
AppListItem(onClick: listClick, index: 5),
AppListItem(onClick: listClick, index: 6),
AppListItem(onClick: listClick, index: 7),
],
),
),
);
}
// Improvement/Suggestion 4.4 -> Running AddTOCartAnimation BEFORE runCArtAnimation
void listClick(GlobalKey gkImageContainer) async {
await runAddToCardAnimation(gkImageContainer);
await gkCart.currentState!.runCartAnimation((++_cartQuantityItems).toString());
}
}
OR
[not null safety]
this is a sample of add to cart, add_cart_parabola:
import 'dart:ui';
import 'package:add_cart_parabola/add_cart_parabola.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> {
int _counter = 0;
GlobalKey floatKey = GlobalKey();
GlobalKey rootKey = GlobalKey();
Offset floatOffset ;
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_){
RenderBox renderBox = floatKey.currentContext.findRenderObject();
floatOffset = renderBox.localToGlobal(Offset.zero);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Container(
key: rootKey,
width: double.infinity,
height: double.infinity,
color: Colors.grey,
child: ListView(
children: List.generate(40, (index){
return generateItem(index);
}).toList(),
),
),
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.yellow,
key: floatKey,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
Widget generateItem(int index){
Text text = Text("item $index",style: TextStyle(fontSize:
25),);
Offset temp;
return GestureDetector(
onPanDown: (details){
temp = new Offset(details.globalPosition.dx, details.globalPosition
.dy);
},
onTap: (){
Function callback ;
setState(() {
OverlayEntry entry = OverlayEntry(
builder: (ctx){
return ParabolaAnimateWidget(rootKey,temp,floatOffset,
Icon(Icons.cancel,color: Colors.greenAccent,),callback,);
}
);
callback = (status){
if(status == AnimationStatus.completed){
entry?.remove();
}
};
Overlay.of(rootKey.currentContext).insert(entry);
});
},
child: Container(
color: Colors.orange,
child: text,
),
);
}
}
For animating widget in the same screen you can use AnimatedPositioned widget see the below code
import 'dart:math';
import 'package:flutter/material.dart';
class AnimatedPositionedDemo extends StatefulWidget {
const AnimatedPositionedDemo({Key? key}) : super(key: key);
static String routeName = 'animated_positioned';
#override
_AnimatedPositionedDemoState createState() => _AnimatedPositionedDemoState();
}
class _AnimatedPositionedDemoState extends State<AnimatedPositionedDemo> {
late double topPosition;
late double leftPosition;
double generateTopPosition(double top) => Random().nextDouble() * top;
double generateLeftPosition(double left) => Random().nextDouble() * left;
#override
void initState() {
super.initState();
topPosition = generateTopPosition(30);
leftPosition = generateLeftPosition(30);
}
void changePosition(double top, double left) {
setState(() {
topPosition = generateTopPosition(top);
leftPosition = generateLeftPosition(left);
});
}
#override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
final appBar = AppBar(title: const Text('AnimatedPositioned'));
final topPadding = MediaQuery.of(context).padding.top;
// AnimatedPositioned animates changes to a widget's position within a Stack
return Scaffold(
appBar: appBar,
body: SizedBox(
height: size.height,
width: size.width,
child: Stack(
children: [
AnimatedPositioned(
top: topPosition,
left: leftPosition,
duration: const Duration(seconds: 1),
child: InkWell(
onTap: () => changePosition(
size.height -
(appBar.preferredSize.height + topPadding + 50),
size.width - 150),
child: Container(
alignment: Alignment.center,
width: 150,
height: 50,
child: Text(
'Click Me',
style: TextStyle(
color:
Theme.of(context).buttonTheme.colorScheme!.onPrimary,
),
),
color: Theme.of(context).primaryColor,
),
),
),
],
),
),
);
}
}
I hope it works for you
For Animated widgets, flutter team has provided a video on youtube here
And you can read all about them on their website here
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 didn't find any example for constructor CupertinoFullscreenDialogTransition
https://api.flutter.dev/flutter/cupertino/CupertinoFullscreenDialogTransition-class.html
I tried to understand below code but I didn't get it.
CupertinoFullscreenDialogTransition({
Key key,
#required Animation<double> animation,
#required this.child,
}) : _positionAnimation = CurvedAnimation(
parent: animation,
curve: Curves.linearToEaseOut,
// The curve must be flipped so that the reverse animation doesn't play
// an ease-in curve, which iOS does not use.
reverseCurve: Curves.linearToEaseOut.flipped,
).drive(_kBottomUpTween),
super(key: key);
Here's a more complete example
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
static const String _title = 'AppBar tutorial';
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
brightness: Brightness.light,
primaryColor: Colors.blue[900],
appBarTheme: AppBarTheme(iconTheme: IconThemeData(color: Colors.white)),
),
title: _title,
home: CupertinoFullscreenDialogTransitionPage(),
);
}
}
//First Page
class CupertinoFullscreenDialogTransitionPage extends StatefulWidget {
#override
_CupertinoFullscreenDialogTransitionState createState() =>
_CupertinoFullscreenDialogTransitionState();
}
class _CupertinoFullscreenDialogTransitionState
extends State<CupertinoFullscreenDialogTransitionPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: PreferredSize(
preferredSize: Size.fromHeight(60),
child: AppBar(
title: Text("Cupertino Screen Transition"),
centerTitle: true,
),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CupertinoButton.filled(
child: Text("Next Page Cupertino Transition"),
onPressed: () => Navigator.of(context).push(
PageRouteBuilder(
opaque: false,
pageBuilder: (context, _, __) {
return FullDialogPage();
},
),
),
),
],
)),
);
}
}
//Second Page
class FullDialogPage extends StatefulWidget {
#override
_FullDialogPageState createState() => _FullDialogPageState();
}
class _FullDialogPageState extends State<FullDialogPage>
with TickerProviderStateMixin {
AnimationController _primary, _secondary;
Animation<double> _animationPrimary, _animationSecondary;
#override
void initState() {
//Primaty
_primary = AnimationController(vsync: this, duration: Duration(seconds: 1));
_animationPrimary = Tween<double>(begin: 0, end: 1)
.animate(CurvedAnimation(parent: _primary, curve: Curves.easeOut));
//Secondary
_secondary =
AnimationController(vsync: this, duration: Duration(seconds: 1));
_animationSecondary = Tween<double>(begin: 0, end: 1)
.animate(CurvedAnimation(parent: _secondary, curve: Curves.easeOut));
_primary.forward();
super.initState();
}
#override
void dispose() {
_primary.dispose();
_secondary.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return CupertinoFullscreenDialogTransition(
primaryRouteAnimation: _animationPrimary,
secondaryRouteAnimation: _animationSecondary,
linearTransition: false,
child: Scaffold(
appBar: AppBar(
backgroundColor: Colors.indigo[900],
title: Text("Testing"),
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () {
_primary.reverse();
Future.delayed(Duration(seconds: 1), () {
Navigator.of(context).pop();
});
},
),
),
),
);
}
}
I've made this simple example, I hope it helps you understand how to implement the CupertinoFullscreenDialogTransition Widget.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.orange,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin{
AnimationController _animationController;
#override
void initState() {
_animationController = AnimationController(
vsync: this,
duration: Duration(milliseconds: 500),
);
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Stackoverflow playground'),
),
body: Container(
child: Column(
children: <Widget>[
CupertinoFullscreenDialogTransition(
primaryRouteAnimation: _animationController,
secondaryRouteAnimation: _animationController,
linearTransition: false,
child: Center(
child: Container(
color: Colors.blueGrey,
width: 300,
height: 300,
),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
RaisedButton(
onPressed: () => _animationController.forward(),
child: Text('Forward'),
),
RaisedButton(
onPressed: () => _animationController.reverse(),
child: Text('Reverse'),
),
],
),
],
),
)
);
}
}