Tạo ứng dụng làm trắc nghiệm bằng Flutter – Phần 1

Làm hay không bằng hay làm, hôm nay mình sẽ hướng dẫn các bạn làm một ứng dụng làm trắc nghiệm đơn giản bằng Flutter. Với ứng dụng này, mọi người sẽ hiểu thêm về UI, state trong Flutter. Bắt đầu nào! Khởi tạo ứng dụng Vối ứng dụng Flutter chúng ta có thể khởi

Làm hay không bằng hay làm, hôm nay mình sẽ hướng dẫn các bạn làm một ứng dụng làm trắc nghiệm đơn giản bằng Flutter.
Với ứng dụng này, mọi người sẽ hiểu thêm về UI, state trong Flutter.

Bắt đầu nào!

Khởi tạo ứng dụng

Vối ứng dụng Flutter chúng ta có thể khởi tạo đơn giản bằng lệnh

 flutter create app_name

Tất nhiên bạn phải đã phải setup đầy đủ môi trường từ trước. Xem thêm tại đây

Phát thảo ý tưởng

Chúng ta sẽ làm chức năng chính cần thiết đầu tiên, layout dựa vào wireframe ở trên.

Tạo đối tượng

Chúng ta tạo class Question với các trường thông tin cần sử dụng trong đó:

  • question là câu hỏi
  • options là danh sách đáp án
  • answer là đáp án đúng
  • feedback là lời giải
classQuestion{
  int id;String question;List<String> options;
  int answer;String feedback;Question({required this.id,
      required this.question,
      required this.options,
      required this.answer,
      required this.feedback});}

Đồng thời mình sẽ tạo data câu hỏi

finalList<Question> questionsData =[Question(
      id:1,
      question:"The three pillars of empiricism are: The three pillars of empiricism are: The three pillars of empiricism are: The three pillars of empiricism are:",
      options:['Planning, Inspection, Adaptation.','Transparency, Eliminating Waste, Kaizen.','Inspection, Transparency, Adaptation.','Planning, Demonstration, Retrospective.','Respect For People, Kaizen, Eliminating Waste.'],
      answer:1,
      feedback:"Scrum is founded on empirical process control theory, or empiricism. Empiricism asserts that knowledge comes from experience and making decisions based on what is known.nThree pillars uphold every implementation of empirical process control: transparency, inspection, and adaptation."),Question(
      id:2,
      question:"Who has the final say on the order of the Product Backlog?",
      options:['The Scrum Master.','The Product Owner.','The Stakeholders.','The Developers.','The CEO.'],
      answer:1,
      feedback:"The Product Owner is the sole person responsible for ordering the Product Backlog."),Question(
      id:3,
      question:"What is the recommended size for a Scrum Team?",
      options:["Minimum of 7.","9.","Typically 10 or fewer people.","7 plus or minus 2."],
      answer:2,
      feedback:"A Scrum Team is small enough to remain nimble and large enough to complete significant work within a Sprint, typically 10 or fewer people. Generally smaller teams communicate better and are more productive")];

Màn Home

Ở main.dart các bạn chỉnh sửa app điều hướng home là HomeScreen

class _MyAppState extendsState<MyApp>{Widgetbuild(BuildContext context){returnMaterialApp(
      debugShowCheckedModeBanner:false,
      title:'Quizzy',
      theme:ThemeData(
        primarySwatch:Colors.deepPurple,),
      home:HomeScreen(),);}}

Mình tạo một StatefulWidgetHomeScreen. Chứa một Button để start bắt đầu làm trắc nghiệm.

classHomeScreenextendsStatefulWidget{constHomeScreen({Key? key}):super(key: key);
  _HomeScreenState createState()=>_HomeScreenState();}class _HomeScreenState extendsState<HomeScreen>{Widgetbuild(BuildContext context){returnScaffold(
      body:Center(
        child:ElevatedButton.icon(
          onPressed:(){Navigator.push(
                context,MaterialPageRoute(builder:(_)=>QuizScreen()));},
          label:Text('Start'),
          icon:Icon(Icons.arrow_forward),),),);}}

