如何在Dart中反跳Textfield onChange?

时间:2018-08-10 17:35:32

标签: dart flutter debouncing

我正在尝试开发一个TextField,以便在它们更改时更新Firestore数据库上的数据。看来可以,但是我需要防止onChange事件触发多次。

在JS中,我会使用lodash _debounce(),但在Dart中,我不知道该怎么做。我已经读过一些防抖库,但是我不知道它们是如何工作的。

那是我的代码,这只是一个测试,所以可能有些奇怪:

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

  }
}

10 个答案:

答案 0 :(得分:18)

在您的小部件状态下,声明一个控制器和计时器:

final _searchQuery = new TextEditingController();
Timer _debounce;

添加侦听器方法:

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

将方法挂钩并取消挂钩到控制器:

@override
void initState() {
    super.initState();
    _searchQuery.addListener(_onSearchChanged);
}

@override
void dispose() {
    _searchQuery.removeListener(_onSearchChanged);
    _searchQuery.dispose();
    super.dispose();
}

在构建树中,将控制器绑定到TextField:

child: TextField(
        controller: _searchQuery,
        // ...
    )

答案 1 :(得分:5)

您可以使用jmap -heap -J-d64 $iPid来制作Debouncer

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

并触发它

final _debouncer = Debouncer(milliseconds: 500);

答案 2 :(得分:2)

您可以使用rxdart包使用流创建一个Observable,然后根据需要对其进行去抖动。我认为link可以帮助您入门。

答案 3 :(得分:2)

这是我的解决方案

 subject = new PublishSubject<String>();
      subject.stream
          .debounceTime(Duration(milliseconds: 300))
          .where((value) => value.isNotEmpty && value.toString().length > 1)
          .distinct()
          .listen(_search);

答案 4 :(得分:1)

使用rxdart lib中的BehaviorSubject是一个很好的解决方案。 它会忽略在上一个X秒内发生的更改。

final searchOnChange = new BehaviorSubject<String>();
...
TextField(onChanged: _search)
...

void _search(String queryString) {
  searchOnChange.add(queryString);
}   

void initState() {    
  searchOnChange.debounce(Duration(seconds: 1)).listen((queryString) { 
  >> request data from your API
  });
}

答案 5 :(得分:1)

这是我的两美分,可以防弹跳。

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

答案 6 :(得分:1)

看看EasyDebounce

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

答案 7 :(得分:0)

可以使用Future.delayed方法实现简单的去抖动

bool debounceActive = false;
...

//listener method
onTextChange(String text) async {
  if(debounceActive) return null;
  debounceActive = true;
  await Future.delayed(Duration(milliSeconds:700));
  debounceActive = false;
  // hit your api here
}

答案 8 :(得分:0)

正如其他人所建议的那样,实现自定义防反弹器类并不是那么困难。您还可以使用Flutter插件,例如EasyDebounce

在您的情况下,您将像这样使用它:

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

完整披露:我是EasyDebounce的作者。

答案 9 :(得分:0)

实用程序功能如何?

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

然后:

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

将来使用variable arguments可以对其进行改进。