driftavalii
driftavalii

Reputation: 1339

Fetch JSON list data from api

I am trying to fetch a list of questions from an endpoint that returns json formatted like so:

[
  {
    "id": 1,
    "question": "text",
    "option1": "text",
    "option2": "text",
    "option3": "text",
    "option4": "text",
    "answer": "text"
  },
  {
    "id": 2,
    "question": "text",
    "option1": "text",
    "option2": "text",
    "option3": "text",
    "option4": "text",
    "answer": "text"
  }
]

Following the example here, I have a quiz class:

class Quiz extends Object with _$QuizSerializerMixin {
  List<Question> questions;

  Quiz(this.questions);

  factory Quiz.fromJson(Map<String, dynamic> json) => _$QuizFromJson(json);
}

and question class:

class Question extends Object with _$QuestionSerializerMixin {

  final String question;
  final String option1;
  final String option2;
  final String option3;
  final String option4;
  final String answer;

  Question(
      {this.question,
      this.option1,
      this.option2,
      this.option3,
      this.option4,
      this.answer});

  factory Question.fromJson(Map<String, dynamic> json) =>
      _$QuestionFromJson(json);
}

with the function defined as:

Future<Quiz> fetchQuiz() async {
  final response = await http.get('json_endpoint.placeholder/questions');

  if (response.statusCode == 200) {
    return Quiz.fromJson(json.decode(response.body));
  } else {
    throw Exception('Failed to load quiz');
  }
}

how do I pass this into a new instance of quiz? This may be obvious but I am finding it difficult.

I have tried declaring a variable as follows:

class QuizScreen extends StatefulWidget {
  @override
  _QuizScreenState createState() => _QuizScreenState();
}

class _QuizScreenState extends State<QuizScreen> {
Question question
String option1
...

Quiz quiz = quiz(fetchQuiz());

@override
void initState() {
super.initState();
question = quiz.nextQuestion;
option1 = question.option1;
...

Quiz quiz = Quiz(fetchQuiz()); throws an error.

Upvotes: 1

Views: 1245

Answers (2)

Jacob Phillips
Jacob Phillips

Reputation: 9264

I think you're looking for FutureBuilder. It lets you build UI when the Future completes. The result will be in snapshot.data if snapshot.hasData == true.

@override
  Widget build(BuildContext context) {
    return Scaffold(
        body: FutureBuilder<Quiz>(
      future: fetchQuiz(),
      builder: (context, snapshot) {
        if (snapshot.hasData) {
          return _buildQuiz(context, snapshot.data);
        } else if (snapshot.hasError) {
          return Text('${snapshot.error}');
        }
        return Center(child: CircularProgressIndicator());
      },
    ));
  }

void _buildQuiz(BuildContext context, Quiz quiz) {

}

Upvotes: 1

G&#252;nter Z&#246;chbauer
G&#252;nter Z&#246;chbauer

Reputation: 657318

The code should look like

Future foo() async {
  Quiz quiz = await fetchQuiz();
} 

fetchQuiz() returns a Future<Quiz> and to get the value out, you use await, for that the function that contains the code (foo() in my example) needs to be made async.

Upvotes: 2

Related Questions