1

Hello I am new to flutter and have created a form builder app. I am using Cloud Firestore as my database. The issue I am having is trying to render specific widgets based on a conditional. I have build the form and have the object data set up like this: Each projects has a list of Questions objects that hold the question information. I want to iterate through the question objects and check the question type then based on that question type render a certain widget (EX. Multiple choice, image upload, short answer, etc). Ideally it would be display as one question per page with a next button. I would like to stay on one page and just call/display other widgets/functions as the user presses next.

The issue is that I can not get the widgets to render and/or redirect by a conditional statement. Right now I set up a simple for each loop with a switch statement nested inside however since the iteration continues until it the last object the pages never render. My goal is to add a button when on pressed would continue the iteration.

I have a view project page that sets up the GetProject object and then calls the necessary class functions to get the data from firestore.

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 {
  final String docID;
  final String title;
  GetProject({this.docID, this.title});

  List<Questions> questions = new List();

   //....Class Functions below not relevant

}

Here is what I have so far for the view project page

import 'package:flutter/material.dart';
import '../../models/project.dart';
import '../../Services/getproject.dart';
import 'textquestion.dart';
import 'multiplechoicequestion.dart';
import 'UserLocationInfo.dart';
import 'shortanswerquestion.dart'; 

class ViewProject extends StatefulWidget {
  final String docIDref;
  final String title;
  ViewProject({this.docIDref, this.title});
  @override
  _ViewProjectState createState() => _ViewProjectState();
}

class _ViewProjectState extends State<ViewProject> {


   @override

  Widget build(BuildContext context) {
  GetProject project = new GetProject(docID: widget.docIDref, title: widget.title);
  project.getdataFromProject();
  project.questions.forEach((e){
    var type = e.type;
    switch(type){
      case 'TextInputItem':
      return new TextQuestionWidget(question: e);
      case 'MultipleChoice':
      return new MultQuestionWidget(question: e);
      case 'ShortAnswer':
      return new ShortAnswerQuestion(question: e); 
      case 'UserLocation':
      return new UserLocationInfo(question: e); 
    }
    return null; 
  });
  //project.printproj();
 return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),

      body: Container(
        child: Card(
                  child: ListTile(
                    title: Text('Print Values to debug console'),
                    subtitle: Text(''),
                    trailing: Icon(Icons.arrow_forward_ios),
                    onTap: () {
                      project.printproj();
                    }
                  ),
            ),

      ),
 );

  }
}

Here is an example of what the called widget will look like

import '../../Services/getproject.dart';

class UserLocationInfo extends StatefulWidget {
  final Questions question;
  UserLocationInfo({this.question});
  @override
  _UserLocationInfoState createState() => _UserLocationInfoState();
}

class _UserLocationInfoState extends State<UserLocationInfo> {
  @override
  Widget build(BuildContext context) {
    return Container(

    );
  }
}

2 Answers 2

3

Very Interesting Question!!

I posted some code using FutureBuilder. When you push the button labeled 'NEXT' a Widget is displayed based in a random number (random number could be a database result)

Thank you.

import 'package:flutter/material.dart';
import 'dart:math';

void main() {
  runApp(new MyApp());
}

class MyApp extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return MyAppState();
  }
}


class MyAppState extends State<MyApp> {
  @override
  Future<int> _newRandomNumber() async{
    print("_newRandomNumber");
    return await Random().nextInt(4);
  }

  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new Scaffold(
          appBar: AppBar(title: Text("Random Widget")),
          body: Center(child:
          FutureBuilder(
              initialData: 0,
              future:_newRandomNumber(),
              builder: (context, snapshot) {
                if(snapshot.hasData){
                  return getRandomWidget(snapshot.data);
                }
              }
          )
      )),
    );
  }

  Widget getRandomWidget(int randomNumber) {
    switch(randomNumber){
      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;
    }
  }


  Widget getNextButton(){
      return RaisedButton(
          child: Text("NEXT"),
          color: Colors.red,
          onPressed: () {
            setState(() {
              _newRandomNumber();
            });

          }
      );
  }
}

Sign up to request clarification or add additional context in comments.

3 Comments

I have never used future builder. This looks promising I will try and implement it and let you know. Thank you!
This is issue I am having is I set up the project object to contain all of the questions.So I need to call getQuestions before the Future Builder. Then I need to call get type in the future data member of future builder. The issue is that the project is not initialized before this.
I dont know if this helps but usually If you need an initial setup, you can use the FutureBuilder parameter constructor 'initialData' or overwrite the method initState.
2

It seems like you have few problems that needs to be addressed in your code, so this might not be what exactly you want to achieve, but it will give you a general directions to go from here.

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 _currentPage = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      // when there is no questions(still loading), show a progress indicator
      body: project.questions == null
          ? Center(child: CircularProgressIndicator())
          // IndexedStack will hide all children except the "index"th child
          : IndexedStack(
              index: _currentPage,
              children: project.questions
                  .map((question) => _question(question))
                  .toList(),
            ),
    );
  }

  @override
  void initState() {
    project = GetProject(docID: widget.docIDref, title: widget.title);
    super.initState();
  }

  // Call this function when you want to move to the next page
  void goToNextPage() {
    setState(() {
      _currentPage++;
    });
  }

  Future<void> _getQuestions() async {
    // you mentioned you use firebase for database, so 
    // you have to wait for the data to be loaded from the network
    await project.getdataFromProject();
    setState(() {});
  }

  Widget _question(Questions question) {
    switch (question.type) {
      case 'TextInputItem':
        return TextQuestionWidget(question: e);
      case 'MultipleChoice':
        return MultQuestionWidget(question: e);
      case 'ShortAnswer':
        return ShortAnswerQuestion(question: e);
      case 'UserLocation':
        return UserLocationInfo(question: e);
    }
  }
}

I was not sure where you wanted to call the goToNextPage(), so you would have to decide where to call it.

1 Comment

I do like how this example I am going to try an implement it. Thank you for you response!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.