Elonas Marcauskas
Elonas Marcauskas

Reputation: 361

Generating widgets in Pageview based on conditional logic

Currently facing a problem with conditional logic and PageView widget. Let's say that the PageView will dynamically generate 3 pages. In those 3 pages different widgets will be generated. One of the widgets is a button (called "Next"), which is a PageController, but that widget has to be replaced by a button widget that is supposed to submit (called "Submit") the whole form (the PageView is wrapped in a form).

It seems obvious, just write conditional logic that compares the current page of the PageView to the length of the PageView (PageView is populated with a List, so it is easy to get the length). Then switch the widgets when the right conditions meet: when current page equals to 3, change the widget. Unfortunately, the PageView renders the "Next" button on every page. So only when I get to the last page and then click "Next" again will it change to "Submit". It is supposed to be "Submit", when the user gets on the last page.

const int TRIVIA_STARTING_TIME = 10;

class TriviaOneForm extends StatefulWidget {
  final UserRepository _userRepository;

  TriviaOneForm({Key key, @required UserRepository userRepository})
      : assert(userRepository != null),
        _userRepository = userRepository,
        super(key: key);

  State<TriviaOneForm> createState() => _TriviaOneFormState();
}

class _TriviaOneFormState extends State<TriviaOneForm> {
  final TextEditingController _answerController = TextEditingController();

  UserRepository get _userRepository => widget._userRepository;

  TriviaOneBloc _triviaOneBloc;
  PageController _pageController;

  Timer _timer;

  bool _isLoadingScreen;
  bool _isNextOrSubmitButton;
  int _start;
  int _indexOfCarouselItem;
  List<int> _selectedValList;

  List _triviaDataList;

  @override
  void initState() {
    super.initState();
    _isLoadingScreen = true;
    _getTriviaData();
    _pageController = PageController();
    _indexOfCarouselItem = 0;
    _isNextOrSubmitButton = true;
    _selectedValList = [0, 0, 0, 0, 0];
    _triviaDataList = [];
    _start = TRIVIA_STARTING_TIME;
    _triviaOneBloc = BlocProvider.of<TriviaOneBloc>(context);
    _answerController.addListener(_onAnswerChanged);
  }

  @override
  void dispose() {
    if (_timer != null) {
      _timer.cancel();
    }
    _pageController.dispose();
    super.dispose();
  }

  void startTimer() {
    const oneSec = const Duration(seconds: 1);
    _timer = new Timer.periodic(
      oneSec,
      (Timer timer) => setState(
        () {
          if (_start < 1) {
            timer.cancel();
          } else {
            _start = _start - 1;
          }
        },
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return BlocListener<TriviaOneBloc, TriviaOneState>(
      listener: (context, state) {
        if (state.isFailure) {
          Scaffold.of(context)
            ..hideCurrentSnackBar()
            ..showSnackBar(
              SnackBar(
                content: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: <Widget>[
                    Text('Submition Failure'),
                    Icon(Icons.error)
                  ],
                ),
                backgroundColor: Colors.red,
              ),
            );
        }
        if (state.isSubmitting) {
          Scaffold.of(context)
            ..hideCurrentSnackBar()
            ..showSnackBar(
              SnackBar(
                content: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: <Widget>[
                    Text('Submitting Answers...'),
                  ],
                ),
              ),
            );
        }
        if (state.isSuccess) {
          BlocProvider.of<TriviaOneBloc>(context).add(Submitted());
        }
      },
      child: BlocBuilder<TriviaOneBloc, TriviaOneState>(
        builder: (context, state) {
          return _isLoadingScreen
              ? _displayLoadScreen()
              : Padding(
                  padding: EdgeInsets.all(20.0),
                  child: Form(
                    child: PageView(
                      physics: NeverScrollableScrollPhysics(),
                      controller: _pageController,
                      reverse: false,
                      scrollDirection: Axis.horizontal,
                      children: _triviaDataList.map<Widget>((triviaData) {
                        return ListView(
                          shrinkWrap: true,
                          children: <Widget>[
                            Text(triviaData.getQuestion),
                            ListView(
                              shrinkWrap: true,
                              children: triviaData.getAnswers
                                  .map<Widget>((triviaAnswer) {
                                int index =
                                    triviaData.getAnswers.indexOf(triviaAnswer);
                                return ListTile(
                                  title: Text(triviaAnswer.getAnswer),
                                  leading: Radio(
                                    value: index,
                                    groupValue:
                                        _selectedValList[_indexOfCarouselItem],
                                    onChanged: (int value) {
                                      setState(() {
                                        print(value);
                                        _selectedValList[_indexOfCarouselItem] =
                                            value;
                                      });
                                    },
                                  ),
                                );
                              }).toList(),
                            ),
                            _isNextOrSubmitButton ? _nextButton() : _submitButton(),
                            RaisedButton(
                              onPressed: () {
                                startTimer();
                              },
                              child: Text('Start'),
                            ),
                            Text('$_start'),
                          ],
                        );
                      }).toList(),
                    ),
                  ),
                );
        },
      ),
    );
  }

