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