web-dev-qa-db-fra.com

Comment rebondir Textfield onChange dans Dart?

J'essaie de développer un TextField qui met à jour les données d'une base de données Firestore lorsqu'elles changent. Cela semble fonctionner, mais je dois empêcher l'événement onChange de se déclencher plusieurs fois.

En JS j'utiliserais lodash _debounce () mais en Dart je ne sais pas comment faire. J'ai lu quelques bibliothèques de debounce mais je ne peux pas comprendre comment elles fonctionnent.

C'est mon code, c'est seulement un test donc quelque chose peut être étrange:

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

  }
}
15
DxW

Dans votre état de widget, déclarez un contrôleur et une minuterie:

final _searchQuery = new TextEditingController();
Timer _debounce;

Ajoutez une méthode d'écoute:

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

Accrochez et décrochez la méthode au contrôleur:

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

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

Dans votre arborescence de construction, liez le contrôleur au TextField:

child: TextField(
        controller: _searchQuery,
        // ...
    )
33
Jannie Theunissen

Vous pouvez créer une classe Debouncer en utilisant 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);
  }
}

Déclarez-le

final _debouncer = Debouncer(milliseconds: 500);

et le déclencher

onTextChange(String text) {
  _debouncer.run(() => print(text));
}
15
onmyway133

L'utilisation de BehaviorSubject à partir de rxdart lib est une bonne solution. Il ignore les changements qui se produisent dans les X secondes de la précédente.

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

Vous pouvez utiliser le package rxdart pour créer un observable à l'aide d'un flux, puis le rebounce selon vos besoins. Je pense que cela link vous aiderait à démarrer.

2
bhanu

un simple anti-rebond peut être implémenté en utilisant Future.delayed méthode

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
}
0
HasilT

Comme d'autres l'ont suggéré, l'implémentation d'une classe anti-rebond personnalisée n'est pas si difficile. Vous pouvez également utiliser un plugin Flutter, tel que EasyDebounce .

Dans votre cas, vous l'utiliseriez comme ceci:

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

Divulgation complète: je suis l'auteur de EasyDebounce .

0
Magnus W