DEV Community

A0mineTV
A0mineTV

Posted on

Building an Interactive Quiz App with Streamlit ๐Ÿš€

Introduction

Streamlit is a fantastic framework for creating interactive web applications in Python. In this tutorial, weโ€™ll build a fully functional quiz app with real-time feedback, scoring, and an option to restart the quiz. This project demonstrates how to structure a Streamlit app using fragments for modularity and maintainability.

By the end of this article, youโ€™ll have a working quiz app with the following features:

  1. Display multiple-choice questions.
  2. Provide instant feedback on user responses.
  3. Track and display the userโ€™s score.
  4. Allow restarting the quiz.

Why Use Streamlit Fragments?

The @st.fragment decorator in Streamlit allows developers to modularize their app by encapsulating related components into reusable and independent blocks. This approach simplifies the development process, improves maintainability, and enhances performance by avoiding unnecessary re-runs of unrelated parts of the app.

In this quiz app, we utilize fragments to manage different functionalities such as displaying questions, providing feedback, showing scores, and restarting the quiz.

The Code

Hereโ€™s the complete code for the Streamlit quiz app:

import streamlit as st

# Initialize session state variables
if "questions" not in st.session_state:
    st.session_state.questions = [
        {"question": "What is the capital of France?", "options": ["Paris", "London", "Berlin"],
         "answer": "Paris"},
        {"question": "What is the largest ocean in the world?", "options": ["Atlantic", "Pacific", "Indian"],
         "answer": "Pacific"},
        {"question": "What is the chemical symbol for water?", "options": ["H2O", "O2", "CO2"], "answer": "H2O"},
    ]

if "current_question" not in st.session_state:
    st.session_state.current_question = 0

if "score" not in st.session_state:
    st.session_state.score = 0

if "feedback" not in st.session_state:
    st.session_state.feedback = None

@st.fragment
def question_fragment():
    """
        Fragment to display a question and capture the user's response.
    """
    question_data = st.session_state.questions[st.session_state.current_question]
    st.subheader(f"Question {st.session_state.current_question + 1}/{len(st.session_state.questions)}")
    st.write(question_data['question'])

    selected_option = st.radio('Choose an answer: ', question_data['options'])
    if st.button('Submit'):
        if selected_option == question_data['answer']:
            st.session_state.feedback = ('success', 'Correct! ๐ŸŽ‰')
            st.session_state.score += 1
        else:
            st.session_state.feedback = ("error", f"Wrong! The correct answer was: {question_data['answer']}")

        if st.session_state.current_question + 1 < len(st.session_state.questions):
            st.session_state.current_question += 1
            st.rerun()
        else:
            st.session_state.current_question = None
            st.rerun()

@st.fragment
def feedback_fragment():
    """
    Fragment to display feedback messages.
    """
    if st.session_state.feedback:
        msg_type, msg_content = st.session_state.feedback
        if msg_type == "success":
            st.success(msg_content)
        elif msg_type == "error":
            st.error(msg_content)
        st.session_state.feedback = None

@st.fragment
def score_fragment():
    """
        Fragment to display the userโ€™s current score.
    """
    st.metric('Current Score', st.session_state.score)

@st.fragment
def restart_quiz_fragment():
    """
        Fragment to restart the quiz.
    """
    if st.button('Restart Quiz'):
        st.session_state.current_question = 0
        st.session_state.score = 0
        st.session_state.feedback = None
        st.rerun()

# Main application
st.title('Interactive Quiz App')

feedback_fragment()

if st.session_state.current_question is not None:
    score_fragment()
    question_fragment()
else:
    st.subheader('Quiz Finished! ๐ŸŽ‰')
    st.write(f"Your final score is {st.session_state.score}/{len(st.session_state.questions)}.")
    restart_quiz_fragment()
Enter fullscreen mode Exit fullscreen mode

Code Breakdown

1. Session State Initialization

Session state variables ensure the persistence of quiz data (questions, current question index, score, and feedback) across user interactions. These variables are initialized only once during the app's lifecycle.

2. Fragments for Modular Design

The app uses Streamlit fragments (@st.fragment) to encapsulate functionality into reusable components:

  • question_fragment(): Displays the current question and options, validates the userโ€™s answer, and updates the state.
  • feedback_fragment(): Provides immediate feedback on the userโ€™s answer (correct or incorrect).
  • score_fragment(): Displays the userโ€™s score dynamically.
  • restart_quiz_fragment(): Resets all session state variables, allowing the user to restart the quiz.

3. Real-Time Feedback

After each response, the app displays a success or error message, enhancing the interactivity of the quiz.

4. Dynamic State Updates

The app updates the question index (current_question) and re-renders the interface after each question using st.rerun().

Features

  1. Dynamic Question Progression:

    • The app automatically moves to the next question after validation.
  2. Real-Time Feedback:

    • Users receive instant feedback on their answers.
  3. Score Tracking:

    • The score is displayed in real-time during the quiz.
  4. Restart Option:

    • Users can restart the quiz at any time.

Step-by-Step Execution

  1. Display a Question:

    • The question_fragment shows a single question at a time with its multiple-choice options.
    • Upon selecting an answer and clicking "Submit," the app validates the response and updates the state.
  2. Provide Feedback:

    • The feedback_fragment displays whether the answer was correct or incorrect.
    • Feedback is reset after displaying to avoid repetition.
  3. Update the Score:

    • Correct answers increment the score, shown dynamically using the score_fragment.
  4. End of Quiz:

    • When all questions are answered, the app displays the final score and provides an option to restart.

Potential Improvements

  1. Shuffle Questions:
    • Add functionality to randomize the order of questions for each quiz session.
   import random
   random.shuffle(st.session_state.questions)
Enter fullscreen mode Exit fullscreen mode
  1. Add a Timer:

    • Introduce a countdown timer for each question to increase difficulty.
  2. Custom Questions:

    • Allow users to input their own questions and answers before starting the quiz.
  3. Multiple Quiz Categories:

    • Provide options to choose from different quiz topics or categories.

Conclusion

This Streamlit quiz app showcases the power of modular design and interactive elements. With real-time feedback and score tracking, it provides an engaging user experience. You can enhance it further by adding features like question shuffling, timers, or multiple quiz topics.

Let us know in the comments how you plan to use this app or what features youโ€™d like to add! ๐Ÿš€


Happy coding! ๐ŸŽ‰

Top comments (0)