【发布时间】: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