Problems with multiple tween animations in Flutter - Videos Included - dart

I have a weird flutter issue with a certain animation I'm trying to create.
I am trying to animate an image onto the screen.
I want it to move on the x-axis, and I want it to slowly fade in as well.
So I figured - Positioned and Opacity, and animate their value with a tween.
Both the Positioned and Opacity widgets work great on their own, but when I combine the two - I get a weird animation, that only starts to draw after a while (about 3 seconds).
I tried printing the animation.value and it seems to be fine, slowly climbing from 0.0 to 1.0 - but the clouds suddenly appear only after like 3 seconds.
I tried separating them to different controllers, thought that maybe somehow that was the culprit, but nope.
TLDR Videos:
Opacity Animation Only
Positioned Animation Only
Both Animations Combined - NOT GOOD
Here's the widget's code:
import 'package:app/weather/widgets/CloudProperties.dart';
import 'package:flutter/widgets.dart';
class WeatherCloudWidget extends StatefulWidget {
final double sunSize;
final CloudProperties properties;
WeatherCloudWidget({Key key, this.properties, this.sunSize})
: super(key: key);
#override
State<StatefulWidget> createState() => _WeatherCloudWidget();
}
class _WeatherCloudWidget extends State<WeatherCloudWidget>
with TickerProviderStateMixin {
AnimationController controller;
AnimationController controller2;
Animation<double> position;
Animation<double> opacity;
#override
initState() {
super.initState();
_startAnimation();
}
#override
Widget build(BuildContext context) {
// screen width and height
final screenWidth = MediaQuery.of(context).size.width;
final screenHeight = MediaQuery.of(context).size.height;
final properties = widget.properties;
var vertical =
screenHeight * 0.5 + (widget.sunSize * properties.verticalOffset * -1);
var horizontal = (screenWidth * 0.5) + (widget.sunSize * position.value);
print(opacity.value);
// both Positioned & Opacity widgets
return Positioned(
left: horizontal,
top: vertical,
child: Opacity(
opacity: opacity.value,
child: Image.asset(
properties.path,
width: properties.getScaledWidth(widget.sunSize),
height: properties.getScaledHeight(widget.sunSize),
),
));
// Positioned only
return Positioned(
left: horizontal,
top: vertical,
child: Image.asset(
properties.path,
width: properties.getScaledWidth(widget.sunSize),
height: properties.getScaledHeight(widget.sunSize),
));
// Opacity only
return Positioned(
left: (screenWidth * 0.5) + (widget.sunSize * properties.tween[1]),
top: vertical,
child: Opacity(
opacity: opacity.value,
child: Image.asset(
properties.path,
width: properties.getScaledWidth(widget.sunSize),
height: properties.getScaledHeight(widget.sunSize),
),
));
}
#override
dispose() {
controller.dispose();
controller2.dispose();
super.dispose();
}
void _startAnimation() {
controller = AnimationController(
duration: const Duration(milliseconds: 5000), vsync: this);
controller2 = AnimationController(
duration: const Duration(milliseconds: 5000), vsync: this);
position = Tween(
begin: widget.properties.tween[0], end: widget.properties.tween[1])
.animate(
new CurvedAnimation(parent: controller, curve: Curves.decelerate))
..addListener(() => setState(() {}));
opacity = Tween(begin: 0.0, end: 1.0).animate(controller2)
..addListener(() => setState(() {}));
controller.forward();
controller2.forward();
}
}

