11

I am using a TextField under which onChanged function

I am calling my code but the issue right now is the code gets execute everytime a new word is either entered or deleted.

what I am looking for is how to recognize when a user has stopped typing.

means adding some delay or something like that.

I have tried adding delay also using Future.delayed function but that function also gets executed n number of times.

TextField(
   controller: textController,
   onChanged: (val) {
            if (textController.text.length > 3) {
              Future.delayed(Duration(milliseconds: 450), () {
                 //code here
              });
            }
            setState(() {});
          },
 )
Vicky Salunkhe
  • 9,869
  • 6
  • 42
  • 59
  • are you familiar with rxdart? or with streams in general? – pskink Jun 30 '19 at 09:07
  • I have seen some examples related to it, but not used it till now. Do you have some example you can share related to this use case? – Vicky Salunkhe Jun 30 '19 at 09:18
  • 1
    see https://pub.dev/packages/stream_transform, and use [debounce](https://pub.dev/packages/stream_transform#debounce-debouncebuffer) stream transformer - the docs say: *"Prevents a source stream from emitting too frequently by dropping or collecting values that occur within a given duration."* – pskink Jun 30 '19 at 09:21
  • thankyou @pskink do you know where can I find an example for that package. I guess there is too less document available. – Vicky Salunkhe Jun 30 '19 at 09:23
  • https://api.dartlang.org/stable/2.4.0/dart-async/Stream/transform.html – pskink Jun 30 '19 at 09:25
  • Hi @pskink can you post some code example on how to use it with TextField, I am right now clueless on how to use it. – Vicky Salunkhe Jun 30 '19 at 09:40
  • see https://dart.dev/articles/libraries/creating-streams#using-a-streamcontroller - inside `onChanged` simply call `StreamController.add` method – pskink Jun 30 '19 at 09:53
  • 1
    as simple as: `StreamController controller = StreamController();` then inside `initState` method: `controller.stream.transform(debounce(Duration(seconds: 1))).listen((s) => print('you typed: [$s]'));` and use it in your widget: `TextField(onChanged: controller.add),` – pskink Jun 30 '19 at 10:21
  • Thanks a lot @pskink that works seamlessly. – Vicky Salunkhe Jun 30 '19 at 10:35
  • sure, your welcome – pskink Jun 30 '19 at 10:37
  • @pskink Hi mate, over last few months, I have seen you providing excellent solutions but only in comment, why don't you write them down as an answer, you would have easily gained +10k reputation. – CopsOnRoad Jun 30 '19 at 12:04
  • @CopsOnRoad i am too lazy ;-) – pskink Jun 30 '19 at 12:05

4 Answers4

3

I find something so lighter in this link

it define this class

class Debouncer {
  final int milliseconds;

  Timer? _timer;

  Debouncer({this.milliseconds=500});

  run(VoidCallback action) {
    if (null != _timer) {
      _timer!.cancel();
    }
    _timer = Timer(Duration(milliseconds: milliseconds), action);
  }
}

this sample will help you more

TextField(
     decoration: new InputDecoration(hintText: 'Search'),
     onChanged: (string) {
       _debouncer.run(() {
       print(string);
       //perform search here
       });
     },
   ),
Hamid Waezi
  • 878
  • 1
  • 7
  • 14
2

Thanks to @pskink

I was able to achieve the functionality I was looking for.

import stream_transform package in your pubspec.yaml

stream_transform: ^0.0.19

import 'package:stream_transform/stream_transform.dart';

StreamController<String> streamController = StreamController();

@override
void initState() {
  streamController.stream
    .transform(debounce(Duration(milliseconds: 400)))
    .listen((s) => _validateValues());

  super.initState();
}

//function I am using to perform some logic
_validateValues() {
  if (textController.text.length > 3) {
     // code here
  }else{
     // some other code here
  }
}

TextField code

TextField(
   controller: textController,
   onChanged: (val) {
        streamController.add(val);
     },
)
Vicky Salunkhe
  • 9,869
  • 6
  • 42
  • 59
1

In my case I also required flutter async.

//pubspec.yaml
stream_transform: ^2.0.0

import 'dart:async';
import 'package:stream_transform/stream_transform.dart';

StreamController<String> streamController = StreamController();

// In init function
streamController.stream
    .debounce(Duration(seconds: 1))
    .listen((s) => { 
       // your code 
     });

// In build function
TextField(
   style: TextStyle(fontSize: 16),
   controller: messageController,
   onChanged: (val) {
     myMetaRef.child("isTyping").set(true);
     streamController.add(val);
     },
   )
RHS.Dev
  • 412
  • 6
  • 18
1

I know this is pretty late. In my case I needed to check the availability of an email during the sign up process and needed to show the relevant message to user when the user stopped typing the email in the textfield. You need to create a timer and reset it every time the user types something inside the textfield. Here's the code:

Timer? _checkTypingTimer;

startTimer() {
    _checkTypingTimer = Timer(const Duration(milliseconds: 600), () {//set your desired duration
      //perform your logic here
      //i was calling the method here which checks if the email is available or not
    });
  }

resetTimer() {
    _checkTypingTimer?.cancel();
    startTimer();
  }

Now call this resetTimer method inside the onChanged method of your textfield. And don't forget to import dart:async

Note: The answer given above with the medium link, the "debouncer one", may needlessly invoke your code several times

Mohammad Khan Awan
  • 164
  • 1
  • 3
  • 8