3 votes

Rafraîchissement du FutureBuilder de Flutter lorsque la valeur du champ de texte change

En _futureData est à utiliser pour le FutureBuilder après avoir récupéré la valeur du _loadPhobias() fonction.

écran_d'entrée.dart

Future _futureData;
final TextEditingController _textEditingController = TextEditingController();

_loadPhobias() ne semble pas avoir de problème.

écran_d'entrée.dart

Future<List<String>> _loadPhobias() async =>
    await rootBundle.loadString('assets/phobias.txt').then((phobias) {
    List _listOfAllPhobias = [];
    List<String> _listOfSortedPhobias = [];
    _textEditingController.addListener(() {
      ...
    }); 
    return _listOfSortedPhobias;
});

@override
void initState() {
super.initState();
    _futureData = _loadPhobias();
}

@override
Widget build(BuildContext context) {
    return Scaffold(
    appBar: AppBar(
    title: TextField(
        // When the value is changed, the value returned from the _loadPhobias will also change. So I want the FutureBuilder to be rebuilt.
        onChanged: (text) { setState(() => _futureData =  _loadPhobias()) },
        ),
    ),
    body: FutureBuilder(
        future: _futureData,
        builder: (context, snapshot) {
            return snapshot.hasData
                ? ListView.builder(
                    itemCount: snapshot.data.length,
                    itemBuilder: (context, index) => Column(
                            children: <Widget>[
                                PhobiasCard(sentence: snapshot.data[index]),
                            )
                        ],
                    ))
                    : Center(
                        child: CircularProgressIndicator(),
                    );
                },
            ),
        ),
    );
}

Voici l'erreur que j'ai obtenue :

FlutterError (l'argument de la callback setState() a renvoyé un Future.

La méthode setState() de _EntryScreenState#51168 a été appelée avec une fermeture ou une méthode renvoyant un Future. Elle est peut-être marquée comme "async".

Au lieu d'effectuer un travail asynchrone à l'intérieur d'un appel à setState(), exécutez d'abord le travail (sans mettre à jour l'état du widget), puis mettez à jour l'état de manière synchrone à l'intérieur d'un appel à setState()).

7voto

Joshua de Guzman Points 1480

La première chose à noter, vous avez mentionné que vous voulez reconstruire votre application à chaque fois qu'il y a un changement dans le texte. Pour cela, vous devez utiliser StreamBuilder à la place. FutureBuilder est destiné à être consommé une seule fois, c'est comme un événement "fire and forget" ou "Promise" en JavaScript.

Ici à une bonne comparaison entre StreamBuilder vs FutureBuilder .

Voici comment refactoriser votre code pour utiliser StreamBuilder .

main.dart

import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyAppScreen(),
    );
  }
}

class MyAppScreen extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return MyAppScreenState();
  }
}

class MyAppScreenState extends State<MyAppScreen> {
  StreamController<List<String>> _phobiasStream;

  final TextEditingController _textEditingController = TextEditingController();

  void _loadPhobias() async =>
      await rootBundle.loadString('lib/phobia.txt').then((phobias) {
        List<String> _listOfSortedPhobias = [];
        for (String i in LineSplitter().convert(phobias)) {
          for (String t in _textEditingController.text.split('')) {
            if (i.split('-').first.toString().contains(t)) {
              _listOfSortedPhobias.add(i);
            }
          }
        }
        _phobiasStream.add(_listOfSortedPhobias);
      });

  @override
  void initState() {
    super.initState();
    _phobiasStream = StreamController<List<String>>();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: TextField(
          controller: _textEditingController,
          onChanged: (text) {
            print("Text $text");
            _loadPhobias();
          },
        ),
      ),
      body: StreamBuilder(
        stream: _phobiasStream.stream,
        builder: (context, snapshot) {
          return snapshot.hasData
              ? Container(
                  height: 300,
                  child: ListView.builder(
                    itemCount: snapshot.data.length,
                    itemBuilder: (context, index) {
                      print("Data ${snapshot.data[index]}");
                      return Text(snapshot.data[index]);
                    },
                  ),
                )
              : Center(
                  child: CircularProgressIndicator(),
                );
        },
      ),
    );
  }
}

Comme on le voit dans le code ci-dessus, j'ai éliminé les rappels de changement de texte inutiles à l'intérieur de la boucle for a.

lib/phobie.txt

test1-test2-test3-test4-test5

Faites-moi savoir si c'est le scénario attendu.

J'espère que cela vous aidera.

0voto

Victor Eronmosele Points 1641

La solution peut être déduite de la troisième ligne du message d'erreur :

Au lieu d'effectuer un travail asynchrone à l'intérieur d'un appel à setState(), exécutez d'abord le travail (sans mettre à jour l'état du widget), puis mettez à jour l'état de manière synchrone à l'intérieur d'un appel à setState()).

Cela signifie donc que vous devrez effectuer l'opération avant de rafraîchir le widget. Vous pouvez disposer d'une variable temporaire pour contenir le résultat de l'opération asynchrone et l'utiliser dans votre fichier setState méthode :

onChanged: (text) {
     setState(() => _futureData =  _loadPhobias())
},

On pourrait l'écrire comme suit :

onChanged: (text) async {
    var phobias = _loadPhobias();
    setState(() {
        _futureData = phobias;
    });
},

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X