Alright guys. I managed to sort this using SlideTransition and FadeTransition.
I guess we should only use Transition widgets for... transitions? while things like Positioned and Opacity are for more static widgets? Not sure...
What it looks like: https://youtu.be/hj7PkjXrgfg
Anyways, here's the replacement code, if anyone's looking for reference:
class WeatherCloudWidget extends StatefulWidget {
final double sunSize;
final CloudProperties properties;
WeatherCloudWidget({Key key, this.properties, this.sunSize})
: super(key: key);
#override
State<StatefulWidget> createState() => _WeatherCloudWidget();
}
class _WeatherCloudWidget extends State<WeatherCloudWidget>
with TickerProviderStateMixin {
AnimationController controller;
Animation<Offset> position;
Animation<double> opacity;
final alphaTween = new Tween(begin: 0.0, end: 1.0);
#override
initState() {
super.initState();
_startAnimation();
}
#override
Widget build(BuildContext context) {
// screen width and height
final screenWidth = MediaQuery.of(context).size.width;
final screenHeight = MediaQuery.of(context).size.height;
final properties = widget.properties;
var vertical = (screenHeight * 0.5) +
(widget.sunSize * properties.verticalOffset * -1);
var horizontal =
(screenWidth * 0.5) + (widget.sunSize * properties.tweenEnd);
return Positioned(
left: horizontal,
top: vertical,
child: SlideTransition(
position: position,
child: FadeTransition(
opacity: opacity,
child: Image.asset(
properties.path,
width: properties.getScaledWidth(widget.sunSize),
height: properties.getScaledHeight(widget.sunSize),
),
)),
);
}
#override
dispose() {
controller.dispose();
super.dispose();
}
void _startAnimation() {
controller = AnimationController(
duration: const Duration(milliseconds: 2000), vsync: this);
position = new Tween<Offset>(
begin: new Offset(widget.properties.tweenStart, 0.0),
end: new Offset(0.0, 0.0),
).animate(new CurvedAnimation(parent: controller, curve: Curves.decelerate));
opacity = alphaTween.animate(controller);
controller.forward();
}
}

Related

Flutter - DragBox Feedback animate to original position

