【问题标题】:Flutter initializing an object from firestore before widget buildsFlutter 在构建小部件之前从 firestore 初始化对象
【发布时间】:2020-08-12 23:31:32
【问题描述】:

我是 Flutter 的新手,正在构建一个创建项目的应用程序。我正在查看允许用户查看每个问题的项目页面。它的设置方式允许用户查看一个问题,然后单击下一步(同时保持在同一页面上)以查看下一个问题。第一个 initState 初始化一个 Project 对象。然后,该项目对象需要从文档中获取数据/信息。问题存储为 Question 对象列表,其中每个对象都有类型(文本、多项选择、数字、照片上传等)、问题和数字。问题的类型用于确定要呈现哪个小部件(例如:照片上传与文本输入的形式不同。)。

我遇到的问题是项目对象已初始化,但问题对象列表未初始化,并且出现索引错误。但是,当我热刷新时,我看到了正确的小部件(目前只是文本)。我认为相关的另一个问题是,每次用户单击下一步时,对象都会再次检索所有问题,因此列表永远不会结束。我相信问题出在我的 getdataFromProject 和 getproject.dart 中的 questionData() 函数中(第一个代码 sn-p)。

我曾尝试使用未来的构建器,但我不确定这是正确的解决方案。但是,我的构建确实取决于正在完成的对象。所以我尝试使用变量 Future projectFuture 并将其设置为等于我的 _getQuestions() 函数(在 initState() 中调用)。

感谢任何帮助。

class Questions {
  final String question;
  final String number;
  final String type;
  Questions({this.question, this.number, this.type});
  List<String> answers = new List();
}

class GetProject {
//this is what keeps growing each time next is pressed
List<Questions> questions; //holds the question objects 
  String docID;
   String title;
  GetProject(String title, String docID){
    this.docID = docID;
    this.title=title;
  questionData();

  } 





  Future<void> get getdataFromProject async {
    return await questionData();
  }



  Future<void> questionData() async {

    int count = 0;

  Future<DocumentSnapshot> snapshot =
        Firestore.instance.collection('Projects').document(this.docID).get();


    return await snapshot.then((DocumentSnapshot questionSnap) => {
          questionSnap.data.forEach((key, value) {
            if ('$key' == 'count') {
              count = value;
              count--;
              //returncount=count;
            } else if ('$key' == ('Question' + count.toString())) {
              print(value['Type']);
              Questions question = new Questions(
                type: value['Type'],
                number: value['Number'],
                question: value['Question'],
              );
              if (value['Type'] == 'MultipleChoice') {
                value['Answers'].forEach((e) {
                  question.answers.add(e.toString());
                });
                //question.answers.addAll(value['Answers']);
              }
              questions.add(question);

              count--;
            }
          }),
        });

  }


 int getType(int index) {
    switch (questions[index].type) {
      case 'TextInputItem':
        return 0;
      case 'MultipleChoice':
        return 1;
      case 'ShortAnswerItem':
        return 2;
      case 'UserLocation':
        return 3; 
    }
    return -1;
  }
class ViewProject extends StatefulWidget {
  final String docIDref;
  final String title;
  ViewProject({this.docIDref, this.title});

  @override
  _ViewProjectState createState() => _ViewProjectState();
}

class _ViewProjectState extends State<ViewProject> {
  GetProject project;
  int _currentQuestion = 0;
  Future projectFuture;


  int _getType(_currentQuestion) {
    switch(project.questions[_currentQuestion].type){
      case 'TextInputItem':
        return 0;
      case 'MultipleChoice':
        return 1;
      case 'ShortAnswerItem':
        return 2;
      case 'UserLocation':
        return 3; 
    }
    return -1;
  }

Widget build(BuildContext context) {

  return new MaterialApp(

      home: new Scaffold(
          appBar: AppBar(title: Text("Random Widget")),
          body: 
          project.questions.length == 0

         ? Center(child: CircularProgressIndicator()


         )
         :
          Center(child:

          FutureBuilder(
              initialData: 0,
              future: projectFuture,
              builder: (context, snapshot) {


                if(project.questions.length>0){
                  return getQuestionWidget();
               }
              else{

                  return CircularProgressIndicator();
               }
              }
          )
      )),
    );
}


