2023-08-27 05:07:35 +00:00
import ' dart:async ' ;
2024-01-15 15:26:13 +00:00
import ' package:flutter_hooks/flutter_hooks.dart ' ;
2023-08-27 05:07:35 +00:00
2024-01-15 15:26:13 +00:00
/// Used to debounce function calls with the [interval] provided.
2024-12-04 21:03:46 +00:00
/// If [maxWaitTime] is provided, the first [run] call as well as the next call since [maxWaitTime] has passed will be immediately executed, even if [interval] is not satisfied.
2024-01-15 15:26:13 +00:00
class Debouncer {
2024-12-04 21:03:46 +00:00
Debouncer ( { required this . interval , this . maxWaitTime } ) ;
2024-01-15 15:26:13 +00:00
final Duration interval ;
2024-12-04 21:03:46 +00:00
final Duration ? maxWaitTime ;
2023-08-27 05:07:35 +00:00
Timer ? _timer ;
2024-01-15 15:26:13 +00:00
FutureOr < void > Function ( ) ? _lastAction ;
2024-12-04 21:03:46 +00:00
DateTime ? _lastActionTime ;
Future < void > ? _actionFuture ;
2023-08-27 05:07:35 +00:00
2024-01-15 15:26:13 +00:00
void run ( FutureOr < void > Function ( ) action ) {
_lastAction = action ;
2023-08-27 05:07:35 +00:00
_timer ? . cancel ( ) ;
2024-12-04 21:03:46 +00:00
if ( maxWaitTime ! = null & &
// _actionFuture == null && // TODO: should this check be here?
( _lastActionTime = = null | |
DateTime . now ( ) . difference ( _lastActionTime ! ) > maxWaitTime ! ) ) {
_callAndRest ( ) ;
return ;
}
2024-01-15 15:26:13 +00:00
_timer = Timer ( interval , _callAndRest ) ;
2023-08-27 05:07:35 +00:00
}
2024-12-04 21:03:46 +00:00
Future < void > ? drain ( ) {
if ( _timer ! = null & & _timer ! . isActive ) {
_timer ! . cancel ( ) ;
if ( _lastAction ! = null ) {
_callAndRest ( ) ;
}
}
return _actionFuture ;
}
@ pragma ( ' vm:prefer-inline ' )
2023-08-27 05:07:35 +00:00
void _callAndRest ( ) {
2024-12-04 21:03:46 +00:00
_lastActionTime = DateTime . now ( ) ;
final action = _lastAction ;
_lastAction = null ;
final result = action ! ( ) ;
if ( result is Future ) {
_actionFuture = result . whenComplete ( ( ) {
_actionFuture = null ;
} ) ;
}
2023-08-27 05:07:35 +00:00
_timer = null ;
}
void dispose ( ) {
_timer ? . cancel ( ) ;
_timer = null ;
2024-01-15 15:26:13 +00:00
_lastAction = null ;
2024-12-04 21:03:46 +00:00
_lastActionTime = null ;
_actionFuture = null ;
2023-08-27 05:07:35 +00:00
}
2024-12-04 21:03:46 +00:00
bool get isActive = >
_actionFuture ! = null | | ( _timer ! = null & & _timer ! . isActive ) ;
2023-08-27 05:07:35 +00:00
}
2024-01-15 15:26:13 +00:00
/// Creates a [Debouncer] that will be disposed automatically. If no [interval] is provided, a
/// default interval of 300ms is used to debounce the function calls
Debouncer useDebouncer ( {
Duration interval = const Duration ( milliseconds: 300 ) ,
2024-12-04 21:03:46 +00:00
Duration ? maxWaitTime ,
2024-01-15 15:26:13 +00:00
List < Object ? > ? keys ,
} ) = >
2024-12-04 21:03:46 +00:00
use (
_DebouncerHook (
interval: interval ,
maxWaitTime: maxWaitTime ,
keys: keys ,
) ,
) ;
2024-01-15 15:26:13 +00:00
class _DebouncerHook extends Hook < Debouncer > {
const _DebouncerHook ( {
required this . interval ,
2024-12-04 21:03:46 +00:00
this . maxWaitTime ,
2024-01-27 16:14:32 +00:00
super . keys ,
} ) ;
2024-01-15 15:26:13 +00:00
final Duration interval ;
2024-12-04 21:03:46 +00:00
final Duration ? maxWaitTime ;
2024-01-15 15:26:13 +00:00
@ override
HookState < Debouncer , Hook < Debouncer > > createState ( ) = > _DebouncerHookState ( ) ;
}
class _DebouncerHookState extends HookState < Debouncer , _DebouncerHook > {
2024-12-04 21:03:46 +00:00
late final debouncer = Debouncer (
interval: hook . interval ,
maxWaitTime: hook . maxWaitTime ,
) ;
2024-01-15 15:26:13 +00:00
@ override
Debouncer build ( _ ) = > debouncer ;
@ override
void dispose ( ) = > debouncer . dispose ( ) ;
@ override
String get debugLabel = > ' useDebouncer ' ;
}