How can I animate an IconButton placed in a AppBar? The sync icon should spinning while a database synchronisation is running.
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Dashboard"),
actions: <Widget>[
IconButton(
icon: Icon(Icons.sync), // <-- Icon
onPressed: () {
print("sync");
// start spinning
syncDatabaseFull(); // Returns future and resolves when sync is finish
},
)
],
),
body: Center(
child: RaisedButton(
child: Text('HOME screen'),
onPressed: () {
},
),
),
);
}
}
You can copy paste run full code below
You can extend AnimatedWidget and pass callback
example code below simulate syncDatabaseFull run for 5 seconds
code snippet
class AnimatedSync extends AnimatedWidget {
VoidCallback callback;
AnimatedSync({Key key, Animation<double> animation, this.callback})
: super(key: key, listenable: animation);
Widget build(BuildContext context) {
final Animation<double> animation = listenable;
return Transform.rotate(
angle: animation.value,
child: IconButton(
icon: Icon(Icons.sync), // <-- Icon
onPressed: () => callback()),
);
}
}
actions: <Widget>[
AnimatedSync(
animation: rotateAnimation,
callback: () async{
controller.forward();
await syncDatabaseFull();
controller.stop();
controller.reset();
},
),
],
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: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class AnimatedSync extends AnimatedWidget {
VoidCallback callback;
AnimatedSync({Key key, Animation<double> animation, this.callback})
: super(key: key, listenable: animation);
Widget build(BuildContext context) {
final Animation<double> animation = listenable;
return Transform.rotate(
angle: animation.value,
child: IconButton(
icon: Icon(Icons.sync), // <-- Icon
onPressed: () => callback()),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage>
with SingleTickerProviderStateMixin {
AnimationController controller;
Animation colorAnimation;
Animation rotateAnimation;
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
Future<bool> syncDatabaseFull() async{
await Future.delayed(Duration(seconds: 5), () {
});
return Future.value(true);
}
#override
void initState() {
controller =
AnimationController(vsync: this, duration: Duration(seconds: 200));
rotateAnimation = Tween<double>(begin: 0.0, end: 360.0).animate(controller);
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
actions: <Widget>[
AnimatedSync(
animation: rotateAnimation,
callback: () async{
controller.forward();
await syncDatabaseFull();
controller.stop();
controller.reset();
},
),
],
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
CustomedSpinningIconButton class
import 'package:flutter/material.dart';
class SpinningIconButton extends AnimatedWidget {
final VoidCallback onPressed;
final IconData iconData;
final AnimationController controller;
SpinningIconButton({Key key, this.controller, this.iconData, this.onPressed})
: super(key: key, listenable: controller);
Widget build(BuildContext context) {
final Animation<double> _animation = CurvedAnimation(
parent: controller,
// Use whatever curve you would like, for more details refer to the Curves class
curve: Curves.linearToEaseOut,
);
return RotationTransition(
turns: _animation,
child: IconButton(
icon: Icon(iconData),
onPressed: onPressed,
),
);
}
}
How to use it:
class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin {
AnimationController _animationController;
#override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this,
duration: const Duration(seconds: 1)
);
}
#override
Widget build(BuildContext context) {
...
actions: <Widget>[
SpinningIconButton(
controller: _animationController,
iconData: Icons.sync,
onPressed: () async {
// Play the animation infinitely
_animationController.repeat();
// Sleep 1.5 seconds or await the Async method
print('Something has finished.');
// Complete current cycle of the animation
_animationController.forward(from: _animationController.value);
},
)
],
...
}
Related
How do i move from the first page to another page by using items from the first page?
I am using pageview on the page menu, can someone help me?
So basically you can create a variable that contains the value on your first page and while you add your pages in any list you can simply pass that value like
int test=0
final List<Widget> _list = [];
#override
void initState() {
super.initState();
_list.addAll(
const [DashboardScreen(test), WishListScreen(), CartScreen(), ProfileScreen()]);
}
And other side in dashboardscreen
final int test;
const DashboardScreen(this.test, {Key? key}) : super(key: key);
if your page is statful widget then in build file you can simple use widget.test
or if you have stateless widget you can directly use test variable
You can use page controller
// through the page controller you can do using animateToPage or _pageController.jumpToPage(0). here 0 is page index
import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: MyPageView(),
);
}
}
class MyPageView extends StatefulWidget {
const MyPageView({Key? key}) : super(key: key);
#override
State<MyPageView> createState() => _MyPageViewState();
}
class _MyPageViewState extends State<MyPageView> {
final PageController _pageController = PageController();
#override
void dispose() {
_pageController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: PageView(
controller: _pageController,
children: <Widget>[
Container(
color: Colors.red,
child: Center(
child: ElevatedButton(
onPressed: () {
if (_pageController.hasClients) {
_pageController.animateToPage(
1,
duration: const Duration(milliseconds: 400),
curve: Curves.easeInOut,
);
// you can use animateToPage or _pageController.jumpToPage(0). here 0 is page index
}
},
child: const Text('Next'),
),
),
),
Container(
color: Colors.blue,
child: Center(
child: ElevatedButton(
onPressed: () {
if (_pageController.hasClients) {
_pageController.animateToPage(
0,
duration: const Duration(milliseconds: 400),
curve: Curves.easeInOut,
);
}
},
child: const Text('Previous'),
),
),
),
],
),
),
);
}
}
I am currently trying to animate an elevatedButton, nothing too fancy just trying to animate the elevated property. So I’ve scaffolded a new flutter starter app in VS code and added in the relevant code for the animations and animation controller, however I keep getting this error : type ‘double’ is not a subtype of type ‘MaterialStateProperty<double?>?’ I don’t understand why, here’s my code
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> with TickerProviderStateMixin {
AnimationController animationController;
Animation animation;
#override void initState() {
super.initState();
animationController = AnimationController(
vsync: this,
duration: Duration(seconds: 5),
);
animation = Tween(begin: 50.0, end: 150.0).animate(
CurvedAnimation(
parent: animationController,
curve: Interval(0.0, 1.0, curve: Curves.easeInOut),
),
);
}
#override void dispose() {
animationController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
onPressed: (){
animationController.forward();
},
child: Text("Button Text"),
style: ButtonStyle(
elevation: animation.value,
),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: (){},
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
try with this with MaterialStateProperty.all<double>(animation.value), in button style
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> with SingleTickerProviderStateMixin {
AnimationController animationController;
Animation animation;
#override void initState() {
super.initState();
animationController = AnimationController(
vsync: this,
duration: Duration(seconds: 5),
);
animation = Tween(begin: 50.0, end: 150.0).animate(
CurvedAnimation(
parent: animationController,
curve: Interval(0.0, 1.0, curve: Curves.easeInOut),
),
);
}
#override void dispose() {
animationController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
onPressed: (){
animationController.forward();
},
child: Text("Button Text"),
style: ButtonStyle(
// here you will change the `MaterialStateProperty` value
elevation: MaterialStateProperty.all<double>(animation.value),
),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: (){},
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
According to ButtonStyle Documentation:
Many of the ButtonStyle properties are MaterialStateProperty objects which resolve to different values depending on the button's state. For example the Color properties are defined with MaterialStateProperty and can resolve to different colors depending on if the button is pressed, hovered, focused, disabled, etc.
So what you need to do is Pass the animation value as a MaterialStateProperty.
In your case it would look like this:
style: ButtonStyle(
elevation: MaterialStateProperty.all(animation.value),
),
Check out the MaterialStateProperty Class documentation to get more info.
I have a list view and inside the list view, there is a child widget which can grow when user tap on that.
I want to scroll to the bottom of the list when the user taps on the child and it grows.
when I pass callback function from the parent to the child to scroll to the bottom.
and call the function when the user tap on the child.
I get the following error: setState() or markNeedsBuild() called during build.
class MyWidget extends StatefulWidget {
#override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
ScrollController _controller = ScrollController();
void scrollToLast() {
print("trying to scroll");
setState(() {
_controller.animateTo(
_controller.position.maxScrollExtent,
duration: Duration(microseconds: 300),
curve: Curves.easeInOut,
);
});
}
#override
Widget build(BuildContext context) {
return ListView(
controller: _controller,
children: <Widget>[
MyChildWidget(
scrollToLast: this.scrollToLast,
)
],
);
}
}
class MyChildWidget extends StatefulWidget {
final VoidCallback scrollToLast;
MyChildWidget({
this.scrollToLast,
});
#override
_MyChildWidgetState createState() => _MyChildWidgetState(
scrollToLast: this.scrollToLast,
);
}
class _MyChildWidgetState extends State<MyChildWidget> {
final VoidCallback scrollToLast;
_MyChildWidgetState({
this.scrollToLast,
});
int count = 5;
#override
Widget build(BuildContext context) {
return InkWell(
onTap: () {
setState(() {
this.count += 5;
});
this.scrollToLast();
},
child: Column(
mainAxisSize: MainAxisSize.max,
children: List<Widget>.generate(
this.count,
(int index) => Container(
color: Colors.blue,
height: 30,
margin: EdgeInsets.all(10),
),
),
),
);
}
}
You can copy paste run full code below
You can use WidgetsBinding.instance.addPostFrameCallback
code snippet
void scrollToLast() {
print("trying to scroll");
WidgetsBinding.instance.addPostFrameCallback((_) {
setState(() {
_controller.animateTo(
_controller.position.maxScrollExtent,
duration: Duration(microseconds: 300),
curve: Curves.easeInOut,
);
});
});
}
working demo
full code
import 'package:flutter/material.dart';
class MyWidget extends StatefulWidget {
#override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
ScrollController _controller = ScrollController();
void scrollToLast() {
print("trying to scroll");
WidgetsBinding.instance.addPostFrameCallback((_) {
setState(() {
_controller.animateTo(
_controller.position.maxScrollExtent,
duration: Duration(microseconds: 300),
curve: Curves.easeInOut,
);
});
});
}
#override
Widget build(BuildContext context) {
return ListView(
controller: _controller,
children: <Widget>[
MyChildWidget(
scrollToLast: this.scrollToLast,
)
],
);
}
}
class MyChildWidget extends StatefulWidget {
final VoidCallback scrollToLast;
MyChildWidget({
this.scrollToLast,
});
#override
_MyChildWidgetState createState() => _MyChildWidgetState(
/*scrollToLast: this.scrollToLast,*/
);
}
class _MyChildWidgetState extends State<MyChildWidget> {
/* final VoidCallback scrollToLast;
_MyChildWidgetState({
this.scrollToLast,
});*/
int count = 5;
#override
Widget build(BuildContext context) {
return InkWell(
onTap: () {
setState(() {
this.count += 5;
});
widget.scrollToLast();
},
child: Column(
mainAxisSize: MainAxisSize.max,
children: List<Widget>.generate(
this.count,
(int index) => Container(
color: Colors.blue,
height: 30,
margin: EdgeInsets.all(10),
child: Text('$index'),
),
),
),
);
}
}
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: "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> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(child: MyWidget()),
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
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'),
),
],
),
],
),
)
);
}
}
in this below implemented code i can show dialog on bottom of page with Fade animation and now, i want to add SlideTransition to ModalRoute of this implementation to slide dialog from bottom, but i can't to do that
for example, what i want to have:
source code:
import 'package:flutter/material.dart';
import 'package:flutter/services.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.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;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Open the popup window',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
showPopup(context, _popupBody(), 'Popup Demo');
},
tooltip: 'Open Popup',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
showPopup(BuildContext context, Widget widget, String title, {BuildContext popupContext}) {
Navigator.push(
context,
PopupLayout(
top: MediaQuery.of(context).size.height * 0.300,
left: 0,
right: 0,
bottom: 0,
child: PopupContent(
content: Scaffold(
body: widget,
),
),
),
);
}
Widget _popupBody() {
return Container(
child: Text('This is a popup window'),
);
}
}
class PopupLayout extends ModalRoute {
double top;
double bottom;
double left;
double right;
Color bgColor;
final Widget child;
#override
Duration get transitionDuration => Duration(milliseconds: 200);
#override
bool get opaque => false;
#override
bool get barrierDismissible => false;
#override
Color get barrierColor => bgColor == null ? Colors.black.withOpacity(0.5) : bgColor;
#override
String get barrierLabel => null;
#override
bool get maintainState => false;
PopupLayout({Key key, this.bgColor, #required this.child, this.top, this.bottom, this.left, this.right});
#override
Widget buildPage(
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
) {
if (top == null) this.top = 10;
if (bottom == null) this.bottom = 20;
if (left == null) this.left = 20;
if (right == null) this.right = 20;
return GestureDetector(
onTap: () {
// call this method here to hide soft keyboard
SystemChannels.textInput.invokeMethod('TextInput.hide');
},
child: Material(
// This makes sure that text and other content follows the material style
type: MaterialType.transparency,
//type: MaterialType.canvas,
// make sure that the overlay content is not cut off
child: SafeArea(
bottom: true,
child: _buildOverlayContent(context),
),
),
);
}
Widget _buildOverlayContent(BuildContext context) {
return Container(
margin: EdgeInsets.only(bottom: this.bottom, left: this.left, right: this.right, top: this.top),
child: SlideTransition(child: child),
);
}
#override
Widget buildTransitions(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) {
return FadeTransition(opacity: animation, child: child);
}
}
class PopupContent extends StatefulWidget {
final Widget content;
PopupContent({
Key key,
this.content,
}) : super(key: key);
_PopupContentState createState() => _PopupContentState();
}
class _PopupContentState extends State<PopupContent> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Container(
child: widget.content,
);
}
}
Here is a working example
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.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>
with TickerProviderStateMixin {
void showPopup() {
AnimationController controller = AnimationController(
duration: const Duration(milliseconds: 400), vsync: this);
showDialog(
context: context,
builder: (_) => PopUp(
controller: controller,
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: showPopup,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
class PopUp extends StatefulWidget {
final AnimationController controller;
PopUp({this.controller});
#override
State<StatefulWidget> createState() => PopUpState();
}
class PopUpState extends State<PopUp> {
Animation<double> opacityAnimation;
Tween<double> opacityTween = Tween<double>(begin: 0.0, end: 1.0);
Tween<double> marginTopTween = Tween<double>(begin: 600, end: 200);
Animation<double> marginTopAnimation;
AnimationStatus animationStatus;
#override
void initState() {
super.initState();
marginTopAnimation = marginTopTween.animate(widget.controller)
..addListener(() {
animationStatus = widget.controller.status;
if (animationStatus == AnimationStatus.dismissed) {
Navigator.of(context).pop();
}
if(this.mounted) {
setState(() {
});
}
});
widget.controller.forward();
}
#override
Widget build(BuildContext context) {
return FadeTransition(
opacity: opacityTween.animate(widget.controller),
child: GestureDetector(
onTap: () {
widget.controller.reverse();
},
child: Material(
color: Colors.transparent,
child: Container(
margin: EdgeInsets.only(
top: marginTopAnimation.value,
),
color: Colors.red,
child: Text("Container"),
),
),
),
);
}
#override
void dispose() {
widget.controller.dispose();
super.dispose();
}
}
UPDATE 1: Added Material as a child of Container to fix the barrier not dismissing bug.
UPDATE 2: Made a few more changes which reverses the animation when the barrier is dismissed.
NOTE: The screenshot does not reflect the updated changes. It is a demo of the original answer.