Search code examples
flutterdartflutter-getx

How do I reset my controller when I come back or finish?


enter image description here

I have a QuestionController class extends GetxController

When I exit the Page using the controls, I want it to stop working (because it is still running in the background) and to start again if I come back to that page.

I've tried: I added these after the route of the ScoreScreen() (in nextQuestion ()) :

_isAnswered = false;
_questionNumber.value = 1; 

I reset the values ​​before going to the score page. It may work if you go to the score page, but if you come back earlier, it won't. (up side Question num/4 does not work here). So this way is not suitable.

What is the way I can stop and reset it when the page exits?

Controller class code:

class QuestionController extends GetxController
    with SingleGetTickerProviderMixin {
  PageController _pageController;

  PageController get pageController => this._pageController;

  List<Question> _questions = questions_data
      .map(
        (e) => Question(
            id: e["id"],
            question: e["question"],
            options: e["options"],
            answer: e["answer_index"]),
      )
      .toList();

  List<Question> get questions => this._questions;

  bool _isAnswered = false;

  bool get isAnswered => this._isAnswered;

  int _correctAns;

  int get correctAns => this._correctAns;

  int _selectedAns;

  int get selectedAns => this._selectedAns;

  RxInt _questionNumber = 1.obs;

  RxInt get questionNumber => this._questionNumber;

  int _numOfCorrectAns = 0;

  int get numOfCorrectAns => this._numOfCorrectAns;

  @override
  void onInit() {
    _pageController = PageController();
    super.onInit();
  }

  @override
  void onClose() {
    super.onClose();
    _pageController.dispose();
  }

  void checkAns(Question question, int selectedIndex) {
    _isAnswered = true;
    _correctAns = question.answer;
    _selectedAns = selectedIndex;

    if (_correctAns == _selectedAns) _numOfCorrectAns++;

    update();

    Future.delayed(Duration(seconds: 2), () {
      nextQuestion();
    });
  }

  void nextQuestion() {
    if (_questionNumber.value != _questions.length) {
      _isAnswered = false;
      _pageController.nextPage(
          duration: Duration(milliseconds: 300), curve: Curves.ease);
    } else {
      Get.off(ScoreScreen(correctNum: _numOfCorrectAns)); // GetMaterialApp()

      // _isAnswered = false;

      _numOfCorrectAns = 0;

      //_questionNumber.value = 1;

    }
  }

  void updateTheQuestionNum(int index) {
    _questionNumber.value = index + 1;
  }
}

Full code below

import 'package:flutter/material.dart';
import 'dart:async';
import 'package:get/get.dart';  // get: ^3.25.4

//  QuizPage()     ===============> 50. line      (Question 1/4) 81. line
//  QuestionCard()  ==============> 116. line
//  Option()   ===================> 163. line
//  QuestionController()  ========> 218. line
//  ScoreScreen() ================> 345. line

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(canvasColor: Colors.blue),
      home: HomeScreen(),
    );
  }
}

class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Home Page"),
      ),
      body: Center(
        child: InkWell(
          onTap: () {
            Navigator.push(
                context, MaterialPageRoute(builder: (context) => QuizPage()));
          },
          child: Container(
            padding: EdgeInsets.all(22),
            color: Colors.green,
            child: Text(
              "Go Quiz Page",
              style: TextStyle(color: Colors.white),
            ),
          ),
        ),
      ),
    );
  }
}