I want to show the animation of feedback of draggable when it is not accepted by the DropTarget.Flutter doesn't show the feedback. Is there any way we can show that or control it. Like this example, I want to achieve this effect. I somehow achieve this effect but it is not proper accurate returning to the original offset. It is moving ahead to its original position.
Animation effect I want.
Here is my Code, I have one drag box when I lift it to a certain position and leave him from there and it should animate back to original position, but it is returning to some other Offset like this.
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(body: DragBox()),
);
}
}
class DragBox extends StatefulWidget {
DragBox({
Key key,
}) : super(key: key);
#override
State<StatefulWidget> createState() {
return new _MyDragBox();
}
}
class _MyDragBox extends State<DragBox> with TickerProviderStateMixin {
GlobalKey _globalKey = new GlobalKey();
AnimationController _controller;
Offset begin;
Offset cancelledOffset;
Offset _offsetOfWidget;
#override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((s) {
_afeteLayout();
});
super.initState();
_controller = AnimationController(
vsync: this,
duration: Duration(milliseconds: 1000),
);
}
void _afeteLayout() {
final RenderBox box = _globalKey.currentContext.findRenderObject();
Offset offset = -box.globalToLocal(Offset(0.0, 0.0));
_offsetOfWidget = offset;
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
Center(
child: Draggable(
key: _globalKey,
onDraggableCanceled: (v, o) {
setState(() {
cancelledOffset = o;
});
_controller.forward();
},
feedback: Container(
color: Colors.red,
height: 50,
width: 50,
),
child: Container(
color: Colors.red,
height: 50,
width: 50,
),
),
),
_controller.isAnimating
? SlideTransition(
position: Tween<Offset>(
begin: cancelledOffset * 0.01,
end: _offsetOfWidget * 0.01)
.animate(_controller)
..addStatusListener((status) {
if (status == AnimationStatus.completed) {
_controller.stop();
} else {
_controller.reverse();
}
}),
child: Container(
color: Colors.red,
height: 50,
width: 50,
),
)
: Container(
child: Text('data'),
)
],
);
}
}
I think, this documentation about "Animate a widget using a physics simulation" is the closest example suited for what you are trying to achieve.
This recipe demonstrates how to move a widget from a dragged point
back to the center using a spring simulation.
To appreciate it in action, take a look on the example:
import 'package:flutter/material.dart';
import 'package:flutter/physics.dart';
void main() {
runApp(MaterialApp(home: PhysicsCardDragDemo()));
}
class PhysicsCardDragDemo extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: DraggableCard(
child: FlutterLogo(
size: 128,
),
),
);
}
}
/// A draggable card that moves back to [Alignment.center] when it's
/// released.
class DraggableCard extends StatefulWidget {
final Widget child;
DraggableCard({required this.child});
#override
_DraggableCardState createState() => _DraggableCardState();
}
class _DraggableCardState extends State<DraggableCard>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
/// The alignment of the card as it is dragged or being animated.
///
/// While the card is being dragged, this value is set to the values computed
/// in the GestureDetector onPanUpdate callback. If the animation is running,
/// this value is set to the value of the [_animation].
Alignment _dragAlignment = Alignment.center;
late Animation<Alignment> _animation;
/// Calculates and runs a [SpringSimulation].
void _runAnimation(Offset pixelsPerSecond, Size size) {
_animation = _controller.drive(
AlignmentTween(
begin: _dragAlignment,
end: Alignment.center,
),
);
// Calculate the velocity relative to the unit interval, [0,1],
// used by the animation controller.
final unitsPerSecondX = pixelsPerSecond.dx / size.width;
final unitsPerSecondY = pixelsPerSecond.dy / size.height;
final unitsPerSecond = Offset(unitsPerSecondX, unitsPerSecondY);
final unitVelocity = unitsPerSecond.distance;
const spring = SpringDescription(
mass: 30,
stiffness: 1,
damping: 1,
);
final simulation = SpringSimulation(spring, 0, 1, -unitVelocity);
_controller.animateWith(simulation);
}
#override
void initState() {
super.initState();
_controller = AnimationController(vsync: this);
_controller.addListener(() {
setState(() {
_dragAlignment = _animation.value;
});
});
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
return GestureDetector(
onPanDown: (details) {
_controller.stop();
},
onPanUpdate: (details) {
setState(() {
_dragAlignment += Alignment(
details.delta.dx / (size.width / 2),
details.delta.dy / (size.height / 2),
);
});
},
onPanEnd: (details) {
_runAnimation(details.velocity.pixelsPerSecond, size);
},
child: Align(
alignment: _dragAlignment,
child: Card(
child: widget.child,
),
),
);
}
}
Sample output:
A simple way to solve this problem is to create a widget that overrides Draggable and make it a child of an AnimatedPositioned. Here is the example:
import 'package:flutter/material.dart';
class XDraggable extends StatefulWidget {
const XDraggable(
{Key? key,
required this.child,
required this.original_x,
required this.original_y,
this.animation_speed = 200})
: super(key: key);
final Widget child;
final double original_x;
final double original_y;
final double animation_speed;
#override
_XDraggableState createState() => _XDraggableState();
}
class _XDraggableState extends State<XDraggable> {
double x = 200;
double y = 200;
int animation_speed = 0;
#override
void initState() {
x = widget.original_x;
y = widget.original_y;
super.initState();
}
#override
Widget build(BuildContext context) {
return AnimatedPositioned(
left: x,
top: y,
duration: Duration(milliseconds: animation_speed),
child: Draggable(
onDragUpdate: (details) => {
setState(() {
animation_speed = 0;
x = x + details.delta.dx;
y = y + details.delta.dy;
}),
},
onDragEnd: (details) {
setState(() {
animation_speed = 200;
x = widget.original_x;
y = widget.original_y;
});
},
child: widget.child,
feedback: SizedBox(),
),
);
}
}
Then, just use the widget as a Stack child:
...
child: Stack(
fit: StackFit.expand,
children: [
XDraggable(
original_x: 20,
original_y: 20,
child: Container(
height: 50.0,
width: 50.0,
color: Colors.green,
),
)
],
),
...

Animation Of Container using Offset - Flutter

