import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:fladder/util/adaptive_layout/adaptive_layout.dart'; class HideOnScroll extends ConsumerStatefulWidget { final Widget? child; final ScrollController? controller; final double height; final Widget? Function(bool visible)? visibleBuilder; final Duration duration; final bool forceHide; const HideOnScroll({ this.child, this.controller, this.height = kBottomNavigationBarHeight, this.visibleBuilder, this.duration = const Duration(milliseconds: 200), this.forceHide = false, super.key, }) : assert(child != null || visibleBuilder != null); @override ConsumerState createState() => _HideOnScrollState(); } class _HideOnScrollState extends ConsumerState { late final ScrollController scrollController = widget.controller ?? ScrollController(); bool isVisible = true; @override void initState() { super.initState(); scrollController.addListener(_onScroll); } @override void dispose() { scrollController.removeListener(_onScroll); if (widget.controller == null) { scrollController.dispose(); } super.dispose(); } void _onScroll() { final position = scrollController.position; final direction = position.userScrollDirection; bool newVisible; if (position.atEdge && position.pixels >= position.maxScrollExtent) { // Always show when scrolled to bottom newVisible = true; } else { newVisible = direction == ScrollDirection.forward; } if (newVisible != isVisible) { setState(() => isVisible = newVisible); } } @override Widget build(BuildContext context) { if (widget.visibleBuilder != null) { return widget.visibleBuilder!(isVisible) ?? const SizedBox(); } if (widget.child == null) return const SizedBox(); if (AdaptiveLayout.viewSizeOf(context) == ViewSize.desktop) { return widget.child!; } return AnimatedAlign( alignment: const Alignment(0, -1), heightFactor: widget.forceHide ? 0 : isVisible ? 1.0 : 0, duration: widget.duration, child: Wrap( children: [widget.child!], ), ); } }