I\'m trying to develop a TextField that update the data on a Firestore database when they change. It seems to work but I need to prevent the onChange event to fire multiple
Using BehaviorSubject from rxdart lib is a good solution. It ignores changes that happen within X seconds of the previous.
final searchOnChange = new BehaviorSubject<String>();
...
TextField(onChanged: _search)
...
void _search(String queryString) {
searchOnChange.add(queryString);
}
void initState() {
searchOnChange.debounceTime(Duration(seconds: 1)).listen((queryString) {
>> request data from your API
});
}
You can use rxdart package to create an Observable using a stream then debounce it as per your requirements. I think this link would help you get started.
You can make Debouncer
class using Timer
import 'package:flutter/foundation.dart';
import 'dart:async';
class Debouncer {
final int milliseconds;
VoidCallback action;
Timer _timer;
Debouncer({ this.milliseconds });
run(VoidCallback action) {
if (_timer != null) {
_timer.cancel();
}
_timer = Timer(Duration(milliseconds: milliseconds), action);
}
}
Declare it
final _debouncer = Debouncer(milliseconds: 500);
and trigger it
onTextChange(String text) {
_debouncer.run(() => print(text));
}
Here is my solution
subject = new PublishSubject<String>();
subject.stream
.debounceTime(Duration(milliseconds: 300))
.where((value) => value.isNotEmpty && value.toString().length > 1)
.distinct()
.listen(_search);
As others have suggested, implementing a custom debouncer class is not that difficult. You can also use a Flutter plugin, such as EasyDebounce.
In your case you'd use it like this:
import 'package:easy_debounce/easy_debounce.dart';
...
// Start listening to changes
nomeTextController.addListener(((){
EasyDebounce.debounce(
'_updatenomecliente', // <-- An ID for this debounce operation
Duration(milliseconds: 500), // <-- Adjust Duration to fit your needs
() => _updateNomeCliente()
);
}));
Full disclosure: I'm the author of EasyDebounce.
Here's my two cents with preemptive debouncing.
import 'dart:async';
/// Used to debounce function call.
/// That means [runnable] function will be called at most once per [delay].
class Debouncer {
int _lastTime;
Timer _timer;
Duration delay;
Debouncer(this.delay)
: _lastTime = DateTime.now().millisecondsSinceEpoch;
run(Function runnable) {
_timer?.cancel();
final current = DateTime.now().millisecondsSinceEpoch;
final delta = current - _lastTime;
// If elapsed time is bigger than [delayInMs] threshold -
// call function immediately.
if (delta > delay.inMilliseconds) {
_lastTime = current;
runnable();
} else {
// Elapsed time is less then [delayInMs] threshold -
// setup the timer
_timer = Timer(delay, runnable);
}
}
}