  Widget _triviaControlButton(PageController pageController) {
    if (0 < _triviaDataList.length) {
      return RaisedButton(
        child: Text('Next'),
        onPressed: () {
          pageController.nextPage(
              duration: Duration(seconds: 1), curve: Curves.easeInOut);
          print('Next');
        },
      );
    } else if (pageController.page.toInt() == _triviaDataList.length) {
      return RaisedButton(
        child: Text('Submit'),
        onPressed: () {
          print('Submit');
        },
      );
    } else {
      return RaisedButton(
        child: Text('Error'),
        onPressed: () {
          print('Error');
        },
      );
    }
  }

  Widget _displayLoadScreen() {
    return Container(
      alignment: Alignment(0.0, 0.0),
      child: CircularProgressIndicator(),
    );
  }

  void _onAnswerChanged() {
    _triviaOneBloc.add(AnswerChanged(answer: _answerController.text));
  }

  void _getTriviaData() async {
    var data = _userRepository.retrieveTriviaData();
    // Await trivia data to be retrieved from firebase
    await data.getDocuments().then((collection) {
      collection.documents.forEach((document) {
        TriviaData triviaData = TriviaData();
        List<TriviaAnswer> triviaAnswerList = List<TriviaAnswer>();
        // Iterate through all of the answers for a question
        // Create a list of TriviaAnswer objects to hold key and value
        document.data['answers'].forEach((key, value) {
          TriviaAnswer triviaAnswer = TriviaAnswer();
          triviaAnswer.setAnswer = key;
          triviaAnswer.setAnswerValue = value;
          triviaAnswerList.add(triviaAnswer);
        });
        // Assign question String and answer List to TriviaData
        // Add all data to data list
        triviaData.setAnswers = triviaAnswerList;
        triviaData.setQuestion = document.data['question'];
        _triviaDataList.add(triviaData);
      });
    });
    setState(() {
      _isLoadingScreen = false;
    });
  }

  Widget _nextButton() {
    return RaisedButton(
      child: Text('Next'),
      onPressed: () {
        if (_indexOfCarouselItem < _triviaDataList.length) {
          _pageController.nextPage(
              duration: const Duration(milliseconds: 100),
              curve: Curves.easeInOut);
          setState(() {
            _start = TRIVIA_STARTING_TIME;
            _indexOfCarouselItem += 1;
          });
        }
        if (_indexOfCarouselItem == _triviaDataList.length) {
          Future.delayed(const Duration(seconds: 0), () {
            setState(() {
              _isNextOrSubmitButton = false;
            });
          });
        }
        try {
          if (_timer != null || !_timer.isActive) {
            startTimer();
          }
        } catch (_) {
          print('Error: Timer is already disabled');
        }
      },
    );
  }

  Widget _submitButton() {
    return RaisedButton(
      child: Text('Submit'),
      onPressed: () {
        print(_selectedValList);
        _userRepository.storeTriviaToFirebase();
        setState(() {
          if (_timer != null || _timer.isActive) {
            _timer.cancel();
          }
        });
      },
    );
  }
}

EDIT 1: This is the updated code I use for the button to populate in the PageView. I am setting a String to initial value "Next" then updating it when _indexOfCarouselItem + 2 == _triviaDataList.length is true. The updated value will be "Submit", when the condition is met.

Widget _triviaControlButton() {
    return RaisedButton(
      child: Text(buttonText),
      onPressed: () {
        _pageController.nextPage(
            duration: const Duration(milliseconds: 100),
            curve: Curves.easeInOut);
        if (_indexOfCarouselItem + 2 == _triviaDataList.length) {
          setState(() {
            buttonText = "Submit";
          });
        }
        if (_indexOfCarouselItem < _triviaDataList.length) {
          setState(() {
            _start = TRIVIA_STARTING_TIME;
            _indexOfCarouselItem += 1;
          });
        }
        print(_indexOfCarouselItem);
        print(_triviaDataList.length);
      },
    );
  }

Upvotes: 0

Views: 1009

Answers (1)

i6x86
i6x86

Reputation: 1627

I’m on phone now so I can’t guarantee the code I’ll post is ok, but you’ll get the idea.

First: I don’t think you need 2 buttons if they are equals on size etc. so you can implement something like this:

child: Text( _indexOfCarouselItem += 1 !=  _triviaDataList.length
 ? 'Next' : 'Submit')

And then use the same logic in the onPressed:

onPressed() {
   _indexOfCarouselItem += 1 != _triviaDataList.length ? doSomethibg : doSomethingDifferent;
}

Edit: Ok if I understand correctly the problem right now is that because of the transition the button says "Submit" but there are no question yet? If this is the case you can like you said, add delay, but I think a better approach will be wire the text of the button with the question. I mean you can keep the actual logic (because it works) and add something like this:

child: Text( _indexOfCarouselItem += 1 != _triviaDataList.length && questionText != "" ? 'Next' : 'Submit')

This logic can be applied in if ... else ... block too.

Edit 2: try this one:

Widget _triviaControlButton() {
    return RaisedButton(
      child: Text(buttonText),
      onPressed: () {
        _pageController.nextPage(
            duration: const Duration(milliseconds: 100),
            curve: Curves.easeInOut);
        if (_indexOfCarouselItem < _triviaDataList.length) {
          setState(() {
            _start = TRIVIA_STARTING_TIME;
            _indexOfCarouselItem += 1;
          });
if (_indexOfCarouselItem == _triviaDataList.length) {
          setState(() {
            buttonText = "Submit";
          });
        }
      },
    );
  }

Upvotes: 1

Related Questions