I am trying to move the container on the screen by giving begin and end offset like from Offset(0.0,0.0) to Offset(400.0,300.0). I am using Slide Transition to animate the container I am using Tween<Offset>(begin: const Offset(3.0, 4.0), end: Offset(0.0, 0.0)) to move it on the screen I want to pass these Offset(400.0,300.0) and animate it.
Here is my code
class MoveContainer extends StatefulWidget {
MoveContainer({Key key, }) : super(key: key);
#override
State<StatefulWidget> createState() {
return new _MyMoveContainer();
}
}
class _MyMoveContainer extends State<MoveContainer>
with TickerProviderStateMixin {
GlobalKey _globalKey = new GlobalKey();
AnimationController _controller;
Animation<Offset> _offset;
Offset local;
#override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: const Duration(seconds: 3),
);
_offset =
Tween<Offset>(begin: const Offset(3.0, 4.0), end: Offset(0.0, 0.0))
.animate(_controller);
_offset.addListener(() {
setState(() {});
});
_controller.forward();
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return SlideTransition(
position: _offset,
child: GestureDetector(
onPanStart: (start) {
RenderBox getBox = context.findRenderObject();
local = getBox.localToGlobal(start.globalPosition);
print('point are $local');
},
child: Container(
color: Colors.cyan,
height: 200.0,
width: 200.0,
child: Text("hello ")),
),
);
}
}
Probably this question is not actual for the author. (Asked 7 months ago).
But maybe my answer will help someone else.
Usually Slide Transition is used for transitions between pages. That is why, one unit of position value here is the size of one page. When you put there Offset(400.0,300.0) it's equal 400 screen right, and 300 pages down.
For your case it better to use AnimatedPositioned Widget.
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
backgroundColor: Colors.blue,
body: MoveContainer(),
),
);
}
}
class MoveContainer extends StatefulWidget {
#override
_MoveContainerState createState() => _MoveContainerState();
}
class _MoveContainerState extends State<MoveContainer> {
Offset offset = Offset.zero;
final double height = 200;
final double width = 200;
#override
Widget build(BuildContext context) {
return GestureDetector(
onPanStart: (details) {
RenderBox getBox = context.findRenderObject();
setState(() {
offset = getBox.localToGlobal(details.globalPosition);
});
},
child: Stack(
children: <Widget>[
AnimatedPositioned(
duration: Duration(milliseconds: 300),
top: offset.dy - (height / 2),
left: offset.dx - (width / 2),
child: Container(
color: Colors.cyan,
height: height,
width: width,
child: Text("hello "),
),
),
],
),
);
}
}

Flutter custom animated dialog

