Let's build an Android Quiz Application with Appwrite

πŸ€” What is Appwrite?
In case you’re new to Appwrite, Appwrite is an new open-source, end-to-end, backend server for front-end and mobile developers that allows you to build apps much faster. Its goal is to abstract and simplify common development tasks behind REST APIs and tools, helping you to build advanced apps faster.
πŸ€– Appwrite for Android
Appwrite provides sleek REST APIs for various common features that are required for mobile application development such as a database, storage, cloud functions, authentication etc. so that we as a developer can focus on our application rather than on backend implementation. This makes Appwrite very suitable for those of us who want to build Android applications. And with the latest release, Appwrite now has an official Android SDK to make it even easier to use Appwrite in our Android applications. In this tutorial we'll build a simple android quiz application using Appwrite. So let's get started.
πŸ“ Prerequisites
In order to continue with this tutorial, you need to have an Appwrite console that you can access and have permission to create a project or already have an existing project. If you have not already installed Appwrite, please do so. Installing Appwrite is really simple following Appwrite's official installation docs. Installation should be done in less than 2 minutes. Once installed login to your console and create a new Project.
πŸ’Ύ Setup Database
In the Appwrite console, let's select the project that we will be using for our Android app. If you don't have a project yet, you can easily create one by clicking on the Create Project button. Once inside, select Database from the left sidebar. Once on the database page click on the Add Collection button and inside the dialog set the collection name to Questions and then click the Create button. This will then create a collection and you will be redirected to the new collection's page where we can define rules for our collection. Define the following rules and click on the Update button.
  • Question
    • label: Question
    • Key: question
    • Rule Type: Text
    • Required: true
    • Array: false
  • Options
    • label: Options
    • Key: options
    • Rule Type: Text
    • Required: true
    • Array: true
  • Answer
    • label: Answer
    • Key: answer
    • Rule Type: Text
    • Required: true
    • Array: false
  • Now you can navigate to the Documents tab and using the Add Document button, add as many questions with options and answers as you like for your quiz application. Make sure to add a few questions so that we can easily fetch and build our quiz application in Android.
    βš™οΈ Setup Android Project and Dependencies
    Using Android Studio, create a new Android Application project choosing the Empty Activity template. Once the project is created, in your app's root level build.gradle(.kts) file, add the mavenCentral() repository in order to fetch the Appwrite Android SDK.
    repositories {      
        mavenCentral()
    }
    Next, add the dependency to your app's build.gradle(.kts) file:
    implementation("io.appwrite:sdk-for-android:0.0.1")
    βž•οΈ Add Android Platform
    To initialize your SDK and start interacting with Appwrite services, you need to add a new Android platform to your project. To add a new platform, go to your Appwrite console, select your project (create one if you haven't already), and click the 'Add Platform' button on the project Dashboard.
    From the options, choose to add a new Android platform and add your app credentials.
    Add your app name and package name. Your package name is generally the applicationId in your app-level build.gradle file. You may also find your package name in your AndroidManifest.xml file. By registering a new platform, you are allowing your app to communicate with the Appwrite API.
    βš’οΈ Build Activity and Layout
    Add new activity by going to File>New>Activity>Empty Activity and name it QuizActivity. This will create QuizActivity.kt and activity_quiz.xml files in their respective folders.
    Now, open your app/src/main/res/layout/activity_main.xml and update the layout as following
    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/darkslategray"
        tools:context=".MainActivity">
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="16dp"
            android:fontFamily="sans-serif-medium"
            android:text="Demo Quiz With Android"
            android:textColor="@color/darkgray"
            android:textSize="24sp"
            app:layout_constraintBottom_toTopOf="@+id/btnPlay"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent" />
    
        <Button
            android:id="@+id/btnPlay"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Play Now"
            style="@style/buttonStyle"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    Then, open app/src/main/java/com/example/demoquizwithandroid/MainActivity.kt and update the onCreate function like so:
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val button = findViewById<Button>(R.id.btnPlay)
        button.setOnClickListener {
            val intent = Intent(this, QuizActivity::class.java)
            startActivity(intent)
        }
    }
    Now, open app/src/main/res/layout/activity_quiz.xml and update the layout to the following:
    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="24dp"
        android:background="@color/darkslategray"
        tools:context=".QuizActivity">
    
        <TextView
            android:id="@+id/questionNumber"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="16dp"
            android:text="Question 5/15"
            android:textSize="32sp"
            android:textStyle="bold"
            android:textColor="@color/darkgray"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    
        <ImageView
            android:id="@+id/dashedLine"
            android:layout_width="match_parent"
            android:layout_height="4dp"
            android:layout_marginTop="16dp"
            android:src="@drawable/dashed_line"
            app:layout_constraintTop_toBottomOf="@id/questionNumber"
            android:layerType="software" />
    
        <TextView
            android:id="@+id/question"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_marginTop="16dp"
            android:text="Question"
            android:textColor="@color/white"
            android:textSize="26sp"
            android:textStyle="bold"
            app:layout_constraintBottom_toTopOf="@+id/options"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/dashedLine" />
    
        <RadioGroup
            android:id="@+id/options"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginBottom="24dp"
            app:layout_constraintBottom_toTopOf="@+id/btnNext"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent">
    
            <RadioButton
                android:id="@+id/option1"
                style="@style/optionRadioButton"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Option 1"
                tools:layout_editor_absoluteX="116dp"
                tools:layout_editor_absoluteY="75dp" />
    
            <RadioButton
                android:id="@+id/option2"
                style="@style/optionRadioButton"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Option 2" />
    
            <RadioButton
                android:id="@+id/option3"
                style="@style/optionRadioButton"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Option 3" />
    
            <RadioButton
                android:id="@+id/option4"
                style="@style/optionRadioButton"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Option 4" />
        </RadioGroup>
    
        <Button
            android:id="@+id/btnNext"
            style="@style/buttonStyle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="8dp"
            android:text="Next"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent" />
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    πŸ‘©β€πŸ”§ Create View Model
    Create app/src/main/java/com/example/demoquizwithandroid/QuizViewModel.kt and update it with following code. Make sure to replace YOUR_APPWRITE_ENDPOINT, YOUR_PROJECT_ID and YOUR_QUESTIONS_COLLECTION_ID with your values which can all be found in your Appwrite console.
    import android.content.Context
    import android.util.Log
    import androidx.lifecycle.LiveData
    import androidx.lifecycle.MutableLiveData
    import androidx.lifecycle.ViewModel
    import androidx.lifecycle.viewModelScope
    import io.appwrite.Client
    import io.appwrite.exceptions.AppwriteException
    import io.appwrite.services.Database
    import kotlinx.coroutines.launch
    import org.json.JSONArray
    import org.json.JSONObject
    
    class QuizViewModel : ViewModel() {
        private val collectionId = "YOUR_QUESTIONS_COLLECTION_ID" // Replace with your own questions collection id that we created above
        private val db by lazy {
            Database(client)
        }
        private val _questions = MutableLiveData<JSONArray>().apply { value = null }
        lateinit var client : Client
        val questions: LiveData<JSONArray> = _questions;
        val selectedQuestion = MutableLiveData<Int>().apply { value = 0}
        val correct = MutableLiveData<Int>().apply { value = 0 }
    
        fun create(context: Context) {
            client = Client(context)
                .setEndpoint("YOUR_APPWRITE_ENDPOINT") // Replace with your own endpoint
                .setProject("YOUR_PROJECT_ID") // Replace with your project id
            getQuestions()
        }
    
        private fun getQuestions() {
            viewModelScope.launch {
                try {
                    var response = db.listDocuments(collectionId)
                    val json = response.body?.string() ?: ""
                    var que = JSONObject(json)
                    _questions.postValue( que["documents"] as JSONArray)
                } catch(e : AppwriteException) {
                    Log.e("Get questions",e.message.toString())
                }
            }
        }
    }
    For this to work you also need to add following dependencies to your app/build.gradle file. These are the dependencies required for LiveData and ViewModel to work.
    dependencies{
        implementation 'androidx.appcompat:appcompat:1.3.0'
        implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1'
        implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1'
    }
    Finally update app/src/main/java/com/example/demoquizwithandroid/QuizActivity.kt with the following code and build your application
    class QuizActivity : AppCompatActivity() {
        private lateinit var viewModel: QuizViewModel
        private var currentQuestion : JSONObject? = null;
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_quiz)
            viewModel = ViewModelProvider(this).get(QuizViewModel::class.java)
            viewModel.create(this)
            viewModel.questions.observe(this, {
                questions ->
                if(questions != null) {
                    viewModel.selectedQuestion.observe(this, { index ->
                        findViewById<TextView>(R.id.questionNumber).text = "Question ${(index+1)}/${questions.length()}"
                        currentQuestion = questions[index] as JSONObject
    
                        val options = currentQuestion!!["options"] as JSONArray
                        findViewById<TextView>(R.id.question).text = currentQuestion!!["question"].toString()
                        findViewById<RadioButton>(R.id.option1).text = options[0].toString()
                        findViewById<RadioButton>(R.id.option2).text = options[1].toString()
                            val option3= findViewById<RadioButton>(R.id.option3)
                            val option4=findViewById<RadioButton>(R.id.option4)
                        if(options.length() > 2) {
                                option4.text = options[3].toString()
                                option3.text = options[2].toString()
                            option3.visibility = View.VISIBLE
                            option4.visibility = View.VISIBLE
                        } else {
                            option3.visibility = View.GONE
                            option4.visibility = View.GONE
    
                        }
                    })
                }
            })
            findViewById<Button>(R.id.btnNext).setOnClickListener {
                _ -> onNextClicked()
            }
        }
    
        private fun onNextClicked() {
            if(currentQuestion == null) return
            val rbGroup : RadioGroup = findViewById<RadioGroup>(R.id.options)
            val rbSelected : RadioButton = findViewById(rbGroup.checkedRadioButtonId) ?: return
            val ans: String = rbSelected.text.toString();
            if (ans == currentQuestion!!["answer"].toString()) {
                viewModel.correct.postValue(viewModel.correct.value?.inc())
            }
            rbGroup.clearCheck()
            if(viewModel.selectedQuestion.value!! >= (viewModel.questions.value!!.length() - 1)) {
                //show complete dialog
                Log.d("Quiz complete","Quiz complete")
                val alertDialog = AlertDialog.Builder(this)
                    .setTitle("Quiz complete")
                    .setMessage("Score: ${viewModel.correct.value!!} / ${viewModel.questions.value!!.length()}")
                    .setNeutralButton("Done", DialogInterface.OnClickListener { _, _ ->
                        val intent = Intent(this, MainActivity::class.java)
                            .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
                            .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                    startActivity(intent)
                })
                alertDialog.show()
            } else {
                viewModel.selectedQuestion.postValue(viewModel.selectedQuestion.value?.inc())
            }
        }
    }
    We should now have our complete quiz application which should look as follows.
    πŸ₯‚ Conclusion
    I hope you enjoyed building this Quiz Application with Appwrite and Android. Let's us know if you have any feedback and suggestions. Can't wait for the community to build awesome apps with Appwrite and Android.
    πŸŽ“ Learn More

    52

    This website collects cookies to deliver better user experience

    Let's build an Android Quiz Application with Appwrite