Flutter - setState not updating inner Stateful Widget

后端 未结 5 460
生来不讨喜
生来不讨喜 2021-02-01 02:57

Basically I am trying to make an app whose content will be updated with an async function that takes information from a website, but when I do try to set the new state, it doesn

相关标签:
5条回答
  • 2021-02-01 03:05

    don't use a future within a future; use different function that will return each future individually like this

     List<Requests> requestsData;
     List<DocumentSnapshot> requestsDocumentData;
     var docId;
    
    
    
      @override
      void initState() {
        super.initState();
    
        getRequestDocs();
      }
    
      Future<FirebaseUser> getData() {
        var _auth = FirebaseAuth.instance;
        return _auth.currentUser();
      }
    
      getRequestDocs() {
        getData().then((FirebaseUser user) {
          this.setState(() {
            docId = user.uid;
          });
        });
    
        FireDb()
            .getDocuments("vendorsrequests")
            .then((List<DocumentSnapshot> documentSnapshots) {
          this.setState(() {
            requestsDocumentData = documentSnapshots;
          });
        });
    
        for (DocumentSnapshot request in requestsDocumentData) {
          this.setState(() {
            requestsData.add(Requests(
                request.documentID,
                request.data['requests'],
                Icons.data_usage,
                request.data['requests'][0],
                "location",
                "payMessage",
                "budget",
                "tokensRequired",
                "date"));
          });
        }
      }
    

    you can create individual functions for

      FireDb().getDocuments("vendorsrequests")
                .then((List<DocumentSnapshot> documentSnapshots) {
              this.setState(() {
                requestsDocumentData = documentSnapshots;
              });
            });
    

    and

      for (DocumentSnapshot request in requestsDocumentData) {
              this.setState(() {
                requestsData.add(Requests(
                    request.documentID,
                    request.data['requests'],
                    Icons.data_usage,
                    request.data['requests'][0],
                    "location",
                    "payMessage",
                    "budget",
                    "tokensRequired",
                    "date"));
              });
            }
    

    I found that the use of

    this
    

    with setState is must

    0 讨论(0)
  • 2021-02-01 03:06

    I'm unsure why this happens when calling setState(...) in an async function, but one simple solution is to use:

    WidgetsBinding.instance.addPostFrameCallback((_) => setState(...));
    

    instead of just setState(...)

    0 讨论(0)
  • 2021-02-01 03:07

    This was really giving me headache and no Google results were working. What finally worked was so simple. In your child build() assign the value to the local variable before you return. Once I did this everything worked with subsequent data loads. I even took out the initState() code.

    Many thanks to @Simon. Your answer somehow inspired me to try this.

    In your childState:

    @override
    Widget build(BuildContext context) {
    _title = widget._title; // <<< ADDING THIS HERE IS THE FIX
    return new GestureDetector(onTap: foo(),
       child: new Card(child: new Text(_title));
    
    }
    

    Hopefully this works in your code. For me, I use a Map for the entire JSON record passed in, rather than a single String, but that should still work.

    0 讨论(0)
  • 2021-02-01 03:09

    This fixed my issue... If you have an initial value to be assigned on a variable use it in initState()

    Note : Faced this issue when I tried to set initial value inside build function.

    @override
      void initState() {
        count = widget.initialValue.length; // Initial value
        super.initState();
      }
    
    0 讨论(0)
  • 2021-02-01 03:28

    This should sort your problem out. Basically you always want your Widgets created in your build method hierarchy.

    import 'dart:async';
    
    import 'package:flutter/material.dart';
    
    void main() => runApp(new MaterialApp(home: new Scaffold(body: new MainWidget())));
    
    class MainWidget extends StatefulWidget {
        @override
        State createState() => new MainWidgetState();
    }
    
    class MainWidgetState extends State<MainWidget> {
    
        List<ItemData> _data = new List();
        Timer timer;
    
        Widget build(BuildContext context) {
            return new ListView(children: _data.map((item) => new ChildWidget(item)).toList());
        }
    
        @override
        void initState() {
            super.initState();
            timer = new Timer.periodic(new Duration(seconds: 2), (Timer timer) async {
                ItemData data = await loadData();
                this.setState(() {
                    _data = <ItemData>[data];
                });
            });
        }
    
    
        @override
        void dispose() {
            super.dispose();
            timer.cancel();
        }
    
        static int testCount = 0;
    
        Future<ItemData> loadData() async {
            testCount++;
            return new ItemData("Testing #$testCount");
        }
    }
    
    class ChildWidget extends StatefulWidget {
    
        ItemData _data;
    
        ChildWidget(ItemData data) {
            _data = data;
        }
    
        @override
        State<ChildWidget> createState() => new ChildState();
    }
    
    class ChildState extends State<ChildWidget> {
    
        @override
        Widget build(BuildContext context) {
            return new GestureDetector(onTap: () => foo(),
                child: new Padding(
                    padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 24.0),
                    child: new Card(
                        child: new Container(
                            padding: const EdgeInsets.all(8.0),
                            child: new Text(widget._data.title),
                        ),
                    ),
                )
            );
        }
    
        foo() {
            print("Card Tapped: " + widget._data.toString());
        }
    }
    
    class ItemData {
        final String title;
    
        ItemData(this.title);
    
        @override
        String toString() {
            return 'ItemData{title: $title}';
        }
    }
    
    0 讨论(0)
提交回复
热议问题