class QuizPage extends StatelessWidget {
  const QuizPage({
    Key key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    QuestionController _questionController = Get.put(QuestionController());

    return Scaffold(
      appBar: AppBar(
        title: Text("Quiz Page"),
      ),
      body: Stack(
        children: [
          SafeArea(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                SizedBox(
                  height: 16,
                ),
                Padding(
                  padding: EdgeInsets.symmetric(horizontal: 16),
                  child: Obx(
                    () => Center(
                      child: RichText(
                          text: TextSpan(
                              // text,style default adjust here OR:children[TextSpan(1,adjust 1),TextSpan(2,adjust 2),..]
                              text:
                                  "Question ${_questionController._questionNumber.value}",
                              style: TextStyle(
                                  fontSize: 33, color: Colors.white70),
                              children: [
                            TextSpan(
                                text:
                                    "/${_questionController._questions.length}",
                                style: TextStyle(fontSize: 25))
                          ])),
                    ),
                  ),
                ),
                Divider(color: Colors.white70, thickness: 1),
                SizedBox(
                  height: 16,
                ),
                Expanded(
                  child: PageView.builder(
                    physics: NeverScrollableScrollPhysics(),
                    controller: _questionController._pageController,
                    onPageChanged: _questionController.updateTheQuestionNum,
                    itemCount: _questionController.questions.length,
                    itemBuilder: (context, index) => QuestionCard(
                      question: _questionController.questions[index],
                    ),
                  ),
                )
              ],
            ),
          )
        ],
      ),
    );
  }
}

class QuestionCard extends StatelessWidget {
  final Question question;

  const QuestionCard({
    Key key,
    @required this.question,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    QuestionController _controller = Get.put(QuestionController());

    return Container(
      margin: EdgeInsets.only(left: 16, right: 16, bottom: 16),
      padding: EdgeInsets.all(16),
      decoration: BoxDecoration(
        borderRadius: BorderRadius.circular(25),
        color: Colors.white,
      ),
      child: Column(
        children: [
          Text(
            question.question,
            style: TextStyle(fontSize: 22),
          ),
          SizedBox(
            height: 8,
          ),
          Flexible(
            child: SingleChildScrollView(
              child: Column(
                children: [
                  ...List.generate(
                      question.options.length,
                      (index) => Option(
                          text: question.options[index],
                          index: index,
                          press: () => _controller.checkAns(question, index)))
                ],
              ),
            ),
          )
        ],
      ),
    );
  }
}

class Option extends StatelessWidget {
  final String text;
  final int index;
  final VoidCallback press;

  const Option({
    Key key,
    @required this.text,
    @required this.index,
    @required this.press,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return GetBuilder<QuestionController>(
        init: QuestionController(),
        builder: (q) {
          Color getRightColor() {
            if (q.isAnswered) {
              if (index == q._correctAns) {
                return Colors.green;
              } else if (index == q.selectedAns &&
                  q.selectedAns != q.correctAns) {
                return Colors.red;
              }
            }
            return Colors.blue;
          }

          return InkWell(
            onTap: press,
            child: Container(
              //-- Option
              margin: EdgeInsets.only(top: 16),
              padding: EdgeInsets.all(16),
              decoration: BoxDecoration(
                  color: getRightColor(),
                  borderRadius: BorderRadius.circular(16)),
              child: Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  Text(
                    "${index + 1}. $text",
                    style: TextStyle(fontSize: 16, color: Colors.white),
                  ),
                ],
              ),
            ),
          );
        });
  }
}

class QuestionController extends GetxController
    with SingleGetTickerProviderMixin {
  PageController _pageController;

  PageController get pageController => this._pageController;

  List<Question> _questions = questions_data
      .map(
        (e) => Question(
            id: e["id"],
            question: e["question"],
            options: e["options"],
            answer: e["answer_index"]),
      )
      .toList();

  List<Question> get questions => this._questions;

  bool _isAnswered = false;

  bool get isAnswered => this._isAnswered;

  int _correctAns;

  int get correctAns => this._correctAns;

  int _selectedAns;

  int get selectedAns => this._selectedAns;

  RxInt _questionNumber = 1.obs;

  RxInt get questionNumber => this._questionNumber;

  int _numOfCorrectAns = 0;

  int get numOfCorrectAns => this._numOfCorrectAns;

  @override
  void onInit() {
    _pageController = PageController();
    //_pageController.addListener(() { _questionNumber.value = _pageController.page.round()+1; });
    super.onInit();
  }

  @override
  void onClose() {
    super.onClose();
    _pageController.dispose();
  }

  void checkAns(Question question, int selectedIndex) {
    _isAnswered = true;
    _correctAns = question.answer;
    _selectedAns = selectedIndex;

    if (_correctAns == _selectedAns) _numOfCorrectAns++;

    update();

    Future.delayed(Duration(seconds: 2), () {
      nextQuestion();
    });
  }

  void nextQuestion() {
    if (_questionNumber.value != _questions.length) {
      _isAnswered = false;
      _pageController.nextPage(
          duration: Duration(milliseconds: 300), curve: Curves.ease);
    } else {
      Get.off(ScoreScreen(correctNum: _numOfCorrectAns)); // GetMaterialApp()

      // _isAnswered = false;

      _numOfCorrectAns = 0;

      //_questionNumber.value = 1;

    }
  }

  void updateTheQuestionNum(int index) {
    _questionNumber.value = index + 1;
  }
}