   Widget getQuestionWidget() {

    switch(_getType(_currentQuestion++)){
      case 0:
        return Column(children: <Widget>[
          Text("TextInputItem", textScaleFactor: 4),
          getNextButton()
        ]);
        break;
      case 1:
        return Column(children: <Widget>[
          Text("MultipleChoice", textScaleFactor: 4),
          getNextButton()
        ]);
        break;
      case 2:
        return Column(children: <Widget>[
          Text("ShortAnswer", textScaleFactor: 4),
          getNextButton()
        ]);
        break;
      case 3:
        return Column(children: <Widget>[
          Text("UserLocation", textScaleFactor: 4),
          getNextButton()
        ]);
        break;
      case -1:
        return Column(children: <Widget>[
          Text("Submit Page", textScaleFactor: 4),
          //getNextButton()
        ]);
    }
  }


Widget getNextButton(){
      return RaisedButton(
          child: Text("NEXT"),
          color: Colors.red,
          onPressed: () {
            if(_currentQuestion < project.questions.length){
                return getQuestionWidget();
            }
            return Text("All done!"); 
            //setState(() {

              //_currentQuestion++;
              //return getQuestionWidget();

              //_getType(_currentQuestion);
           // });

          }
      );
  }

  @override
  void initState() {
    project = new GetProject(widget.title, widget.docIDref);
    //project.getdataFromProject();
    //_getQuestions();
    projectFuture=_getQuestions();
    super.initState();

  }

  Future<void> _getQuestions() async {

    return await project.getdataFromProject;


  }

  // Call this function when you want to move to the next page
  void goToNextPage() {

      _currentQuestion++;

  }


}

【问题讨论】:

  • 我不想从build 方法开始加载数据。今天早些时候在这里查看我对类似问题的回答:stackoverflow.com/questions/61487131/…
  • @FrankvanPuffelen 我确实尝试在构造函数中添加它。问题仍然是即使我将对象放入构造函数中,该对象在构建小部件之前也没有得到问题。我的 getquestions() 函数可能有问题吗?
  • 没问题,这是按预期工作的。由于数据来自云端,您的应用程序可能需要一段时间才能获取数据。您的build 方法需要能够处理数据尚不可用的事实。要么你在这个小部件的构建中有一些条件渲染,比如isLoaded ? widgetWithData : "Loading...",要么使用这个的小部件会跳过这个小部件,直到它的数据被加载。
  • 所以当对象未完成时,我已经渲染了循环进度指示器。但是,除非我在调试时热重载,否则它永远不会停止并切换到主小部件。另外,您是否建议我根本不使用未来的构建器而只使用 switch 语句小部件?
  • 很可能FutureBuilder也可以使用,我发现当我开始直接使用setState时效率更高。在您当前的方法中,我的猜测是您的 return await snapshot.then... 实际上从未返回任何内容。但我肯定发现这比调用setState() 更难调试。

标签: flutter dart google-cloud-firestore


【解决方案1】:

我通常在 didChangeDependencies 方法中加载期货。

类似:-

@override
didChangeDependencies() { 
   if (this.asyncdata == null) 
        loadData();
}

Future<void> loadData() async {
    this.asyncdata = await someAsyncFuntionThatReturnsData();
}

这应该会有所帮助。在按下按钮的那一天加载东西总是更容易,因为这些功能可以是异步期货,但上述模式也应该解决 if 的双重加载。

【讨论】:

    【解决方案2】:

    我会为每个元素使用单独的小部件并在 initState 中加载数据。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-04
      • 2021-03-03
      • 1970-01-01
      • 1970-01-01
      • 2011-09-13
      • 2019-07-23
      相关资源
      最近更新 更多