This article addresses a common issue encountered when animating a conditionally hidden BottomAppBar
in Flutter using a SlideTransition
. It explains the cause of the placeholder widget and provides practical solutions to resolve the issue and achieve a smooth animation.
Problem Description
Users often experience a visual glitch when animating a BottomAppBar
that’s conditionally hidden. A placeholder widget, resembling the default Material BottomNavigationBar
theme, appears briefly before the actual BottomAppBar
animates into place.
Cause of the Placeholder
Flutter’s animation system needs a temporary placeholder while the animation is in progress. If a widget is conditionally hidden, Flutter, in some cases, needs to preserve a visual representation for the screen area that the animated widget would occupy during the transition to ensure that no visual disruption occurs. The placeholder you see is a result of this default placeholder behavior. The specific cause stems from the interaction between SlideTransition
and the conditional rendering within the widget tree.
Solutions
1. Using a Visibility
Widget with a AnimatedContainer
This approach combines the benefits of conditional visibility with a smooth animation.
import 'package:flutter/material.dart';
class AnimatedBottomAppBar extends StatefulWidget {
const AnimatedBottomAppBar({Key? key}) : super(key: key);
@override
_AnimatedBottomAppBarState createState() => _AnimatedBottomAppBarState();
}
class _AnimatedBottomAppBarState extends State
with SingleTickerProviderStateMixin {
late AnimationController _animationController;
bool _isVisible = false;
@override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 300),
);
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
void _toggleVisibility() {
setState(() {
_isVisible = !_isVisible;
if (_isVisible) {
_animationController.forward();
} else {
_animationController.reverse();
}
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Animated Bottom App Bar')),
body: Center(
child: ElevatedButton(
onPressed: _toggleVisibility,
child: const Text('Toggle Visibility'),
),
),
bottomNavigationBar: AnimatedContainer(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
height: _isVisible ? 56.0 : 0.0,
// Prevents placeholder, only show when isVisible
color: Colors.transparent,
child: Visibility(
visible: _isVisible,
child: SlideTransition(
position: Tween(
begin: const Offset(0, 1),
end: Offset.zero,
).animate(
CurvedAnimation(
parent: _animationController,
curve: Curves.easeInOut,
),
),
child: BottomAppBar(
color: Colors.blue,
),
),
),
),
);
}
}
Explanation: This approach uses AnimatedContainer
to control the height and indirectly the visibility of the BottomAppBar
. Importantly, it sets the color
to Colors.transparent
within the AnimatedContainer
. This crucial change eliminates the default placeholder by preventing the container from attempting to paint anything when hidden.
2. Using a AnimatedBuilder
This solution leverages AnimatedBuilder
to conditionally build the BottomAppBar
. This ensures the placeholder isn’t created when the BottomAppBar
isn’t visible, while smoothly animating as needed.
// ... (previous code) ...
@override
Widget build(BuildContext context) {
return Scaffold(
//... (previous code)
bottomNavigationBar: AnimatedBuilder(
animation: _animationController,
builder: (context, child) {
return SlideTransition(
position: Tween(
begin: const Offset(0, 1),
end: Offset.zero,
).animate(
CurvedAnimation(
parent: _animationController,
curve: Curves.easeInOut,
),
),
child: _isVisible ? BottomAppBar(color: Colors.blue) : SizedBox(height: 0.0),
);
}
),
// ... (previous code)
}
Potential Errors and Troubleshooting
- Incorrect animation duration: Ensure the animation duration is appropriate for the desired effect.
- Missing `vsync` for the `AnimationController`: If the
vsync
parameter is missing in the `AnimationController`, the animations might not work correctly. Always provide the `this` context as the `vsync` parameter.
By implementing these solutions, you can achieve smooth, visually appealing animations for your conditionally hidden BottomAppBar
widgets in Flutter.