Reputation: 1538
I want to do a slide-down
animation for a widget. I've seen a lot of examples from the internet but nothing meets my requirements. Here is what I need. Below is a custom widget I made.
Widget Toast(String content, ToastType type) {
return Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(50),
child: Card(
elevation: 10,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(5)),
color: ToastColors[type.index],
child: Padding(
padding: const EdgeInsets.only(left: 15, right: 20, top: 10, bottom: 10),
child: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ToastIcons[type.index],
SizedBox(
width: 15,
),
Flexible(
child: Text(
content,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w400,
),
),
),
],
),
),
),
),
],
);
}
It's a toast layout. I want this to be accessible from anywhere within the app. That's why I've created separate dart
file for this.
What I want? When the widget is shown, I want the toast layout to slide down. I don't want to loop or reverse the animation. After the animation is completed, the widget must stay still in the end position.
Upvotes: 2
Views: 15246
Reputation: 1538
Nvm. I figured it out. First I created a StatefulWidget
and used it as a child in showToastWidget
function.
Here is the StatefulWidget
class
import 'package:flutter/material.dart';
class SlideToast extends StatefulWidget {
final Widget _toast;
SlideToast(this._toast);
@override
_SlideToastState createState() => _SlideToastState();
}
class _SlideToastState extends State<SlideToast> with TickerProviderStateMixin {
AnimationController _controller;
Animation<Offset> _offsetFloat;
@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 350),
);
_offsetFloat =
Tween(begin: Offset(0.0, -0.03), end: Offset.zero).animate(
CurvedAnimation(
parent: _controller,
curve: Curves.fastOutSlowIn,
),
);
_offsetFloat.addListener(() {
setState(() {});
});
_controller.forward();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return SlideTransition(
position: _offsetFloat,
child: widget._toast,
);
}
}
Required function
, enum
, list
enum ToastType { Info, Warning, Success, Error }
const List<Color> ToastColors = [
Colors.blue,
Colors.orange,
Colors.green,
Colors.redAccent
];
const List<Icon> ToastIcons = [
Icon(
Icons.info,
color: Colors.white,
),
Icon(
Icons.info,
color: Colors.white,
),
Icon(
Icons.check_circle,
color: Colors.white,
),
Icon(
Icons.error,
color: Colors.white,
)
];
Widget Toast(String content, ToastType type) {
return Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(top: 85),
child: Card(
elevation: 10,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(5)),
color: ToastColors[type.index],
child: Padding(
padding:
const EdgeInsets.only(left: 15, right: 20, top: 10, bottom: 10),
child: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ToastIcons[type.index],
SizedBox(
width: 15,
),
Flexible(
child: Text(
content,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w400,
),
),
),
],
),
),
),
),
],
);
}
Finally call showToastWidget
like this
showToastWidget(
Toast('Hello World!!!', ToastType.Warning),
duration: Duration(seconds: 1),
);
Upvotes: 8
Reputation: 40503
I'm extrapolating from your description of your code that you want to be able to create a toast that slides down from the top of the screen, and that you want to be able to create it from anywhere.
You're in luck, flutter has a function for that! showGeneralDialog
is what you're looking for.
Here's an example:
import 'package:flutter/material.dart';
main() => runApp(TheApp());
class TheApp extends StatefulWidget {
@override
_TheAppState createState() => _TheAppState();
}
class _TheAppState extends State<TheApp> {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
floatingActionButton: Builder(
builder: (context) {
return FloatingActionButton(
child: Icon(
Icons.add,
),
onPressed: () {
bool wasCompleted = false;
showGeneralDialog(
context: context,
barrierDismissible: true,
transitionDuration: Duration(milliseconds: 500),
barrierLabel: MaterialLocalizations.of(context).dialogLabel,
barrierColor: Colors.black.withOpacity(0.5),
pageBuilder: (context, _, __) {
return TheToast();
},
transitionBuilder: (context, animation, secondaryAnimation, child) {
if (animation.status == AnimationStatus.completed) {
wasCompleted = true;
}
if (wasCompleted) {
return FadeTransition(
opacity: animation,
child: child,
);
} else {
return SlideTransition(
position: CurvedAnimation(
parent: animation,
curve: Curves.easeOut,
).drive(Tween<Offset>(begin: Offset(0, -1.0), end: Offset.zero)),
child: child,
);
}
},
);
},
);
},
),
body: Container(),
),
);
}
}
class TheToast extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Card(
color: Colors.white,
child: Padding(
padding: EdgeInsets.all(10),
child: Text(
"I'm a Toast",
style: TextStyle(color: Colors.black),
),
),
),
);
}
}
You could make your own function similar to showGeneralDialog (maybe showToast) and call that with the appropriate text. I've also made the toast fade out after the animation; if you just want it to slide back up that's even simpler and you can get rid of that part of the transition logic. I've also made the toast disappear when the background is tapped but you could disable that and instead call Navigator.pop(context) when you want the dialog to be hidden instead.
You could also do this directly with Overlay entries but this is definitely simpler.
Upvotes: 5