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(),
],
),
],
),
);
}
}