FadeTransition animation is not working for backdropfilter.
The opacity goes from 0 to 1 suddenly, without the smooth transition that the animation should give.
As you can see from the code below, I have a background image and then a backdropfilter on top to blur the background at app startup.
(I then show other widgets, but the animation is working fine for them).
import 'dart:ui';
import 'package:flutter/material.dart';
class SplashScreen extends StatefulWidget {
#override
_SplashScreenState createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen>
with SingleTickerProviderStateMixin {
AnimationController _controller;
Animation<double> _backgroundOpacity;
bool _visible = true;
#override
void initState() {
_controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
)..forward();
// background opacity animation
_backgroundOpacity = Tween<double>(
begin: 0,
end: 1,
).animate(
CurvedAnimation(
parent: _controller,
curve: Interval(0, 1, curve: Curves.linear),
),
);
super.initState();
}
#override
void dispose() {
super.dispose();
_controller.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.green[800],
),
body: Stack(
children: [
Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Image(
image: AssetImage('assets/field.jpg'),
fit: BoxFit.cover,
),
),
FadeTransition(
opacity: _backgroundOpacity,
child: Container(
child: BackdropFilter(
filter: new ImageFilter.blur(sigmaX: 6.0, sigmaY: 6.0),
child: new Container(
decoration: new BoxDecoration(
color: Colors.grey.shade200.withOpacity(0.5),
),
),
),
),
),
],
),
);
}
}
basically what I get from this code is the background image clear and nice, then after 2 seconds suddenly blurred, without any transition.
What should I do to make fadeTransition to work with Backdropfilter?
The preferred way to fade in a blur is to use a TweenAnimationBuilder:
TweenAnimationBuilder<double>(
duration: Duration(milliseconds: 500),
tween: Tween<double>(begin: 0, end: 6),
builder: (context, value, _) {
return BackdropFilter(
key: GlobalObjectKey('background'),
filter: ImageFilter.blur(sigmaY: value, sigmaX: value),
child: Container(
decoration: new BoxDecoration(
color: Colors.grey.shade200.withOpacity(0.5),
),
),
);
},
),
Related
I need to divide the range of animation value to a smaller ranges like this simple example:
class TextAnimation extends StatefulWidget {
const TextAnimation({Key? key}) : super(key: key);
#override
State<TextAnimation> createState() => _TextAnimationState();
}
class _TextAnimationState extends State<TextAnimation>
with SingleTickerProviderStateMixin {
late AnimationController anController;
#override
void initState() {
// TODO: implement initState
super.initState();
anController = AnimationController(
vsync: this,
duration: Duration(seconds: 5),
reverseDuration: Duration(seconds: 5));
}
#override
void dispose() {
// TODO: implement dispose
super.dispose();
anController.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: AnimatedBuilder(
animation: anController,
builder: (context, child) {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
width: double.infinity,
),
Container(
width: 300 *
Tween(begin: 0.0, end: 1.0)
.animate(CurvedAnimation(
parent: anController,
curve:
Curves.fastOutSlowIn))
.value,
height: 100,
color: Colors.red,
),
Container(
width: 300 *
(Tween(begin: 0.0, end: 1.0) .animate(CurvedAnimation(
parent: anController, curve: Curves.fastOutSlowIn))
.value.clamp(0.5, 1)),
height: 100,
color: Colors.blue),
InkWell(
onTap: () {
anController.forward();
},
child: Text(
'Start',
style: TextStyle(
color: Colors.black,
fontSize: 20,
),
))
],
);
}),
);
}
}
the second container need to start the animation from the half of the range to the end...
the problem is, the curve effect is lost in this range ,I need to give this range (0.5-1.0) its own curve ,
start fast in 0.5 and end with slow motion.
is there a way to do it without using another animation controller to drive this range?
note : without AnimatedContainer the code above is just a simple example for the problem.
I have card with image file and text where I want to do hover effect. I just want to display more information of product on mouse hover like image below. Card is overlaying the other card. How can I achieve this? Please help. Flutter Web
You can use Mouse Region to detect a hover and use the animation class for animating the widget. create a separate widget for this box and use it like this. here is a code sample you may like to use.
Sample Code here
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
bool touched = false;
String image1 =
'https://images.unsplash.com/photo-1525786210598-d527194d3e9a?ixlib=rb-1.2.1&auto=format&fit=crop&w=634&q=80';
String image2 =
'https://images.unsplash.com/photo-1578616070222-50c4de9d5ade?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=600&q=60';
String image3 =
'https://images.unsplash.com/photo-1502324224542-7ea9927946fd?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=600&q=60';
class _MyAppState extends State<MyApp> with TickerProviderStateMixin {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Container(
color: const Color(0xff393e46),
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
HoverImage(
image: image1,
),
HoverImage(
image: image2,
),
HoverImage(
image: image3,
),
],
),
),
),
),
);
}
}
class HoverImage extends StatefulWidget {
final String image;
const HoverImage({required this.image});
#override
_HoverImageState createState() => _HoverImageState();
}
class _HoverImageState extends State<HoverImage>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation _animation;
late Animation padding;
#override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(milliseconds: 275),
vsync: this,
);
_animation = Tween(begin: 1.0, end: 1.2)
.animate(CurvedAnimation(parent: _controller, curve: Curves.ease, reverseCurve: Curves.easeIn));
padding = Tween(begin: 0.0, end: -25.0)
.animate(CurvedAnimation(parent: _controller, curve: Curves.ease,reverseCurve: Curves.easeIn));
_controller.addListener(() {
setState(() {});
});
}
#override
Widget build(BuildContext context) {
return MouseRegion(
onEnter: (value) {
setState(() {
_controller.forward();
});
},
onExit: (value) {
setState(() {
_controller.reverse();
});
},
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20.0),
boxShadow: const [
BoxShadow(
color: Colors.black26,
offset: Offset(0.0, 20.0),
spreadRadius: -10.0,
blurRadius: 20.0,
)
],
),
child: Container(
height: 220.0,
width: 170.0,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20.0),
),
clipBehavior: Clip.hardEdge,
transform: Matrix4(_animation.value, 0, 0, 0, 0, _animation.value, 0,
0, 0, 0, 1, 0, padding.value, padding.value, 0, 1),
child: Image.network(
widget.image,
fit: BoxFit.cover,
),
),
),
);
}
}
I am trying to create a flutter application where the background image changes based off data coming in from an API. I have the following code written to have the screen fade in using an AnimationController and Animation of doubles, however whenever I try to load the app, all I get is a black screen.
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() => runApp(RootWidget());
class RootWidget extends StatefulWidget {
#override
_RootWidgetState createState() => _RootWidgetState();
}
class _RootWidgetState extends State<RootWidget> with TickerProviderStateMixin {
AnimationController _controller;
Animation<double> _animation;
#override
void initState() {
super.initState();
_controller =
AnimationController(vsync: this, duration: Duration(milliseconds: 500));
_animation =
Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation(
parent: _controller,
curve: Curves.easeOut
));
}
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(primarySwatch: Colors.green),
home: AnimatedOpacity(
opacity: _animation.value,
duration: Duration(milliseconds: 1000),
child: Scaffold(
appBar: AppBar(
title: Text('Vuetiful Mobile'),
),
body: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/Sunny-photo.jpg'),
fit: BoxFit.cover
)
),
child: Container(
color: Color.fromARGB(100, 0, 0, 0),
child: Center(
child: Text('Temp', style: TextStyle(color: Colors.white))
)
)
),
),
),
);
}
}
You should either choose between using an AnimationController or the AnimatedOpacity widget, but not both.
AnimatedOpacity is already an animated version of opacity, so adding a controller to it doesn't do what you want; it handles all of the necessary animation logic itself. You just need to provide an opacity and it animates to it in the given duration and with the given curve.
The likely easier solution would be to stick with AnimatedOpacity and get rid of the controller. Then set the opacity value with a post frame callback:
class _RootWidgetState extends State<RootWidget> {
double opacity = 0;
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
setState(() {
opacity = 1;
});
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(primarySwatch: Colors.green),
home: AnimatedOpacity(
opacity: opacity,
curve: Curves.easeOut,//Add your curve here
duration: Duration(milliseconds: 1000),//Set your desired duration
child: Scaffold(
appBar: AppBar(
title: Text('Vuetiful Mobile'),
),
body: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/Sunny-photo.jpg'),
fit: BoxFit.cover
)
),
child: Container(
color: Color.fromARGB(100, 0, 0, 0),
child: Center(
child: Text('Temp', style: TextStyle(color: Colors.white))
)
)
),
),
),
);
}
}
This solution also ends up being more efficient because flutter doesn't have to rebuild as much stuff for each tick.
Alternatively, you could keep the AnimationController and change AnimatedOpacity to Opacity:
class _RootWidgetState extends State<RootWidget> with TickerProviderStateMixin {
AnimationController _controller;
Animation<double> _animation;
#override
void initState() {
super.initState();
_controller = AnimationController(vsync: this, duration: Duration(milliseconds: 500));
_controller.addListener(() {
setState((){});
});
_controller.forward();
_animation =
Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation(
parent: _controller,
curve: Curves.easeOut
));
}
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(primarySwatch: Colors.green),
home: Opacity(
opacity: _animation.value,
child: Scaffold(
appBar: AppBar(
title: Text('Vuetiful Mobile'),
),
body: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/Sunny-photo.jpg'),
fit: BoxFit.cover
)
),
child: Container(
color: Color.fromARGB(100, 0, 0, 0),
child: Center(
child: Text('Temp', style: TextStyle(color: Colors.white))
)
)
),
),
),
);
}
}
What i want is to chain multiple Animation object like if we have an Animation<double> that goes from 0 to 40 (let's call it the big one) and i have another 4 Animation<double> object what i want is
when the big animation starts the first animation start with it , but it end's when the big one reach 10.
when the big one reach 10 the second animation start's and end's when the big one reach 20.
etc...
any one knows how to do it in flutter ??
It sounds like staggered animations.
Basically, you can create an animation controller and set the total duration time.
Then, you can create a separate tween for each animation you want to perform. For each tween, you can define a curve, for that curve you can define an interval in the percentage of the total duration of the animation. There is a pretty good example in a flutter.dev when you search for staggered animations. Note: they don't have to be one after another, despite the name, they can be fired at the same time, but end at different time as you want. I'm not sure if it is appropriate to give an answer with just sharing the link to the flutter docs, but here it is
https://flutter.dev/docs/development/ui/animations/staggered-animations.
I have done something similar, but with 2 controllers, which I fire at the same time, but they have different durations.
Ah, I was second :)
edit: here is some code
one with 2 controlers
import 'package:flutter/material.dart';
//import 'package:flutter/scheduler.dart';
import 'package:flutter_color_picker/components/color_ripple.dart';
class ColorKnob extends StatefulWidget {
const ColorKnob({this.color, this.ratio, this.saveColor});
final Color color;
final double ratio;
final Function saveColor;
#override
_ColorKnobState createState() => _ColorKnobState();
}
class _ColorKnobState extends State<ColorKnob> with TickerProviderStateMixin {
AnimationController scaleAnimationController;
AnimationController rippleAnimationController;
Animation<double> scaleAnimation;
#override
void initState() {
super.initState();
scaleAnimationController =
AnimationController(vsync: this, duration: Duration(milliseconds: 100));
rippleAnimationController =
AnimationController(vsync: this, duration: Duration(milliseconds: 400));
scaleAnimationController.addStatusListener((AnimationStatus status) {
if (status == AnimationStatus.completed) {
scaleAnimationController.reverse();
}
});
scaleAnimation = Tween<double>(begin: 1.0, end: 0.8).animate(
CurvedAnimation(
parent: scaleAnimationController, curve: Curves.easeOut));
rippleAnimationController.addStatusListener((AnimationStatus status) {
if (status == AnimationStatus.completed) {
widget.saveColor();
}
});
scaleAnimation.addListener(() {
setState(() {});
});
}
#override
void dispose() {
super.dispose();
scaleAnimationController.dispose();
rippleAnimationController.dispose();
}
#override
Widget build(BuildContext context) {
return Center(
child: Container(
decoration: const BoxDecoration(
shape: BoxShape.circle, color: Colors.transparent),
child: FractionallySizedBox(
widthFactor: widget.ratio,
heightFactor: widget.ratio,
child: ClipOval(
clipBehavior: Clip.antiAlias,
child: Center(
child: Stack(children: <Widget>[
ColorRipple(
controller: rippleAnimationController,
color: widget.color,
),
GestureDetector(
onTap: () {
// timeDilation = 1.0;
scaleAnimationController.forward(from: 0.0);
rippleAnimationController.forward(from: 0.0);
},
child: Transform.scale(
scale: scaleAnimation.value,
alignment: Alignment.center,
child: Container(
width: 60.0,
height: 60.0,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: widget.color,
border: Border.all(
color: const Color(0xFFFFFFFF),
style: BorderStyle.solid,
width: 4.0,
),
boxShadow: const <BoxShadow>[
BoxShadow(
offset: Offset(0.0, 1.0),
blurRadius: 6.0,
spreadRadius: 1.0,
color: Color(0x44000000),
)
]),
),
),
),
]),
),
),
)),
);
}
}
and one with multiple tweens
import 'package:flutter/material.dart';
//import 'package:flutter/scheduler.dart';
class ColorRipple extends StatelessWidget {
ColorRipple({this.controller, this.color, this.size})
: scaleUpAnimation = Tween<double>(begin: 0.8, end: 5.0).animate(
CurvedAnimation(
parent: controller,
curve: const Interval(
0.2,
1.0,
curve: Cubic(0.25, 0.46, 0.45, 0.94),
),
),
),
opacityAnimation = Tween<double>(begin: 0.6, end: 0.0).animate(
CurvedAnimation(
parent: controller,
curve: const Interval(
0.4,
1.0,
curve: Cubic(0.25, 0.46, 0.45, 0.94),
),
),
),
scaleDownAnimation = Tween<double>(begin: 1.0, end: 0.8).animate(
CurvedAnimation(
parent: controller,
curve: const Interval(0.0, 0.2, curve: Curves.easeOut),
),
);
final AnimationController controller;
final Animation<double> scaleUpAnimation;
final Animation<double> scaleDownAnimation;
final Animation<double> opacityAnimation;
final Color color;
final Size size;
#override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: controller,
builder: (BuildContext context, Widget child) {
return Container(
child: Transform(
alignment: Alignment.center,
transform: Matrix4.identity()
..scale(scaleDownAnimation.value)
..scale(scaleUpAnimation.value),
child: Opacity(
opacity: opacityAnimation.value,
child: Container(
width: 60.0,
height: 60.0,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: color,
style: BorderStyle.solid,
width: 4.0 - (2 * controller.value))),
)),
),
);
});
}
}
for implementing this animation
i wrote this below code but, Elastic animation doesn't work on project and i'm not sure whats problem,
i want to have repeat of this animation
import 'package:flutter/material.dart';
void main()=>runApp(MaterialApp(home: Avatar(),));
class Avatar extends StatefulWidget {
#override
State<StatefulWidget> createState()=>_Avatar();
}
class _Avatar extends State<Avatar> with TickerProviderStateMixin{
AnimationController avatarController;
Animation<double> avatarSize;
#override
void initState() {
super.initState();
avatarController= AnimationController(
duration: const Duration(seconds: 1),
vsync: this,
);
avatarSize = new Tween(begin: 0.0, end: 1.0).animate(
new CurvedAnimation(
parent: avatarController,
curve: new Interval(
0.100,
0.400,
curve: Curves.elasticOut,
),
),
);
avatarController.repeate();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
fit:StackFit.expand,
children: <Widget>[
AnimatedBuilder(
animation: avatarController,
builder: (context, widget) => Align(
child: Container(
width: 50.0,
height: 50.0,
color:Colors.green
),
),
)
],
),
);
}
}
Output:
You can play with duration and Tween to fine grain it.
void main() => runApp(MaterialApp(home: Avatar()));
class Avatar extends StatefulWidget {
#override
State<StatefulWidget> createState() => _Avatar();
}
class _Avatar extends State<Avatar> with TickerProviderStateMixin {
AnimationController _controller;
Tween<double> _tween = Tween(begin: 0.75, end: 2);
#override
void initState() {
super.initState();
_controller = AnimationController(duration: const Duration(milliseconds: 700), vsync: this);
_controller.repeat(reverse: true);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
Align(
child: ScaleTransition(
scale: _tween.animate(CurvedAnimation(parent: _controller, curve: Curves.elasticOut)),
child: SizedBox(
height: 100,
width: 100,
child: CircleAvatar(backgroundImage: AssetImage(chocolateImage)),
),
),
),
],
),
);
}
}
The Tween's begin and end values should be the values you want to animate between. You then need to use the animated value somewhere in your layout.
For example, change your Tween to Tween(begin: 50.0, end: 100.0) and your Container to
Container(
width: avatarSize.value,
height: avatarSize.value,
color:Colors.green
)
Don't forget to also dispose of the animation controller within your widget's dispose():
#override
void dispose() {
avatarController.dispose();
super.dispose();
}
Add this dependency https://pub.dev/packages/animator
Try this code.
class BounceAnimation extends StatefulWidget {
#override
_BounceAnimationState createState() => _BounceAnimationState();
}
class _BounceAnimationState extends State<BounceAnimation>
with SingleTickerProviderStateMixin {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey,
appBar: AppBar(title: Text("Bouncing Animation example")),
body: Center(
child: Container(
child: Animator(
duration: Duration(seconds: 1),
curve: Curves.elasticOut,
builder: (anim) {
return Transform.scale(
origin: Offset(00, -59),
scale: anim.value,
child: Transform.translate(
offset: Offset(00, -65),
child: CircleAvatar(
radius: 86,
backgroundColor: Colors.white,
child: CircleAvatar(
radius: 84,
backgroundColor: Colors.grey,
child: CircleAvatar(
radius: 80,
backgroundColor: Colors.white,
foregroundColor: Colors.black,
backgroundImage: NetworkImage(
"https://i1.wp.com/devilsworkshop.org/wp-content/uploads/sites/8/2013/01/enlarged-facebook-profile-picture.jpg?w=448&ssl=1",
),
),
),
),
),
);
},
)),
),
);
}
}