Overview
AnimatedAlign is a widget that automatically animates the transition when its child widget’s alignment changes. It provides smooth transitions for position changes. This widget is inspired by Flutter’s AnimatedAlign widget.
Animated version of Align which automatically transitions the child’s position over a given duration whenever the given alignment changes.
Flutter reference: https://api.flutter.dev/flutter/widgets/AnimatedAlign-class.html
When to use?
- When you want to smoothly move a widget’s position
- When dynamically changing widget alignment based on user interaction
- When you want to provide visual continuity during layout changes
- When transitioning positions of floating buttons or popups with animation
Basic usage
import { AnimatedAlign, Alignment, Container, StatefulWidget } from '@flitter/core';
class AlignmentExample extends StatefulWidget {
createState() {
return new AlignmentExampleState();
}
}
class AlignmentExampleState extends State<AlignmentExample> {
alignment = Alignment.topLeft;
build() {
return GestureDetector({
onTap: () => {
this.setState(() => {
// Change alignment position on tap
this.alignment = this.alignment === Alignment.topLeft
? Alignment.bottomRight
: Alignment.topLeft;
});
},
child: AnimatedAlign({
alignment: this.alignment,
duration: 1000, // 1 second
child: Container({
width: 50,
height: 50,
color: "blue",
}),
}),
});
}
}
Props
alignment (required)
Value: Alignment
Specifies the alignment position of the child widget. Predefined values:
Alignment.topLeft
: Top left (-1, -1)Alignment.topCenter
: Top center (0, -1)Alignment.topRight
: Top right (1, -1)Alignment.centerLeft
: Center left (-1, 0)Alignment.center
: Center (0, 0)Alignment.centerRight
: Center right (1, 0)Alignment.bottomLeft
: Bottom left (-1, 1)Alignment.bottomCenter
: Bottom center (0, 1)Alignment.bottomRight
: Bottom right (1, 1)
Custom alignment is also possible:
Alignment.of({ x: 0.5, y: -0.5 }) // x: -1~1, y: -1~1
duration (required)
Value: number
Specifies the animation duration in milliseconds.
curve (optional)
Value: Curve (default: Curves.linear)
Specifies the animation progression curve. Available curves:
Curves.linear
: Constant speedCurves.easeIn
: Slow startCurves.easeOut
: Slow endCurves.easeInOut
: Slow start and endCurves.circIn
: Circular acceleration startCurves.circOut
: Circular deceleration endCurves.circInOut
: Circular acceleration/decelerationCurves.backIn
: Goes back then startsCurves.backOut
: Overshoots target then returnsCurves.backInOut
: backIn + backOutCurves.anticipate
: Anticipatory motion before proceedingCurves.bounceIn
: Bounces at startCurves.bounceOut
: Bounces at endCurves.bounceInOut
: Bounces at start/end
widthFactor (optional)
Value: number | undefined
Multiplier for the child widget’s width. If not specified, uses the full width of the parent.
heightFactor (optional)
Value: number | undefined
Multiplier for the child widget’s height. If not specified, uses the full height of the parent.
child (optional)
Value: Widget | undefined
The child widget to be aligned.
key (optional)
Value: any
A unique identifier for the widget.
Real-world examples
Example 1: Interactive floating button
import { AnimatedAlign, Alignment, Container, FloatingActionButton, Curves } from '@flitter/core';
class InteractiveFloatingButton extends StatefulWidget {
createState() {
return new InteractiveFloatingButtonState();
}
}
class InteractiveFloatingButtonState extends State<InteractiveFloatingButton> {
isExpanded = false;
build() {
return Stack({
children: [
// Main content
Container({
color: "lightgray",
}),
// Animated floating button
AnimatedAlign({
alignment: this.isExpanded
? Alignment.center
: Alignment.bottomRight,
duration: 500,
curve: Curves.easeInOut,
child: Padding({
padding: EdgeInsets.all(16),
child: FloatingActionButton({
onPressed: () => {
this.setState(() => {
this.isExpanded = !this.isExpanded;
});
},
child: Icon(this.isExpanded ? Icons.close : Icons.add),
}),
}),
}),
],
});
}
}
Example 2: Multi-position transition animation
import { AnimatedAlign, Alignment, Container, Row, Column, Curves } from '@flitter/core';
class MultiPositionAnimation extends StatefulWidget {
createState() {
return new MultiPositionAnimationState();
}
}
class MultiPositionAnimationState extends State<MultiPositionAnimation> {
currentPosition = 0;
positions = [
Alignment.topLeft,
Alignment.topRight,
Alignment.bottomRight,
Alignment.bottomLeft,
Alignment.center,
];
build() {
return Column({
children: [
// Control buttons
Row({
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton({
onPressed: () => this.moveToNext(),
child: Text("Next Position"),
}),
ElevatedButton({
onPressed: () => this.moveToCenter(),
child: Text("To Center"),
}),
],
}),
// Animation area
Expanded({
child: Container({
color: "#f0f0f0",
child: AnimatedAlign({
alignment: this.positions[this.currentPosition],
duration: 800,
curve: Curves.anticipate,
child: Container({
width: 80,
height: 80,
decoration: BoxDecoration({
color: "purple",
borderRadius: BorderRadius.circular(40),
}),
child: Center({
child: Text(
(this.currentPosition + 1).toString(),
style: TextStyle({ color: "white", fontSize: 24 }),
),
}),
}),
}),
}),
}),
],
});
}
moveToNext() {
this.setState(() => {
this.currentPosition = (this.currentPosition + 1) % this.positions.length;
});
}
moveToCenter() {
this.setState(() => {
this.currentPosition = 4; // center position
});
}
}
Example 3: Using widthFactor and heightFactor
import { AnimatedAlign, Alignment, Container, Curves } from '@flitter/core';
class FactorAnimation extends StatefulWidget {
createState() {
return new FactorAnimationState();
}
}
class FactorAnimationState extends State<FactorAnimation> {
isCompact = false;
build() {
return GestureDetector({
onTap: () => {
this.setState(() => {
this.isCompact = !this.isCompact;
});
},
child: Container({
width: 300,
height: 300,
color: "lightblue",
child: AnimatedAlign({
alignment: Alignment.center,
widthFactor: this.isCompact ? 0.5 : 1.0,
heightFactor: this.isCompact ? 0.5 : 1.0,
duration: 600,
curve: Curves.bounceOut,
child: Container({
width: 100,
height: 100,
color: "red",
child: Center({
child: Text(
this.isCompact ? "Shrink" : "Expand",
style: TextStyle({ color: "white" }),
),
}),
}),
}),
}),
});
}
}
Notes
- Animation only occurs when the alignment value changes
- widthFactor and heightFactor affect the child widget’s size and also influence the parent widget’s size constraints
- If a new alignment value is set during animation, it smoothly transitions from the current position to the new target position
- If duration is 0, the widget moves immediately without animation
Related widgets
- Align: Basic widget for aligning children without animation
- AnimatedPositioned: Animates position transitions within a Stack
- AnimatedContainer: Animates multiple properties simultaneously
- AnimatedPadding: Animates padding transitions