I'm trying to animate a custom dialog box in dart so that when it pops up it create some animations. There is a library in Android that is having animated dialog boxes, is there any similar library in Flutter Sweet Alert Dialog
how can we achieve the same functionality in flutter?
To create dialog boxes you can use the Overlay or Dialog classes. If you want to add animations like in the given framework you can use the AnimationController like in the following example. The CurvedAnimation class is used to create the bouncing effect on the animation.
Update: In general it is better to show dialogs with the showDialog function, because the closing and gesture are handled by Flutter. I have updated the example, it is now running with showDialog and you are able to close the dialog by tapping on the background.
You can copy & paste the following code into a new project and adjust it. It is runnable on it's own.
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(title: 'Flutter Demo', theme: ThemeData(), home: Page());
}
}
class Page extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: RaisedButton.icon(
onPressed: () {
showDialog(
context: context,
builder: (_) => FunkyOverlay(),
);
},
icon: Icon(Icons.message),
label: Text("PopUp!")),
),
);
}
}
class FunkyOverlay extends StatefulWidget {
#override
State<StatefulWidget> createState() => FunkyOverlayState();
}
class FunkyOverlayState extends State<FunkyOverlay>
with SingleTickerProviderStateMixin {
AnimationController controller;
Animation<double> scaleAnimation;
#override
void initState() {
super.initState();
controller =
AnimationController(vsync: this, duration: Duration(milliseconds: 450));
scaleAnimation =
CurvedAnimation(parent: controller, curve: Curves.elasticInOut);
controller.addListener(() {
setState(() {});
});
controller.forward();
}
#override
Widget build(BuildContext context) {
return Center(
child: Material(
color: Colors.transparent,
child: ScaleTransition(
scale: scaleAnimation,
child: Container(
decoration: ShapeDecoration(
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0))),
child: Padding(
padding: const EdgeInsets.all(50.0),
child: Text("Well hello there!"),
),
),
),
),
);
}
}
Just use 'showGeneralDialog()' No need use extra lib or widget.
You can get more animatated dialog reference from This Link
void _openCustomDialog() {
showGeneralDialog(barrierColor: Colors.black.withOpacity(0.5),
transitionBuilder: (context, a1, a2, widget) {
return Transform.scale(
scale: a1.value,
child: Opacity(
opacity: a1.value,
child: AlertDialog(
shape: OutlineInputBorder(
borderRadius: BorderRadius.circular(16.0)),
title: Text('Hello!!'),
content: Text('How are you?'),
),
),
);
},
transitionDuration: Duration(milliseconds: 200),
barrierDismissible: true,
barrierLabel: '',
context: context,
pageBuilder: (context, animation1, animation2) {});
}
I tried to do the animation shown in your gif. Gonna post the code to help people who want it, its not perfect so if anyone wants to help improving it go for it.
How it looks:
Code:
import 'package:flutter/material.dart';
import 'package:angles/angles.dart';
import 'dart:math';
import 'dart:core';
class CheckAnimation extends StatefulWidget {
final double size;
final VoidCallback onComplete;
CheckAnimation({this.size, this.onComplete});
#override
_CheckAnimationState createState() => _CheckAnimationState();
}
class _CheckAnimationState extends State<CheckAnimation>
with SingleTickerProviderStateMixin {
AnimationController _controller;
Animation<double> curve;
#override
void initState() {
// TODO: implement initState
super.initState();
_controller =
AnimationController(duration: Duration(seconds: 2), vsync: this);
curve = CurvedAnimation(parent: _controller, curve: Curves.bounceInOut);
_controller.addListener(() {
setState(() {});
if(_controller.status == AnimationStatus.completed && widget.onComplete != null){
widget.onComplete();
}
});
_controller.forward();
}
#override
Widget build(BuildContext context) {
return Container(
height: widget.size ?? 100,
width: widget.size ?? 100,
color: Colors.transparent,
child: CustomPaint(
painter: CheckPainter(value: curve.value),
),
);
}
#override
void dispose() {
// TODO: implement dispose
_controller.dispose();
super.dispose();
}
}
class CheckPainter extends CustomPainter {
Paint _paint;
double value;
double _length;
double _offset;
double _secondOffset;
double _startingAngle;
CheckPainter({this.value}) {
_paint = Paint()
..color = Colors.greenAccent
..strokeWidth = 5.0
..strokeCap = StrokeCap.round
..style = PaintingStyle.stroke;
assert(value != null);
_length = 60;
_offset = 0;
_startingAngle = 205;
}
#override
void paint(Canvas canvas, Size size) {
// Background canvas
var rect = Offset(0, 0) & size;
_paint.color = Colors.greenAccent.withOpacity(.05);
double line1x1 = size.width / 2 +
size.width * cos(Angle.fromDegrees(_startingAngle).radians) * .5;
double line1y1 = size.height / 2 +
size.height * sin(Angle.fromDegrees(_startingAngle).radians) * .5;
double line1x2 = size.width * .45;
double line1y2 = size.height * .65;
double line2x1 =
size.width / 2 + size.width * cos(Angle.fromDegrees(320).radians) * .35;
double line2y1 = size.height / 2 +
size.height * sin(Angle.fromDegrees(320).radians) * .35;
canvas.drawArc(rect, Angle.fromDegrees(_startingAngle).radians,
Angle.fromDegrees(360).radians, false, _paint);
canvas.drawLine(Offset(line1x1, line1y1), Offset(line1x2, line1y2), _paint);
canvas.drawLine(Offset(line2x1, line2y1), Offset(line1x2, line1y2), _paint);
// animation painter
double circleValue, checkValue;
if (value < .5) {
checkValue = 0;
circleValue = value / .5;
} else {
checkValue = (value - .5) / .5;
circleValue = 1;
}
_paint.color = const Color(0xff72d0c3);
double firstAngle = _startingAngle + 360 * circleValue;
canvas.drawArc(
rect,
Angle.fromDegrees(firstAngle).radians,
Angle.fromDegrees(
getSecondAngle(firstAngle, _length, _startingAngle + 360))
.radians,
false,
_paint);
double line1Value = 0, line2Value = 0;
if (circleValue >= 1) {
if (checkValue < .5) {
line2Value = 0;
line1Value = checkValue / .5;
} else {
line2Value = (checkValue - .5) / .5;
line1Value = 1;
}
}
double auxLine1x1 = (line1x2 - line1x1) * getMin(line1Value, .8);
double auxLine1y1 =
(((auxLine1x1) - line1x1) / (line1x2 - line1x1)) * (line1y2 - line1y1) +
line1y1;
if (_offset < 60) {
auxLine1x1 = line1x1;
auxLine1y1 = line1y1;
}
double auxLine1x2 = auxLine1x1 + _offset / 2;
double auxLine1y2 =
(((auxLine1x1 + _offset / 2) - line1x1) / (line1x2 - line1x1)) *
(line1y2 - line1y1) +
line1y1;
if (checkIfPointHasCrossedLine(Offset(line1x2, line1y2),
Offset(line2x1, line2y1), Offset(auxLine1x2, auxLine1y2))) {
auxLine1x2 = line1x2;
auxLine1y2 = line1y2;
}
if (_offset > 0) {
canvas.drawLine(Offset(auxLine1x1, auxLine1y1),
Offset(auxLine1x2, auxLine1y2), _paint);
}
// SECOND LINE
double auxLine2x1 = (line2x1 - line1x2) * line2Value;
double auxLine2y1 =
((((line2x1 - line1x2) * line2Value) - line1x2) / (line2x1 - line1x2)) *
(line2y1 - line1y2) +
line1y2;
if (checkIfPointHasCrossedLine(Offset(line1x1, line1y1),
Offset(line1x2, line1y2), Offset(auxLine2x1, auxLine2y1))) {
auxLine2x1 = line1x2;
auxLine2y1 = line1y2;
}
if (line2Value > 0) {
canvas.drawLine(
Offset(auxLine2x1, auxLine2y1),
Offset(
(line2x1 - line1x2) * line2Value + _offset * .75,
((((line2x1 - line1x2) * line2Value + _offset * .75) - line1x2) /
(line2x1 - line1x2)) *
(line2y1 - line1y2) +
line1y2),
_paint);
}
}
double getMax(double x, double y) {
return (x > y) ? x : y;
}
double getMin(double x, double y) {
return (x > y) ? y : x;
}
bool checkIfPointHasCrossedLine(Offset a, Offset b, Offset point) {
return ((b.dx - a.dx) * (point.dy - a.dy) -
(b.dy - a.dy) * (point.dx - a.dx)) >
0;
}
double getSecondAngle(double angle, double plus, double max) {
if (angle + plus > max) {
_offset = angle + plus - max;
return max - angle;
} else {
_offset = 0;
return plus;
}
}
#override
bool shouldRepaint(CheckPainter old) {
return old.value != value;
}
}
I used angles package
Whenever you want to show Dialog with some Animation, the best way is to use showGeneralDialog()
NOTE: ALL PARAMETERS MUST BE PROVIDED OTHERWISE SOME ERROR WILL OCCUR.
showGeneralDialog(
barrierColor: Colors.black.withOpacity(0.5), //SHADOW EFFECT
transitionBuilder: (context, a1, a2, widget) {
return Center(
child: Container(
height: 100.0 * a1.value, // USE PROVIDED ANIMATION
width: 100.0 * a1.value,
color: Colors.blue,
),
);
},
transitionDuration: Duration(milliseconds: 200), // DURATION FOR ANIMATION
barrierDismissible: true,
barrierLabel: 'LABEL',
context: context,
pageBuilder: (context, animation1, animation2) {
return Text('PAGE BUILDER');
});
}, child: Text('Show Dialog'),),
If you need more customization, then extend PopupRoute and create your own _DialogRoute<T> and showGeneralDialog()
EDIT
Edited answer of Niklas with functionality to close Overlay :)
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(title: 'Flutter Demo', theme: ThemeData(), home: Page());
}
}
class Page extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: RaisedButton.icon(
onPressed: () {
OverlayEntry overlayEntry;
overlayEntry = OverlayEntry(builder: (c) {
return FunkyOverlay(onClose: () => overlayEntry.remove());
});
Overlay.of(context).insert(overlayEntry);
},
icon: Icon(Icons.message),
label: Text("PopUp!")),
),
);
}
}
class FunkyOverlay extends StatefulWidget {
final VoidCallback onClose;
const FunkyOverlay({Key key, this.onClose}) : super(key: key);
#override
State<StatefulWidget> createState() => FunkyOverlayState();
}
class FunkyOverlayState extends State<FunkyOverlay>
with SingleTickerProviderStateMixin {
AnimationController controller;
Animation<double> opacityAnimation;
Animation<double> scaleAnimatoin;
#override
void initState() {
super.initState();
controller =
AnimationController(vsync: this, duration: Duration(milliseconds: 450));
opacityAnimation = Tween<double>(begin: 0.0, end: 0.4).animate(
CurvedAnimation(parent: controller, curve: Curves.fastOutSlowIn));
scaleAnimatoin =
CurvedAnimation(parent: controller, curve: Curves.elasticInOut);
controller.addListener(() {
setState(() {});
});
controller.forward();
}
#override
Widget build(BuildContext context) {
return Material(
color: Colors.black.withOpacity(opacityAnimation.value),
child: Center(
child: ScaleTransition(
scale: scaleAnimatoin,
child: Container(
decoration: ShapeDecoration(
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0))),
child: Padding(
padding: const EdgeInsets.all(50.0),
child: OutlineButton(onPressed: widget.onClose, child: Text('Close!'),),
),
),
),
),
);
}
}
Add the below function in your code which shows a animated Dialog with Scale and Fade transition
void _openCustomDialog(BuildContext context) {
showGeneralDialog(
barrierColor: Colors.black.withOpacity(0.5),
transitionBuilder: (context, a1, a2, widget) {
return ScaleTransition(
scale: Tween<double>(begin: 0.5, end: 1.0).animate(a1),
child: FadeTransition(
opacity: Tween<double>(begin: 0.5, end: 1.0).animate(a1),
child: const AboutPasteLog(
title: 'About',
),
));
},
transitionDuration: const Duration(milliseconds: 300),
barrierDismissible: true,
barrierLabel: '',
context: context,
pageBuilder: (context, animation1, animation2) {
return Container();
});
}
And invoke it as below
onPressed:(){
_openCustomDialog(context);
}
The final result

