import 'dart:async'; import 'package:flutter/material.dart'; final class AutoHide extends StatefulWidget { final Widget child; final ScrollController controller; final AxisDirection direction; const AutoHide({ super.key, required this.child, required this.controller, required this.direction, }); @override State createState() => _AutoHideState(); } final class _AutoHideState extends State { bool _visible = true; bool _isScrolling = false; Timer? _timer; @override void initState() { super.initState(); widget.controller.addListener(_scrollListener); _setupTimer(); } @override void dispose() { widget.controller.removeListener(_scrollListener); _timer?.cancel(); _timer = null; super.dispose(); } void _setupTimer() { _timer?.cancel(); _timer = Timer.periodic(const Duration(seconds: 3), (_) { debugPrint('[AutoHideFab._timer] trigger timer'); if (_isScrolling) return; if (!_visible) return; if (!widget.controller.positions.any((e) => e.maxScrollExtent >= 0)) { return; } setState(() { _visible = false; }); _timer?.cancel(); _timer = null; }); } void _scrollListener() { if (_isScrolling) return; _isScrolling = true; if (!_visible) { setState(() { _visible = true; }); _setupTimer(); } _isScrolling = false; } @override Widget build(BuildContext context) { return AnimatedContainer( duration: Durations.medium1, curve: Curves.easeInOutCubic, transform: _transform, child: widget.child, ); } Matrix4? get _transform { switch (widget.direction) { case AxisDirection.down: return _visible ? Matrix4.identity() : Matrix4.translationValues(0, 55, 0); case AxisDirection.up: return _visible ? Matrix4.identity() : Matrix4.translationValues(0, -55, 0); case AxisDirection.left: return _visible ? Matrix4.identity() : Matrix4.translationValues(-55, 0, 0); case AxisDirection.right: return _visible ? Matrix4.identity() : Matrix4.translationValues(55, 0, 0); } } }