I have a Flutter app that is showing some Lottie animations. I sometimes need to flip the animation by 180 degrees on Y axis so that it it is a mirror image of itself.
In C# this is easily achievable by setting animated visual player's plane projection rotationY property to 180 (see below).
<muxc:AnimatedVisualPlayer x:Name="LottiePlayer">
<muxc:AnimatedVisualPlayer.Projection>
<PlaneProjection RotationY="180" x:Name="LottiePayerRotation"/>
</muxc:AnimatedVisualPlayer.Projection>
In flutter I tried using RotationBox, but that only rotates around X axis. I need to rotate around Y axis (see image below).
I also tried wrapping Lottie animation inside Transform widget (see below), but that doesn't work. After I added that, the Lottie animation completely disappears. I don't quite understand how Matrix4 works, there is very little documentation on it. I found this Matrix4 explanation but I still don't understand it. :-(
Transform(
transform: Matrix4(
1,0,0,0,
0,1,0,0,
0,0,1,0,
0,0,0,1,
)..rotateX(0)..rotateY(180)..rotateZ(0),
child: Lottie(
frameRate: FrameRate.max,
composition: _composition,
controller: _controller,
),
),
Note that I do not need the flip to be animated, I just want to flip the Lottie animation instantly so that it looks like a mirror image of itself. So an instant change, not a transition animated.
Any help appreciated.
I have used something like this before to rotate items... not sure if its what you are after
child: Transform.rotate(
angle: 180 / Math.pi, // Rotations are supplied in radians
child: Container())
Turns out I almost had it! What was missing was the alignment. The default value was causing the rotation to get outside the visible area. Changing alignment to "center" fixed that:
child: Transform(
alignment: FractionalOffset.center,
transform: Matrix4(
1,0,0,0,
0,1,0,0,
0,0,1,0,
0,0,0,1,
)..rotateY(180),
child: Lottie(
frameRate: FrameRate.max,
composition: _composition,
controller: _controller,
),
Simply wrap your widget with RotatedBox & give quarterTurns value(0, 1, 2, 3)
Assuming my widget is image of right arrow : →
0 no rotation - →
1 90 rotation - ↓
2 180 rotation - ←
3 270 rotation - ↑
So if I want my image to face left side ←:
RotatedBox(
quarterTurns: 2,
child: Image.asset('assets/right_arrow.png'),
),
EDIT
To rotate on axis, you need to use Transform widget.
Give the transform property as Matrix4
NOTE: You have to give rotation in radians.
import 'dart:math' as math;
Transform(
// to mirror, rotate Y axis
// math.pi is 180 degree in radian
transform: Matrix4.rotationY(math.pi),
child: Text('Hello world!'),
),
Related
Please tell me, is it possible to locate list of widgets along the curve? Like on picture, Let's say teeth are widgets.
If you don't want to use Stack, then you might try out the CustomPaint widget and drawing in flutter with paths.
There's a tutorial to painting: https://medium.com/flutter-community/paths-in-flutter-a-visual-guide-6c906464dcd0
However, painting is a limited thing and if you have an actual widgets that need to be fully responsive and act as a widgets, you need to use Stack with Positioned. That's the only way to go.
Also, in this question about animating along the curve in flutter you can find some code examples that use both: painting and stack and even animate that. Possibly the most useful code from there is calculating the position of an object that you want to place along some curve:
Offset calculate(path, along) {
PathMetrics pathMetrics = path.computeMetrics();
PathMetric pathMetric = pathMetrics.elementAt(0);
along = pathMetric.length * along;
Tangent pos = pathMetric.getTangentForOffset(along);
return pos.position;
}
In above example you need to have some Path which is a representation of some curve. For example you can get one like this:
Path getPath(){
Size size = Size(300,300);
Path path = Path();
path.moveTo(0, size.height / 2);
path.quadraticBezierTo(size.width / 2, size.height, size.width, size.height / 2);
return path;
}
then you give this path and some along, which is number between 0 and 1, which represents beginning and ending of this curve and you give it to the calculate, which return and Offset, which you can use in your Positioned inside your Stack:
Positioned(
top: calculate(path, along).dy,
left: calculate(path, along).dx,
child: ...
When you only want some shapes to be drawn on a screen then you can use simply painting with CustomPaint widget, but this objects won't be widgets
I am struggling to understand the properties delta, globalPosition, localPosition and primaryDelta of DragUpdateDetails. I read the documentation for DragUpdateDetails but it didn't really help much.
I found some question asked: What is primaryDelta and delta in DragUpdateDetails and What is the difference between globalposition and localposition in flutter?. First one has no answer whereas second one only has explanation for globalPosition and localPosition.
This is a sample code for dragging a container:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
double _xPosition = 0;
double _yPosition = 0;
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: SafeArea(
child: Container(
child: Stack(
children: [
Positioned(
top: _yPosition,
left: _xPosition,
child: GestureDetector(
onPanUpdate: (DragUpdateDetails e) {
setState(() {
_xPosition += e.delta.dx;
_yPosition += e.delta.dy;
});
},
child: Container(
height: 200,
width: 200,
color: Colors.black,
),
),
)
],
),
),
),
),
);
}
}
In this example why are we using delta but not globalPosition or localPosition. I mean of course dragging will not work as expected but how to know when to use delta or globalPosition or localPosition?
Can someone provide me a quick explanation for these properties? It would really mean a lot. Thanks!
According to the Source code of DragUpdateDetailsclass:
delta → Offset
The amount the pointer has moved in the coordinate space of the event receiver since the previous update
Meaning, The distance covered by dragging since the last pointer contact. Delta gives dx for horizontal distance and dy for vertical distance.
primaryDelta → double
The amount the pointer has moved along the primary axis in the coordinate space of the event receiver since the previous update
primaryDelta gives the absolute distance in only one primary direction of dragging, meaning if the drag is primarily in horizontal axis(GestureDragUpdateCallback + Horizontal only) then this value represents the drag distance in the horizontal axis. If the drag in is vertical axis (GestureDragUpdateCallback + Vertical only) then this value represents the drag amount in the vertical axis.
Note: if the GestureDragUpdateCallback is for a two-dimensional drag (e.g., a pan), then this value is null.
globalPosition → Offset
The pointer's global position when it triggered this update.
The position of the pointer on the screen with reference to the whole screen area and origin point at the top-left corner of the screen. Global Position gives x for horizontal co-ordinate and y for vertical co-ordinate
localPosition → Offset
The local position in the coordinate system of the event receiver at which the pointer contacted the screen.
The position of the pointer just like the global position, except the referential frame being the widget/render object instead of the whole screen. Here, The widget is the one that has received the pointer contact.
Explanation with respect to the example code
According to the example code that is provided, I can safely say that when you are dealing with the positioning of any draggable widget you must have an initial position.
For the initial position, globalPosition or localPosition can be used. Which one to use is specific to the widget tree and use of the app.
Once the initial position is set, you can use delta or primaryDelta to find the new position for the draggable widget to move to when dragged by following formula:
newXPosition = initialXPosition + (dx or primaryDelta in horizontal axis)
newYPosition = initialYPosition + (dy or primaryDelta in vertical axis)
One thing to keep in mind while using delta and primaryDelta is that, if the widget can/should recognize the drag events in both axis, only delta will be provided, primaryDelat will be null.
Otherwise, if only one direction drag is expected then using only primaryDelta will work as expected, at this point delta will have only one value as a non-zero value and other as 0 based on which direction the drag is to be recognized.
Let me know if you have any other questions about this in the comments.
I have a CustomPainter that can paint all sorts of visuals based on a physical model input parameters.
How can I make a 1 second animation that draws the needed frames between two different end points, essentially calling my CustomPainter to paint intermediate values between the two end points whenever a new frame can be drawn?
Container(
width: 800,
height: 500,
child: CustomPaint(
painter: MyPainter(
context,
inputVal: myProvider
),
))
Basically I want to make a function that runs a one second long sequence where values in myProvider change incrementally from start values to end values, and the CustomPaint redraws the visuals based on current values whenever a new frame is drawn. Is this possible?
Maybe check out tween animation. I think it should help you.
In android, we have overshoot interpolator for animation using Object animator which overshoots the end value of animation and then comes back to end value. Is there any alternative for flutter like in gif below.
According to #pskink help, Similar behaviour of overshoot interpolator is Curves.elasticOut. As I wanted to control oscillation too I used ElasticOutCurve() curve. below code solved the problem.
_clockWiseRotationAnimation =
Tween<double>(begin: 0.0, end: 2 * pi).animate(CurvedAnimation(parent: _controller, curve: ElasticOutCurve(1.0)));
I'm wondering if in Flutter there are any good ways of imitating the iOS Xcode constraint where you center a view inside another (say, vertically), and supply a multiplier such that instead of being exactly centered (50% of the way down the parent view), it's positioned at 30% down, or 70% down, or whatever.
(Rather than use a fixed margin from the top of the screen, I'd like to "float" a header view down by 20% of the screen height...)
FractionallySizedBox is enough by itself to handle such layout
FractionallySizedBox(
heightFactor: .5,
widthFactor: 1.0,
alignment: Alignment.topCenter,
child: child,
)
This will top center a widget taking helf the height of its parent and full width
All my FractionallySizedBox efforts have been unreliable, but here's a way that's proven far stabler for me - using LayoutBuilder and SizedBox as a spacer:
LayoutBuilder(builder: (context, constraints) => Column(
children: <Widget>[
SizedBox(height: (constraints.maxHeight - constraints.minHeight) * 0.2,),
myWidget
],
))
This way constraints give me the ability to calculate 20% of the parent height, and apply that as a spacing using a simple SizedBox.
Set in container of parent view
Container(alignment: Alignment.center, ...)
I found one way, but I'm not sure it's the neatest yet:
For vertical proportional centering:
Embed your layout inside a Center widget that is itself inside a FractionallySizedBox. Provided that FractionallySizedBox is at the top of the screen, by changing its heightFactor you effectively change the centering position caused by the Center widget.
new FractionallySizedBox(
heightFactor: someHeightFactor,
child: Center(
child: myChildWidget
),
);
i.e. if parentHeight = the height of the parent widget to this FractionallySizedBox, and parentY = the (absolute) y origin of that parent widget, then setting heightFactor = 0.6 would center your UI child inside a region measuring 0.6 * parentHeight, therefore with an absolute y center = parentY + 0.3 * parentHeight.
Horizontal proportional centering would be the same but using widthFactor on the FractionallySizedBox.
Use FractionallySizedBox to size a widget relative to all available space, and wrap it with a Container or Align to specify the alignment of it.
For example:
Container(
alignment: Alignment(0, -0.5),
child: FractionallySizedBox(
heightFactor: 0.5,
widthFactor: 0.8,
child: Container(color: Colors.blue),
),
)
This makes a blue box that's 50% of screen height and 80% of screen width, positioned at 25% down vertically.
Note, for the Alignment class, it takes in 2 parameters, for x and y axis alignment, ranges from -1 to +1. For example, (0,0) is center, (-1, -1) is top left corner, so here (0, -0.5) centers it horizontally and lifts it up half way vertically, resulting in 25% padding from the top.