Flutter - Set the scale of a widget

I am trying to create an animation that "pops" the widget to the front and returns to it
import "package:flutter/material.dart";
class ScoreCounter extends StatefulWidget {
#override
_ScoreCounter createState() => new _ScoreCounter();
}
class _ScoreCounter extends State<ScoreCounter> with SingleTickerProviderStateMixin{
AnimationController _controller;
#override
void initState() {
super.initState();
_controller = new AnimationController(
duration: const Duration(seconds: 10),
vsync: this,
)..forward();
}
#override
build(BuildContext context){
return new AnimatedBuilder(
animation: _controller,
child: new Container(width: 200.0, height: 200.0, color: Colors.green),
builder: (BuildContext context, Widget child) {
//What to return that scales the element
},
);
}
}
For rotating, I would use a Transform and return a Matrix. But what should I return to accomplish the scaling animation?
Thanks in advance
Just as a alternative, to set just scale for a specific Widget.
Transform.scale(
scale: 1.0,
child: Container(
height: 200.0,
width: 200.0,
color: Colors.pink,
),
),
If you're going to size your contents manually you could just read controller.value in your builder function and use that to set the size of the container.
Alternatively, you could consider a pair of SizeTransitions for each axis. The Align class may also be useful because you can set a sizeFactor in each dimension.
import 'package:flutter/material.dart';
void main() {
runApp(new MaterialApp(
home: new HomePage(),
));
}
class HomePage extends StatefulWidget {
HomePageState createState() => new HomePageState();
}
class HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return new Scaffold(
floatingActionButton: new FloatingActionButton(
child: new Icon(Icons.sort),
onPressed: () {},
),
body: new Center(
child: new ScoreCounter(),
),
);
}
}
class ScoreCounter extends StatefulWidget {
#override
_ScoreCounter createState() => new _ScoreCounter();
}
class _ScoreCounter extends State<ScoreCounter>
with SingleTickerProviderStateMixin {
AnimationController _controller;
#override
void initState() {
super.initState();
_controller = new AnimationController(
duration: const Duration(seconds: 1),
vsync: this,
)
..forward();
}
#override
build(BuildContext context){
return new AnimatedBuilder(
animation: _controller,
builder: (BuildContext context, Widget child) {
double size = _controller.value * 200.0;
return new Container(width: size, height: size, color: Colors.green);
},
);
}
}
Another Approach is to create generalized Scale Transformation.
Simply add this method to your component
Matrix4 scaleXYZTransform({
double scaleX = 1.00,
double scaleY = 1.00,
double scaleZ = 1.00,
}) {
return Matrix4.diagonal3Values(scaleX, scaleY, scaleZ);
}
Now you can easily Scale any widget by wrapping it:
Transform(
transform: scaleXYZTransform(scaleX: .5, scaleY: .5),
child: Container(
child: myAwesomeWidget(),
));

