如何在Flutter中结合GetX实现滚动时隐藏/显示FloatingActionButton
实现思路
通过监听滚动通知,对比前后滚动偏移量判断滚动方向,用GetX的响应式状态控制FAB的显示/隐藏状态,配合动画组件实现平滑过渡效果。
步骤1:创建GetX控制器
定义控制器管理滚动偏移记录和FAB可见状态:
class ScrollFabController extends GetxController { // 记录上一次滚动位置 final RxDouble _lastScrollOffset = 0.0.obs; // FAB可见状态 final RxBool _fabVisible = true.obs; bool get fabVisible => _fabVisible.value; void handleScroll(ScrollNotification notification) { final currentOffset = notification.metrics.pixels; // 设置滚动阈值,避免轻微滚动触发状态切换 const scrollThreshold = 10.0; if (currentOffset - _lastScrollOffset.value > scrollThreshold) { // 向下滚动超过阈值,隐藏FAB _fabVisible.value = false; } else if (_lastScrollOffset.value - currentOffset > scrollThreshold) { // 向上滚动超过阈值,显示FAB _fabVisible.value = true; } _lastScrollOffset.value = currentOffset; } }
步骤2:在页面中实现滚动监听与FAB控制
用NotificationListener监听滚动事件,通过Obx响应状态变化,配合AnimatedOpacity实现平滑过渡:
class ScrollFabPage extends StatelessWidget { final controller = Get.put(ScrollFabController()); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('滚动控制FAB示例')), body: NotificationListener<ScrollNotification>( onNotification: (notification) { controller.handleScroll(notification); return true; }, child: ListView.builder( itemCount: 50, itemBuilder: (ctx, index) => ListTile(title: Text('列表项 $index')), ), ), floatingActionButton: Obx( () => AnimatedOpacity( opacity: controller.fabVisible ? 1.0 : 0.0, duration: const Duration(milliseconds: 300), // 隐藏时移除点击区域 child: Visibility( visible: controller.fabVisible, child: FloatingActionButton( onPressed: () => print('FAB被点击'), child: const Icon(Icons.add), ), ), ), ), ); } }
可选优化:滑入滑出动画
如果想要更自然的滑入滑出效果,可以替换AnimatedOpacity为SlideTransition(需要将页面改为StatefulWidget):
class ScrollFabPage extends StatefulWidget { const ScrollFabPage({super.key}); @override State<ScrollFabPage> createState() => _ScrollFabPageState(); } class _ScrollFabPageState extends State<ScrollFabPage> with SingleTickerProviderStateMixin { final controller = Get.put(ScrollFabController()); late final AnimationController _animationController; late final Animation<Offset> _slideAnimation; @override void initState() { super.initState(); _animationController = AnimationController( vsync: this, duration: const Duration(milliseconds: 300), ); _slideAnimation = Tween<Offset>( begin: Offset.zero, end: const Offset(0, 2), // 向下滑出屏幕 ).animate(CurvedAnimation( parent: _animationController, curve: Curves.easeInOut, )); // 监听FAB状态变化,控制动画 ever(controller._fabVisible, (isVisible) { if (isVisible) { _animationController.reverse(); } else { _animationController.forward(); } }); } @override void dispose() { _animationController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('滚动控制FAB示例')), body: NotificationListener<ScrollNotification>( onNotification: (notification) { controller.handleScroll(notification); return true; }, child: ListView.builder( itemCount: 50, itemBuilder: (ctx, index) => ListTile(title: Text('列表项 $index')), ), ), floatingActionButton: SlideTransition( position: _slideAnimation, child: FloatingActionButton( onPressed: () => print('FAB被点击'), child: const Icon(Icons.add), ), ), ); } }
内容的提问来源于stack exchange,提问作者Khalid
相关产品推荐
相关产品推荐

