Create an account

Very important

  • To access the important data of the forums, you must be active in each forum and especially in the leaks and database leaks section, send data and after sending the data and activity, data and important content will be opened and visible for you.
  • You will only see chat messages from people who are at or below your level.
  • More than 500,000 database leaks and millions of account leaks are waiting for you, so access and view with more activity.
  • Many important data are inactive and inaccessible for you, so open them with activity. (This will be done automatically)


Thread Rating:
  • 227 Vote(s) - 3.63 Average
  • 1
  • 2
  • 3
  • 4
  • 5
How to debounce Textfield onChange in Dart?

#1
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);

}
}



Reply

#2
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]

Reply

#3
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]

Reply

#4
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
});
}
Reply

#5
**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);
Reply

#6
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]

Reply

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

#8
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]

Reply

#9
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]

Reply

#10
## 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,
// ...
)
```

Reply



Forum Jump:


Users browsing this thread:
1 Guest(s)

©0Day  2016 - 2023 | All Rights Reserved.  Made with    for the community. Connected through