0Day Forums
How to debounce Textfield onChange in Dart? - Printable Version

+- 0Day Forums (https://zeroday.vip)
+-- Forum: Coding (https://zeroday.vip/Forum-Coding)
+--- Forum: Flutter & Dart (https://zeroday.vip/Forum-Flutter-Dart)
+--- Thread: How to debounce Textfield onChange in Dart? (/Thread-How-to-debounce-Textfield-onChange-in-Dart)

Pages: 1 2


How to debounce Textfield onChange in Dart? - abua - 07-21-2023

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 times.

In JS I would use lodash _debounce() but in Dart I don't know how to do it. I've read of some debounce libraries but I can't figure out how they work.

That's my code, it's only a test so something may be strange:

import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';


class ClientePage extends StatefulWidget {

String idCliente;


ClientePage(this.idCliente);

@override
_ClientePageState createState() => new _ClientePageState();


}

class _ClientePageState extends State<ClientePage> {

TextEditingController nomeTextController = new TextEditingController();


void initState() {
super.initState();

// Start listening to changes
nomeTextController.addListener(((){
_updateNomeCliente(); // <- Prevent this function from run multiple times
}));
}


_updateNomeCliente = (){

print("Aggiorno nome cliente");
Firestore.instance.collection('clienti').document(widget.idCliente).setData( {
"nome" : nomeTextController.text
}, merge: true);

}



@override
Widget build(BuildContext context) {

return new StreamBuilder<DocumentSnapshot>(
stream: Firestore.instance.collection('clienti').document(widget.idCliente).snapshots(),
builder: (BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (!snapshot.hasData) return new Text('Loading...');

nomeTextController.text = snapshot.data['nome'];


return new DefaultTabController(
length: 3,
child: new Scaffold(
body: new TabBarView(
children: <Widget>[
new Column(
children: <Widget>[
new Padding(
padding: new EdgeInsets.symmetric(
vertical : 20.00
),
child: new Container(
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
new Text(snapshot.data['cognome']),
new Text(snapshot.data['ragionesociale']),
],
),
),
),
new Expanded(
child: new Container(
decoration: new BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20.00),
topRight: Radius.circular(20.00)
),
color: Colors.brown,
),
child: new ListView(
children: <Widget>[
new ListTile(
title: new TextField(
style: new TextStyle(
color: Colors.white70
),
controller: nomeTextController,
decoration: new InputDecoration(labelText: "Nome")
),
)
]
)
),
)
],
),
new Text("La seconda pagina"),
new Text("La terza pagina"),
]
),
appBar: new AppBar(
title: Text(snapshot.data['nome'] + ' oh ' + snapshot.data['cognome']),
bottom: new TabBar(
tabs: <Widget>[
new Tab(text: "Informazioni"), // 1st Tab
new Tab(text: "Schede cliente"), // 2nd Tab
new Tab(text: "Altro"), // 3rd Tab
],
),
),
)
);

},
);

print("Il widget id è");
print(widget.idCliente);

}
}






RE: How to debounce Textfield onChange in Dart? - gonfanon680188 - 07-21-2023

You can use rxdart package to create an Observable using a stream then debounce it as per your requirements. I think this [link][1] would help you get started.


[1]:

[To see links please register here]




RE: How to debounce Textfield onChange in Dart? - brookbrooke788 - 07-21-2023

As others have suggested, implementing a custom debouncer class is not that difficult. You can also use a Flutter plugin, such as [EasyDebounce][1].

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][1]._


[1]:

[To see links please register here]




RE: How to debounce Textfield onChange in Dart? - Prorilievo662 - 07-21-2023

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
});
}


RE: How to debounce Textfield onChange in Dart? - Aileen970951 - 07-21-2023

**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);


RE: How to debounce Textfield onChange in Dart? - heartburn767 - 07-21-2023

What about a utility function like:

import 'dart:async';

Function debounce(Function func, int milliseconds) {
Timer timer;
return () { // or (arg) if you need an argument
if (timer != null) {
timer.cancel();
}

timer = Timer(Duration(milliseconds: milliseconds), func); // or () => func(arg)
};
}

Then:

var debouncedSearch = debounce(onSearchChanged, 250);
_searchQuery.addListener(debouncedSearch);

In the future with [variable arguments][1] it could be improved.


[1]:

[To see links please register here]




RE: How to debounce Textfield onChange in Dart? - kreiker639 - 07-21-2023

Here's my two cents with preemptive debouncing.

```dart
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);
}
}
}
```



RE: How to debounce Textfield onChange in Dart? - entoiled555763 - 07-21-2023

Have a look at [EasyDebounce][1].

EasyDebounce.debounce(
'my-debouncer', // <-- An ID for this particular debouncer
Duration(milliseconds: 500), // <-- The debounce duration
() => myMethod() // <-- The target method
);


[1]:

[To see links please register here]




RE: How to debounce Textfield onChange in Dart? - Sirzelidarc - 07-21-2023

I like Dart's [Callable Classes][1] for my debounce class:


```dart
import 'dart:async';

class Debounce {
Duration delay;
Timer? _timer;

Debounce(
this.delay,
);

call(void Function() callback) {
_timer?.cancel();
_timer = Timer(delay, callback);
}

dispose() {
_timer?.cancel();
}
}
```

Usage is simple - [example on dartpad][2]

```dart
// 1 - Create a debounce instance
final Debounce _debounce = Debounce(Duration(milliseconds: 400));

// 2 - Use it
_debounce((){ print('First'); });
_debounce((){ print('Second'); });
_debounce((){ print('Third'); });

// ...after 400ms you'll see "Third"
```

For your specific example, it's important to dispose the timer, in case it makes use of your TextController after it's been disposed:

```dart
final TextEditingController _controller = TextEditingController();
final Debounce _debounce = Debounce(Duration(milliseconds: 400));

@override
void dispose() {
_controller.dispose();
_debounce.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
return TextField(
controller: _controller,
onChanged: (String value) {
_debounce((){
print('Value is $value');
});
},
);
}
```


[1]:

[To see links please register here]

[2]:

[To see links please register here]




RE: How to debounce Textfield onChange in Dart? - pasha107 - 07-21-2023

## Implementation

Import dependencies:

```dart
import 'dart:async';
```

In your widget state declare a timer:

```dart
Timer? _debounce;
```

Add a listener method:

```dart
_onSearchChanged(String query) {
if (_debounce?.isActive ?? false) _debounce.cancel();
_debounce = Timer(const Duration(milliseconds: 500), () {
// do something with query
});
}
```

Don't forget to clean up:

```dart
@override
void dispose() {
_debounce?.cancel();
super.dispose();
}
```

## Usage

In your build tree hook the `onChanged` event:

```dart
child: TextField(
onChanged: _onSearchChanged,
// ...
)
```