我正在尝试构建一个智力竞赛应用程序。当任何用户开始参与测试时,计时器就会启动,并且不知道为什么每秒钟都会重新初始化所有内容。布尔参数"answered"每秒设置为false。因此,参与者可以多次回答同一个问题,这会导致错误的结果,而且不会发生。这里有一个片段-
class MathQuizPlay extends StatefulWidget {
final String quizId;
MathQuizPlay({Key key, this.quizId}) : super(key: key);
@override
_MathQuizPlayState createState() => _MathQuizPlayState(this.quizId);
}
int total = 0;
int _correct = 0;
int _incorrect = 0;
int _notAttempted = 0;
int timer;
String showtimer;
class _MathQuizPlayState extends State<MathQuizPlay> {
var quizId;
_MathQuizPlayState(this.quizId);
QuerySnapshot questionSnapshot;
bool isLoading = true;
getQuestionData(String quizId) async {
return await Firestore.instance
.collection('math')
.document(quizId)
.collection('QNA')
.getDocuments();
}
@override
void initState() {
getQuestionData(quizId).then((value) {
questionSnapshot = value;
setState(() {
total = questionSnapshot.documents.length;
_correct = 0;
_incorrect = 0;
_notAttempted = questionSnapshot.documents.length;
isLoading = false;
timer = total * 15;
showtimer = timer.toString();
});
});
starttimer();
super.initState();
}
@override
void setState(fn) {
if (mounted) {
super.setState(fn);
}
}
Questions getQuestionModelFromDatasnapshot(
DocumentSnapshot questionSnapshot) {
final Questions questionModel = Questions(
question: questionSnapshot.data['question'],
option1: questionSnapshot.data['option1'],
option2: questionSnapshot.data['option2'],
option3: questionSnapshot.data['option3'],
option4: questionSnapshot.data['option4'],
correctOption: questionSnapshot.data['correctOption'],
answered: false);
return questionModel;
}
void starttimer() async {
const onesec = Duration(seconds: 1);
Timer.periodic(onesec, (Timer t) {
setState(() {
if (timer < 1) {
t.cancel();
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => Result(
correct: _correct,
incorrect: _incorrect,
total: total,
notattempt: _notAttempted,
collection: 'math',
quizId: quizId,
)));
} else {
timer = timer - 1;
}
showtimer = timer.toString();
});
});
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.teal[300],
title: Text("Questions",
style: TextStyle(
color: Colors.white,
)),
elevation: 5.0,
centerTitle: true,
),
body: isLoading
? Container(
child: Center(child: CircularProgressIndicator()),
)
: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
SizedBox(
height: 10,
),
Center(
child: Container(
height: 60,
width: 60,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(36),
border: Border.all(
width: 2.0,
color: Colors.red.withOpacity(0.8),
),
),
child: Center(
child: Text(
showtimer,
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 19.0,
color: Colors.red.withOpacity(0.8)),
)))),
SizedBox(
height: 10,
),
Center(child: Text('Tap on the option to select answer')),
SizedBox(
height: 10,
),
questionSnapshot.documents == null
? Container(
child: Center(
child: Text("No Data"),
),
)
: ListView.builder(
itemCount: questionSnapshot.documents.length,
shrinkWrap: true,
physics: ClampingScrollPhysics(),
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.symmetric(
vertical: 15.0, horizontal: 25),
child: QuizPlayTile(
questionModel: getQuestionModelFromDatasnapshot(
questionSnapshot.documents[index]),
index: index,
),
);
}),
SizedBox(
height: 30,
),
Center(
child: RaisedButton(
padding:
EdgeInsets.symmetric(vertical: 18, horizontal: 60),
color: Colors.teal[300],
textColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: new BorderRadius.circular(8.0)),
child: Text(
'Submit',
style: TextStyle(fontSize: 16),
),
elevation: 7.0,
onPressed: () {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => Result(
correct: _correct,
incorrect: _incorrect,
total: total,
notattempt: _notAttempted,
collection: 'math',
quizId: quizId,
)));
}),
),
SizedBox(
height: 50,
)
],
),
),
);
}
}
问题:
当你每次定时器完成一秒时都调用setState。。。。它每秒钟都会刷新你的屏幕。因此,构建函数在每秒钟重新构建该函数内的每一行代码后都会被重新调用。现在的主要问题是,每当屏幕刷新时,函数"getQuestionModelFromDatasnapshot"将始终提供一个新的"questionModel",其中"answered"参数的值为false。
解决方案:
现在我有两个解决方案:
-
良好的编程实践:我的猜测是,您在每秒钟后调用setState来更新UI中的计时器值。现在,你必须意识到,只为一个小的更改调用setState是不好的,因为它会刷新所有其他小部件,这也是不必要的。好的方法是使用ChangeNotificationProvider,它将侦听计时器中的更新,然后用Consumer包装显示计时器值的UI文本。因此,无论何时更新计时器值。。。。它只会使用计时器值更新UI文本。
-
如果您想要快速解决方案:您可以做的不是在构建函数中调用"getQuestionModelFromDatasnapshot"方法,而是将initstate中的整个ListView初始化为变量。。。。类似:ListView x=/*包含该函数的列表视图代码*/。。。。。这样做。。。。。。小部件不会每秒都重建一次。
代码有点乱。。。。。。。我强烈希望正确地重新编写代码,或者像我在第一个选项中所说的那样使用ChangeNotificationProvider。
我也无法使用ChangeNotificationProvider解决问题。但幸运的是,我找到了一个包裹,完全解决了我的问题。所以我用了那个软件包而不是周期性的Timer来设置时间。这是更新-
import 'package:circular_countdown_timer/circular_countdown_timer.dart';
class PhyQuizPlay extends StatefulWidget {
final String quizId;
PhyQuizPlay({Key key, this.quizId}) : super(key: key);
@override
_PhyQuizPlayState createState() => _PhyQuizPlayState(this.quizId);
}
int total = 0;
int _correct = 0;
int _incorrect = 0;
int _notAttempted = 0;
int timer;
class _PhyQuizPlayState extends State<PhyQuizPlay> {
var quizId;
_PhyQuizPlayState(this.quizId);
QuerySnapshot questionSnapshot;
bool isLoading = true;
getQuestionData(String quizId) async {
return await Firestore.instance
.collection('physics')
.document(quizId)
.collection('QNA')
.getDocuments();
}
@override
void initState() {
getQuestionData(quizId).then((value) {
questionSnapshot = value;
setState(() {
total = questionSnapshot.documents.length;
_correct = 0;
_incorrect = 0;
_notAttempted = total;
isLoading = false;
timer = total * 15;
});
});
super.initState();
}
Questions getQuestionModelFromDatasnapshot(
DocumentSnapshot questionSnapshot) {
final Questions questionModel = Questions(
question: questionSnapshot.data['question'],
option1: questionSnapshot.data['option1'],
option2: questionSnapshot.data['option2'],
option3: questionSnapshot.data['option3'],
option4: questionSnapshot.data['option4'],
correctOption: questionSnapshot.data['correctOption'],
answered: false);
return questionModel;
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.teal[300],
title: Text("Questions",
style: TextStyle(
color: Colors.white,
)),
elevation: 5.0,
centerTitle: true,
),
body: isLoading
? Container(
child: Center(child: CircularProgressIndicator()),
)
: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
SizedBox(
height: 10,
),
Center(
child: CircularCountDownTimer(
width: 80,
height: 80,
duration: timer,
fillColor: Colors.red,
color: Colors.white38,
isReverse: true,
onComplete: () {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => Result(
correct: _correct,
incorrect: _incorrect,
total: total,
notattempt: _notAttempted,
collection: 'physics',
quizId: quizId,
)));
},
)),
SizedBox(
height: 10,
),
Center(child: Text('Tap on the option to select answer')),
SizedBox(
height: 10,
),
questionSnapshot.documents == null
? Center(
child: CircularProgressIndicator(),
)
: ListView.builder(
itemCount: questionSnapshot.documents.length,
shrinkWrap: true,
physics: ClampingScrollPhysics(),
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.symmetric(
vertical: 15.0, horizontal: 25),
child: QuizPlayTile(
questionModel: getQuestionModelFromDatasnapshot(
questionSnapshot.documents[index]),
index: index,
),
);
}),
SizedBox(
height: 30,
),
Center(
child: RaisedButton(
padding:
EdgeInsets.symmetric(vertical: 18, horizontal: 60),
color: Colors.teal[300],
textColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: new BorderRadius.circular(8.0)),
child: Text(
'Submit',
style: TextStyle(fontSize: 16),
),
elevation: 7.0,
onPressed: () {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => Result(
correct: _correct,
incorrect: _incorrect,
total: total,
notattempt: _notAttempted,
collection: 'physics',
quizId: quizId,
)));
}),
),
SizedBox(
height: 50,
)
],
),
),
);
}
}