How to use an AnimatedContainer for animated transforms (ex. Scale)

I'm looking to animate the scale of a container using the transform property of an AnimatedContainer; however, the scale is not being transitioned and jumps directly from start to end.
Code Snippet:
var container = new AnimatedContainer(
duration: const Duration(milliseconds: 200),
width: 50.0,
height: 50.0,
// selected is a bool that will be toggled
transform: selected ? new Matrix4.identity() : new Matrix4.identity().scaled(0.2,0.2),
decoration: new BoxDecoration(
shape: BoxShape.circle,
backgroundColor: Colors.blue[500],
),
child: new Center(
child: new Icon(
Icons.check,
color: Colors.white,
),
)
);
Any insight on what's going on?
AnimatedContainer supports animting it's transform value, as follow:
/// scale to 95%, centerred
final width = 200.0;
final height = 300.0;
bool shouldScaleDown = true;// change value when needed
AnimatedContainer(
color: Colors.blueAccent,
width: width,
height: height,
duration: Duration(milliseconds: 100),
transform: (shouldScaleDown
? (Matrix4.identity()
..translate(0.025 * width, 0.025 * height)// translate towards right and down
..scale(0.95, 0.95))// scale with to 95% anchorred at topleft of the AnimatedContainer
: Matrix4.identity()),
child: Container(),
);
I'm afraid transform is one of the properties we don't animate (child is another). If you want to animate the scale, you can use ScaleTransition.
ScaleTransition: https://docs.flutter.io/flutter/widgets/ScaleTransition-class.html
Bug for Matrix lerp: https://github.com/flutter/flutter/issues/443
you can Animate using the Animated Builder,the below code will scale a Text from font Size 20-35 in 4 secs let me break this into steps to make you better understand
1.you need to implement your class from TickerProviderStateMixin.
2.You need an AnimationController and a Animation variables;
3.wrap your widget inside an AnimatedBuilder (note AnimatedBuilder must return a Widget at least a container) and add a controller to the animation as
animation: _controller,
and builder Which returns the AnimatedWidget
4.In the init method intialize the controller with vsync and the Duration of Animation.
and the animation with the Tweenit takes begin and end values which define the initial and final values of your Widget to be animated
for me, in this case, it was text widget so the begin and end Values will be corresponding to the fontSize.which receives variable values as animation.value
5.Finally How do you want the animation effect To be will be specified by the animate which takes in controller and the Curve effect
Here is a Working example
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<SplashScreen>
with TickerProviderStateMixin {
AnimationController _controller;
Animation _animation;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xFF005CA9),
body: Center(
child: AnimatedBuilder(
animation: _controller,
builder: (BuildContext context, Widget child) {
return Container(
child: Text(
'Hello World',
style: TextStyle(
color: Colors.white,
fontSize: _animation.value,
),
),
);
},
),
));
}
void initState() {
super.initState();
_controller =
AnimationController(vsync: this, duration: Duration(seconds: 4));
_animation = Tween<double>(
begin: 20,
end: 35,
).animate(
CurvedAnimation(
parent: _controller,
curve: Curves.ease,
),
);
_controller.forward();
}
}
the code above produces the following output

Resources