2

The user can either enter the answer with InputChips or manually type it in the TextField. When I try with InputChips, the correct answer is not detected. When I try to manually type it, the FutureBuilder reloads when I enter and leave the TextField. What is the reason? The Future function should only be called once because it fetches a random document from Firestore, splits the String and scrambles the different pieces. It is some form of quiz.

class _buildPhrases extends State<PhrasesSession>{
  TextEditingController _c;
  String _text = "initial";

  @override
  void initState(){
    _c = new TextEditingController();
    super.initState();
  }

  @override
  void dispose(){
    _c?.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {

    final Arguments args = ModalRoute.of(context).settings.arguments;
    var height = MediaQuery.of(context).size.height;
    var width = MediaQuery.of(context).size.width;



    // TODO: implement build
    return Scaffold(
      body: Column(
        children: <Widget>[
          Flexible(flex: 2, child: _buildRest(context),),
          Flexible(flex: 5,
            child: FutureBuilder(
              future: getEverything(args.colName),
              builder: (context, snapshot){
                if(!snapshot.hasData){
                  return Center(child: CircularProgressIndicator(),);
                }else{

                  return Column(
                    children: <Widget>[
                      Flexible(flex: 1, child: Text(snapshot.data[1]),),
                      Divider(),
                      Flexible(flex: 2, child: Container(
                        child: TextField(
                        onChanged: (t){
                            _text += "$t ";
                            if(_c.text == snapshot.data[0]){
                              return print("CORRECT ANSWER");
                            }
                        },
                        controller: _c,
                        textAlign: TextAlign.center,
                          enabled: true,

                      ),
                      ),),
                      Flexible(flex: 3,
                        child: ListView.builder(
                          scrollDirection: Axis.horizontal,
                          itemCount: snapshot.data.length - 2,
                          itemBuilder: (context, index){
                            if(index>snapshot.data.length - 2){
                              return null;
                            }else{
                              return Padding(
                                padding: const EdgeInsets.all(4.0),
                                child: InputChip(
                                  label: Text(snapshot.data[index + 2]),
                                  onPressed: (){
                                    _c.text += "${snapshot.data[index  + 2]} ";
                                  },
                                ),
                              );
                            }
                          },
                        ))
                    ],
                  );
                }
              },
            ),)
        ],
      )
    );
  }
}
Marc Köhler
  • 83
  • 2
  • 10
  • You have to initialize your Future in initState. Take a lot on this: https://stackoverflow.com/questions/52249578/how-to-deal-with-unwanted-widget-build – Rubens Melo May 27 '19 at 13:05
  • @RubensMelo My Future needs the String that is passed from another route, though. See the `Arguments args = ...`. Any idea how I get it in the initState? – Marc Köhler May 27 '19 at 13:20
  • You can access that with `widget.args.colName` – Rubens Melo May 27 '19 at 13:24
  • @RubensMelo I'm not sure how to initialise my Future. In the example you sent it is `future = Future.value(42);`. In my case I would have to move my Future into the class and initialise like: `getEverything(widget.args.colName) = Future.value(???)` what would be the correct way? thanks – Marc Köhler May 27 '19 at 16:53

1 Answers1

7

Let's solve this in parts.

When I try to manually type it the FutureBuilder reloads when I enter and leave the TextField. What is the reason?

This is hapenning because when the keyboard is showing or hidding the flutter framework calls build method of your widget and this default behavior is the reason why your FutureBuilder is realoading. You should avoid call network methods inside build method and I advise you to use BLoC pattern to handle state of your widget.

My Future needs the String that is passed from another route, though. See the Arguments args = .... Any idea how I get it in the initState?

Well if you need context instance to get this String you can't access current context inside initState method because your widget isn't full initialized yet. A simple way to solve this in your case but not the best is verify if the data was already fetched from network or not.

Future _myNetworkFuture; // declare this as member of your stateWidgetClass

Widget build(BuildContext context){
   final Arguments args = ModalRoute.of(context).settings.arguments;
   var height = MediaQuery.of(context).size.height;
   var width = MediaQuery.of(context).size.width;

   // this line says if(_myNetworkFuture == null) do the thing.
   _myNetworkFuture ??= getEverything(args.colName);

   return ...
   Flexible(flex: 5,
        child: FutureBuilder(
          future: _myNetworkFuture,
          builder: (context, snapshot){ 
           // ...
          }
}

With this approach when flutter framework calls build method if you already fetched the data you don't download the data again. But I really advise you to use BLoC pattern in this kind of situation.

Marcos Boaventura
  • 4,641
  • 1
  • 20
  • 27