class Question {
  final int id, answer;
  final String question;
  final List<String> options;

  Question({
    @required this.id,
    @required this.question,
    @required this.options,
    @required this.answer,
  });
}

const List questions_data = [
  {
    "id": 1,
    "question": "Question 1",
    "options": ['option A', 'B', 'C', 'D'],
    "answer_index": 3,
  },
  {
    "id": 2,
    "question": "Question 2",
    "options": ['option A', 'B', 'C', 'D'],
    "answer_index": 2,
  },
  {
    "id": 3,
    "question": "Question 3",
    "options": ['option A', 'B', 'C', 'D'],
    "answer_index": 0,
  },
  {
    "id": 4,
    "question": "Question 4",
    "options": ['option A', 'B', 'C', 'D'],
    "answer_index": 0,
  },
];

class ScoreScreen extends StatelessWidget {
  final int correctNum;

  ScoreScreen({@required this.correctNum});

  @override
  Widget build(BuildContext context) {
    QuestionController _qController = Get.put(QuestionController());

    return Scaffold(
      body: Stack(
        fit: StackFit.expand,
        children: [
          Column(
            children: [
              Spacer(
                flex: 2,
              ),
              Text(
                "Score",
                style: TextStyle(fontSize: 55, color: Colors.white),
              ),
              Spacer(),
              Text(
                "${correctNum * 10}/${_qController.questions.length * 10}",
                style: TextStyle(fontSize: 33, color: Colors.white),
              ),
              Spacer(
                flex: 2,
              ),
              InkWell(
                onTap: () => Get.back(),
                borderRadius: BorderRadius.circular(16),
                child: Container(
                  padding: EdgeInsets.all(16),
                  decoration: BoxDecoration(
                      borderRadius: BorderRadius.circular(16),
                      color: Colors.white24),
                  child: Text(
                    "Back to Home Page",
                    style: TextStyle(color: Colors.white),
                  ),
                ),
              ),
              Spacer(),
            ],
          ),
        ],
      ),
    );
  }
}

Solution

  • Updated Answer:

    Ok, after seeing your full code my solution requires a couple extra steps.

    Add this to your controller class. It needs to be called in the onPressed from your HomeScreen

     void resetQuestionNumber() => _questionNumber.value = 1;
    

    You'll have to initialize the controller earlier so you can add this in your HomeScreen

    final _questionController = Get.put(QuestionController());
    

    The onTap of your HomeScreen now looks like this.

    onTap: () {
        _questionController.resetQuestionNumber();
        Navigator.push(
        context, MaterialPageRoute(builder: (context) => QuizPage()));
    },
    

    That should do it. The _pageController index wasn't updated until after a question was answered you so just need to reset _questionNumber before going to QuizPage and after that it catches up. Your updateTheQuestionNum can go away completely and you don't need to handle anything this in the onPageChanged of the PageView.builder anymore.

    Original Answer:

    If you just want that RxInt _questionNumber to match the value of the _pageController you could add a listener in your onInit

    _pageController.addListener(() {
      _questionNumber.value = _pageController.page.round() + 1;
    });
    

    Edit: Added + 1 to account for index starting at 0