تطبيق اساله واجوابه باستخدام فلاتر بشكل بسيط
عندما تتعلم في تطوير تطبيقات الجوال تحتاج دائما الى تنفيذ عدد كبير من التطبيقات لكي تتاكد من انك قادر على تنفيذ مشاريع للعملاء ومن ضمن المشاريع التي تطلب بشكل كبير وهيا انشاء تطبيق quiz app حيث يقوم التطبيق بوضع مجموعة من الاسئله والاختيارات حيث يتمكن المستخدم من قراءة السؤال واختيار الاجابة الصحيحه من بين مجموعة الاسئلة , هذا التطبيق بسيط جدا ومع ذلك يوجد به افكار جديده قد تحسن من مستواك البرمجي بشكل كبير وتمكنك من ان تصبح قادر على بناء تطبيقات قوية بشكل رائع وبسرعه مع الدقه في تفنيذ التصميم .
توجد اطارات عمل مشتركة بين الأنظمة الأساسية مثل Xamarin و React Native بالفعل في السوق لتطوير تطبيقات iOS و Android بقاعدة رمز واحدة. بينما يشارك Flutter المفاهيم مع React Native و Xamarin ، تختلف البنية الفنية لجميع الاطارات الثلاثة اختلافًا كبيرًا. دعنا نرى كيف تتراكم Flutter مقابل أطر العمل المشتركة بين الأنظمة الأساسية باستخدام المعايير التالية. دون إجراء أي مقارنات مع الأنظمة الأساسية الأخرى ، إليك بعض الميزات والسمات التي يمكن أن تغريك بتجربة Flutter: في ديسمبر 2018 ، أصبح Flutter مستقرًا. ثم بدأ في إنشاء تطبيقات فيه ، ووصل إلى درجة غير مسبوقة من النشوة. هذا رائع؛ إنه أمر رائع بالنسبة لنا ، ولكن ماذا يعني ذلك لمطور البرامج الرائد لديك؟
model class for questions
في هذا الclass عباره عن نموذج يمكنك من وضع مجموعة اسالة واجابات كما هو موضح بالشكل التالي تستطيع نسخه ولصقه لديك في المشروع الذي تعمل عليه .
Model.dart
class QuestionModel {
final String question; // questions
final List<Option> option; // answers
bool isLocked;
Option? selectedOption;
QuestionModel({
required this.question,
required this.option,
this.selectedOption,
this.isLocked = false,
});
}
class Option {
final String text; // string answer
final bool isCorrect; // is true or false .
const Option({
required this.text,
required this.isCorrect,
});
}
final questions = [
QuestionModel(question: 'what is the best website to learn development ?',
option: [
const Option(text: 'geecoders', isCorrect: true),
const Option(text: 'geekscoders', isCorrect: false),
const Option(text: 'gecocoder', isCorrect: false),
const Option(text: 'geekcoder', isCorrect: false),
],
),
QuestionModel(question: 'hat is your favorite color? ?',
option: [
const Option(text: 'black', isCorrect: true),
const Option(text: 'blue', isCorrect: false),
const Option(text: 'yellow', isCorrect: false),
const Option(text: 'red', isCorrect: false),
],
),
QuestionModel(question: 'what the best website to learn development ?',
option: [
const Option(text: 'geecoders', isCorrect: true),
const Option(text: 'geekscoders', isCorrect: false),
const Option(text: 'gecocoder', isCorrect: false),
const Option(text: 'geekcoder', isCorrect: false),
],
),
QuestionModel(question: 'what is your favorite city ?',
option: [
const Option(text: 'ciro', isCorrect: false),
const Option(text: 'zagazig', isCorrect: false),
const Option(text: '10Th Ramadan', isCorrect: true),
const Option(text: 'another', isCorrect: false),
],
),
QuestionModel(question: 'what is your favorite car ?',
option: [
const Option(text: 'Ferrari', isCorrect: false),
const Option(text: 'bugatti', isCorrect: false),
const Option(text: 'Mercedes', isCorrect: false),
const Option(text: 'Dodge', isCorrect: true),
],
),
QuestionModel(question: 'what is your favorite language ?',
option: [
const Option(text: 'dart', isCorrect: false),
const Option(text: 'python', isCorrect: false),
const Option(text: 'java', isCorrect: true),
const Option(text: 'another', isCorrect: false),
],
),
];
تصميم لصفحة عرض الاسئلة وعدد الاسئلة في فلاتر
في هذا الجزء سوف يكون الصفحة الرئيسيه التي يعمل عليها التطبيق والتي تبدء بسؤال ومجموعة من الاجابات وسوف نقوم بتصميم شكل الاجابات في الجزء التالي سوف يكون عباره عن appbar تقوم بتصميمه يعبر عن السؤال الذي نقف عليه الان وعدد الاسئله الموجوده , واسفلها مجموعة من الاختيارات وعند اختيار اي عنصر يتم اظهار زر للانتقال الى السؤال التالي .
Question_page.dart
class QuestionWidget extends StatefulWidget {
const QuestionWidget({Key? key}) : super(key: key);
@override
State<QuestionWidget> createState() => _QuestionWidgetState();
}
class _QuestionWidgetState extends State<QuestionWidget> {
int _questionNumber = 1;
late PageController _controller;
int _score = 0;
bool isLocked = false;
void initState() {
super.initState();
_controller = PageController(initialPage: 0);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
const SizedBox(height: 32,),
Text('Question $_questionNumber/${questions.length}'),
const Divider(thickness: 1, color: Colors.grey,),
Expanded
(child: PageView.builder(
itemCount: questions.length,
controller: _controller,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context,index) {
return buildQuestion(questions[index]);
})),
isLocked ? buildElevatedButton() : const SizedBox.shrink(),
const SizedBox(height: 20,),
],
),),
);
}
// button show is isLocked true and if Question is final ? result page
ElevatedButton buildElevatedButton() {
return ElevatedButton(
onPressed: (){
if ( _questionNumber < questions.length) {
_controller.nextPage(
duration : const Duration(microseconds: 250),
curve : Curves.easeInExpo,
);
setState((){
_questionNumber++;
isLocked = false;
});
}
else {
navToPush(context, ResulPage(score: _score));
}
},
child: Text(
_questionNumber < questions.length ? 'Next' : 'Result',
)
);
}
// design .
Column buildQuestion(QuestionModel questionModel){
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 32,),
// appbar
Text(questionModel.question , style: const TextStyle(fontSize: 25),),
const SizedBox(height: 32,),
// list of Option (answers)
Expanded(
child: OptionWidget(
// send question to show answers
question : questionModel,
// click any item from answers
onClick: (Option value) {
// if locked any item selectedOption = value is clicked .
// and isLocked is true
if (questionModel.isLocked) {
return;
} else {
setState((){
questionModel.isLocked = true;
questionModel.selectedOption = value;
});
// if locked is true ? _score ++ .
isLocked = questionModel.isLocked;
if (questionModel.selectedOption!.isCorrect) {
_score++;
}
}
},
))
],
);
}
}
تصميم لصفحة عرض الاجابات الخاصه بالاسئله في فلاتر
هذا الجزء عباره عن تصميم للاجابات الخاصه بالسؤال كل ما سوف نقوم به هو اننا نحصل على الindex الخاص بالسؤال وبعدها نمر على جميع الاجابات التي توجد لهذا السؤال لكي نقوم بعدها بتنفيذها بشكل سليم كما هو موضح تم استخدام map لكي نمر على جميع العناصر الموجوده ونقوم بعمل تصميم مخصص لهم .
answers.dart
class OptionWidget extends StatelessWidget {
const OptionWidget({Key? key,required this.question , required this.onClick}) : super(key: key);
final QuestionModel question;
final ValueChanged<Option> onClick;
@override
Widget build(BuildContext context) {
return Column(
// loop all answers for Question and show .
children: question.option.map((e) => buildOption(context,e)).toList(),
);
}
// design Question
Widget buildOption(BuildContext context , Option e) {
// color answer after click .
final color = getColorForOption(e,question);
return GestureDetector(
onTap: ()=> onClick(e),
child: Container(
height: 50,
padding: EdgeInsets.all(12),
margin: EdgeInsets.symmetric(vertical: 8),
decoration: BoxDecoration(
color: Colors.grey.shade200,
borderRadius: BorderRadius.circular(16),
// if color is true return green else red .
border: Border.all(color: color),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
e.text,
style: const TextStyle(fontSize: 20),
),
// if Icon is true return green else red .
getIconForOption(e,question),
],
),
),
);
}
Color getColorForOption(Option e,QuestionModel question) {
final isSelected = e == question.selectedOption;
if (question.isLocked) {
if (isSelected) {
return e.isCorrect ? Colors.green : Colors.red;
} else if (e.isCorrect){
return Colors.green;
}
}
return Colors.grey.shade300;
}
Widget getIconForOption(Option e,QuestionModel question) {
// isSelected value is e and e value is question.selectedOption
final isSelected = e == question.selectedOption; // your choice
print('is Seledted ------------ $isSelected');
if (question.isLocked) {
if (isSelected) {
// if your choice is false .
return e.isCorrect ? const Icon(Icons.check_circle , color: Colors.green,) :
const Icon(Icons.cancel , color: Colors.red,);
}else if (e.isCorrect){
// if your choice is true .
return const Icon(Icons.check_circle , color: Colors.green,);
}
}
return const SizedBox.shrink();
}
}
تصميم صفحة عرض نتيجة الطالب بعد الانتهاء من الامتحان داخل فلاتر
الجزء الاخير وهو الجزء الذي يتم من خلاله عرض نتيجة الطالب وهي عباره عن مجموع الاجابات الصحيحه / مجموع عدد الاسئله يمكنك تنفيذ عملية حسابية لكي تظهر النسبة المئوية للطالب اذا اردت ذلك او تغيير شكل التصميم الذي تعرض بداخلها هذه العناصر .
result.dart
class ResulPage extends StatelessWidget {
const ResulPage({Key? key,required this.score}) : super(key: key);
final int score;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text('your Reslt is $score/${questions.length}'),
),
);
}
}
لمزيد من المقالات
- شرح كيفية تحديث flutter وDart الى احدث اصدار
- شرح كيفية عمل post للبيانات من نوع params داخل الapi في flutter
- شرح كيفية استخدام الapi مع repostery في Flutter وتنظيم الكود
- شرح كيفية التاكد من الاتصال بالانترنت وفي حالة عدم الاتصال اظهار صفحة عدم وجود انترنت
- شرح كيفية تكبيره الصورة والتحكم في جميع تفاصيلها بواسطة flutter