7.5 KiB
7.5 KiB
| name | description |
|---|---|
| features-implicit-animations | Flutter implicit animations - AnimatedContainer, AnimatedOpacity, AnimatedSize, and other implicit animation widgets |
Implicit Animations
Implicit animations automatically animate property changes. They're simpler to use than explicit animations but less flexible.
AnimatedContainer
Automatically animates changes to Container properties.
class AnimatedContainerExample extends StatefulWidget {
const AnimatedContainerExample({super.key});
@override
State<AnimatedContainerExample> createState() => _AnimatedContainerExampleState();
}
class _AnimatedContainerExampleState extends State<AnimatedContainerExample> {
bool _expanded = false;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
setState(() {
_expanded = !_expanded;
});
},
child: AnimatedContainer(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
width: _expanded ? 200 : 100,
height: _expanded ? 200 : 100,
decoration: BoxDecoration(
color: _expanded ? Colors.blue : Colors.red,
borderRadius: BorderRadius.circular(_expanded ? 20 : 10),
),
child: Center(
child: Text(_expanded ? 'Expanded' : 'Collapsed'),
),
),
);
}
}
AnimatedOpacity
Animates opacity changes.
class AnimatedOpacityExample extends StatefulWidget {
const AnimatedOpacityExample({super.key});
@override
State<AnimatedOpacityExample> createState() => _AnimatedOpacityExampleState();
}
class _AnimatedOpacityExampleState extends State<AnimatedOpacityExample> {
bool _visible = true;
@override
Widget build(BuildContext context) {
return Column(
children: [
AnimatedOpacity(
opacity: _visible ? 1.0 : 0.0,
duration: const Duration(milliseconds: 300),
child: const FlutterLogo(size: 100),
),
ElevatedButton(
onPressed: () {
setState(() {
_visible = !_visible;
});
},
child: Text(_visible ? 'Hide' : 'Show'),
),
],
);
}
}
AnimatedSize
Animates size changes.
class AnimatedSizeExample extends StatefulWidget {
const AnimatedSizeExample({super.key});
@override
State<AnimatedSizeExample> createState() => _AnimatedSizeExampleState();
}
class _AnimatedSizeExampleState extends State<AnimatedSizeExample> {
bool _expanded = false;
@override
Widget build(BuildContext context) {
return Column(
children: [
AnimatedSize(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
child: Container(
width: _expanded ? 200 : 100,
height: _expanded ? 200 : 100,
color: Colors.blue,
child: _expanded
? const Center(child: Text('Expanded'))
: const Center(child: Text('Small')),
),
),
ElevatedButton(
onPressed: () {
setState(() {
_expanded = !_expanded;
});
},
child: Text(_expanded ? 'Collapse' : 'Expand'),
),
],
);
}
}
AnimatedCrossFade
Cross-fades between two children.
class AnimatedCrossFadeExample extends StatefulWidget {
const AnimatedCrossFadeExample({super.key});
@override
State<AnimatedCrossFadeExample> createState() => _AnimatedCrossFadeExampleState();
}
class _AnimatedCrossFadeExampleState extends State<AnimatedCrossFadeExample> {
bool _showFirst = true;
@override
Widget build(BuildContext context) {
return Column(
children: [
AnimatedCrossFade(
duration: const Duration(milliseconds: 300),
firstChild: Container(
width: 200,
height: 200,
color: Colors.red,
child: const Center(child: Text('First')),
),
secondChild: Container(
width: 200,
height: 200,
color: Colors.blue,
child: const Center(child: Text('Second')),
),
crossFadeState: _showFirst
? CrossFadeState.showFirst
: CrossFadeState.showSecond,
),
ElevatedButton(
onPressed: () {
setState(() {
_showFirst = !_showFirst;
});
},
child: const Text('Toggle'),
),
],
);
}
}
AnimatedSwitcher
Switches between children with a transition.
class AnimatedSwitcherExample extends StatefulWidget {
const AnimatedSwitcherExample({super.key});
@override
State<AnimatedSwitcherExample> createState() => _AnimatedSwitcherExampleState();
}
class _AnimatedSwitcherExampleState extends State<AnimatedSwitcherExample> {
int _counter = 0;
@override
Widget build(BuildContext context) {
return Column(
children: [
AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
transitionBuilder: (child, animation) {
return ScaleTransition(
scale: animation,
child: child,
);
},
child: Text(
'$_counter',
key: ValueKey(_counter),
style: const TextStyle(fontSize: 48),
),
),
ElevatedButton(
onPressed: () {
setState(() {
_counter++;
});
},
child: const Text('Increment'),
),
],
);
}
}
AnimatedPositioned
Animates position changes within a Stack.
class AnimatedPositionedExample extends StatefulWidget {
const AnimatedPositionedExample({super.key});
@override
State<AnimatedPositionedExample> createState() => _AnimatedPositionedExampleState();
}
class _AnimatedPositionedExampleState extends State<AnimatedPositionedExample> {
bool _moved = false;
@override
Widget build(BuildContext context) {
return Stack(
children: [
AnimatedPositioned(
duration: const Duration(milliseconds: 300),
left: _moved ? 200 : 0,
top: _moved ? 200 : 0,
child: Container(
width: 100,
height: 100,
color: Colors.blue,
),
),
Positioned(
bottom: 20,
left: 20,
child: ElevatedButton(
onPressed: () {
setState(() {
_moved = !_moved;
});
},
child: const Text('Move'),
),
),
],
);
}
}
Animation Curves
Use curves to control animation timing.
AnimatedContainer(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut, // Common curves: linear, easeIn, easeOut, easeInOut, bounce, elastic
// ... properties
)
Key Points
- Implicit animations automatically animate property changes
- Use
AnimatedContainerfor animating container properties - Use
AnimatedOpacityfor fade in/out effects - Use
AnimatedSizefor size changes - Use
AnimatedCrossFadeto transition between two widgets - Use
AnimatedSwitcherto switch between children - Use
AnimatedPositionedfor position animations in Stack - Specify
durationandcurvefor timing control - Implicit animations are simpler but less flexible than explicit animations