skip to Main Content

I have a quiz screen where I am using an API with FutureBuilder. Each time build method is refreshed, the new question is fetched. There’s a submit button at the bottom to save the response and reset the screen. What I want to do is to disable the submit button until new question is fetched after pressing the submit button and make enabled when new question is rebuild. I cannot call the setstate to make it null with a bool variable because new question is loaded due to this. Here’s my code to reproduce the issue:

import 'package:flutter/material.dart';

class QuizForm extends StatefulWidget {
  const QuizForm({Key? key}) : super(key: key);

  @override
  State<QuizForm> createState() => _QuizFormState();
}

class _QuizFormState extends State<QuizForm> {

  int buildCount = 0 ;

  getQuestion () {}

  @override
  Widget build(BuildContext context) {
    print(buildCount);
    print('Question Fetched and UI is building');
    return SafeArea(child: Scaffold(
      body: FutureBuilder(
        future: getQuestion(),
        builder: (context, snapshot){
          return ListView(
            children: [
              ListTile(title: Text('Quiz Title'),),
              ListTile(title: Text('1'),),
              ListTile(title: Text('2'),),
              ListTile(title: Text('3'),),
              ListTile(title: Text('4'),),
              SizedBox(height: 20,),

              ElevatedButton(
                  onPressed: () async {
                    print('Please Wait, Answer is getting Saved');
                    // Button Should be shown disabled for 3 seconds
                    await Future.delayed(const Duration(seconds: 3));
                    buildCount++;

                    setState(() {
                  // this setState rebuilds the screen and new question is loaded
                  // because of future builder
                });
              }, child: Text('Submit Quiz'))
            ],
          );
        },
      ),
    ));
  }
}

2

Answers


  1. Chosen as BEST ANSWER

    I managed to get it through ValueListenableBuilder. Here is my code that is working as expected:

    import 'package:flutter/material.dart';
    
    class QuizForm extends StatefulWidget {
      const QuizForm({Key? key}) : super(key: key);
    
      @override
      State<QuizForm> createState() => _QuizFormState();
    }
    
    class _QuizFormState extends State<QuizForm> {
      final _buttonEnabled = ValueNotifier(true);
    
      int buildCount = 0;
    
      getQuestion () {}
    
      @override
      Widget build(BuildContext context) {
        print(buildCount);
        return SafeArea(
          child: Scaffold(
            body: FutureBuilder(
              future: getQuestion(),
              builder: (context, snapshot) {
                return ListView(
                  children: [
                    ListTile(title: Text('Quiz Title')),
                    ListTile(title: Text('1')),
                    ListTile(title: Text('2')),
                    ListTile(title: Text('3')),
                    ListTile(title: Text('4')),
                    SizedBox(height: 20),
    
                    ValueListenableBuilder(
                      valueListenable: _buttonEnabled,
                      builder: (context, value, child) {
                        return ElevatedButton(
                          onPressed: _buttonEnabled.value
                              ? () async {
                            _buttonEnabled.value = false;
                            print('Please Wait, Answer is getting Saved');
                            await Future.delayed(const Duration(seconds: 3));
                            _buttonEnabled.value = true;
                            buildCount++;
                            setState(() {
    
                            });
                          }
                              : null,
                          child: child,
                        );
                      },
                      child: Text('Submit Quiz'),
                    ),
                  ],
                );
              },
            ),
          ),
        );
      }
    }
    

  2. When you are getting data from API check if you have data in your variable , if has data return data if not then call API ,

    update : with _submitEnabled value .

    Here example :

        import 'package:flutter/material.dart';
    import 'package:http/http.dart' as http;
    import 'dart:convert';
    
    class QuizForm extends StatefulWidget {
      const QuizForm({Key? key}) : super(key: key);
    
      @override
      State<QuizForm> createState() => _QuizFormState();
    }
    
    class _QuizFormState extends State<QuizForm> {
      Question _cachedQuestion;
      bool _submitEnabled = false;
    
      Future<Question> getQuestion() async {
        if (_cachedQuestion != null) {
          return _cachedQuestion;
        }
        final response = await http.get('https://your-api-endpoint.com/question');
        if (response.statusCode == 200) {
          final question = Question.fromJson(json.decode(response.body));
          _cachedQuestion = question;
          _submitEnabled = true;
          return question;
        } else {
          throw Exception('Failed to fetch question');
        }
      }
    
      @override
      Widget build(BuildContext context) {
        return SafeArea(
          child: Scaffold(
            body: FutureBuilder(
              future: getQuestion(),
              builder: (context, snapshot) {
                if (snapshot.hasData) {
                  final question = snapshot.data;
                  return ListView(
                    children: [
                      ListTile(title: Text(question.title)),
                      ListTile(title: Text(
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search