Màn làm trắc nghiệm

Như ý tưởng mình sẽ tạo một appbar

appBar:AppBar(
        title:Text("QUIZ", style:TextStyles.titleHome),
        elevation:0,),

Mình tạo một biến _currentIndex để lưu lại vị trí câu hỏi hiện tại trong list câu hỏi.

  int _currentIndex =0;

Mình lấy ra câu hỏi trong danh sách câu hỏi bằng cách

final question = questionsData[_currentIndex];

Phần UI câu hỏi mình sẽ tạo một Row

Row(
              children:[CircleAvatar(
                  backgroundColor:Colors.deepPurple,
                  child:Text("${_currentIndex + 1}"),),SizedBox(width:16.0),Expanded(
                  child:Text(question.question),)],),

Với danh sách câu trả lời, mình sẽ tạo một Card

Card(
              child:Column(
                mainAxisSize:MainAxisSize.min,
                children:[...question.options.map((option)=>RadioListTile(
                      title:Text(option),
                      value: option,
                      groupValue: _answers[_currentIndex],
                      onChanged:(value){setState((){
                          _answers[_currentIndex]= option;});},),)],),),

Để lưu lại kết quả lựa chọn mfinh tạo một biến _answers

finalMap<int,dynamic> _answers ={};

Nút chuyển câu hỏi:

Expanded(
              child:Container(
                alignment:Alignment.bottomCenter,
                child:ElevatedButton.icon(
                  onPressed: _handleNext,
                  label:Text('Next'),
                  icon:Icon(Icons.arrow_forward),),),)

Mình xử lý logic khi bấn nút chuyển câu hỏi như sau:

  • Người dùng không chọn câu trả lời nào => Hiện dialog nhắc nhở.
  • Còn câu hỏi trong list câu hỏi => Hiện thị câu hỏi kế tiếp
  • Đã hết câu hỏi => Hiện thị trang kết quả
void_handleNext(){if(_answers[_currentIndex]==null){_showAlertDialog();return;}if(_currentIndex <(questionsData.length -1)){setState((){
        _currentIndex++;});}else{Navigator.of(context).pushReplacement(MaterialPageRoute(
        builder:(context)=>QuizResultScreen(),));}}

Khi mình set state cập nhật _currentIndex, câu hỏi sẽ được update lại.

Dùng showDialog để hiện dialog

void_showAlertDialog(){
    showDialog<String>(
      context: context,
      builder:(BuildContext context)=>AlertDialog(
        title:constText('Warning'),
        content:constText('You must attempt all questions to continue'),
        actions:<Widget>[TextButton(
            onPressed:()=>Navigator.pop(context,'OK'),
            child:constText('OK'),),],),);}

Và kết quả như sau

Ở phần sau chúng ta sẽ làm thêm màn hình kiểm tra kết quả.

Các bạn đón theo dõi nhé.

Link project: https://github.com/ngthanhphuc/Quizzy

Nguồn: viblo.asia

Bài viết liên quan

WebP là gì? Hướng dẫn cách để chuyển hình ảnh jpg, png qua webp

WebP là gì? WebP là một định dạng ảnh hiện đại, được phát triển bởi Google

Điểm khác biệt giữa IPv4 và IPv6 là gì?

IPv4 và IPv6 là hai phiên bản của hệ thống địa chỉ Giao thức Internet (IP). IP l

Check nameservers của tên miền xem website trỏ đúng chưa

Tìm hiểu cách check nameservers của tên miền để xác định tên miền đó đang dùn

Mình đang dùng Google Domains để check tên miền hàng ngày

Từ khi thông báo dịch vụ Google Domains bỏ mác Beta, mình mới để ý và bắt đầ