# Streamlit Patterns ## Session State Initialization Always initialize session state at the start of the main function: ```python # app.py import streamlit as st def main(): # Initialize all session state variables if "session_id" not in st.session_state: st.session_state.session_id = None if "current_motion_index" not in st.session_state: st.session_state.current_motion_index = 0 if "motions" not in st.session_state: st.session_state.motions = [] if "show_results" not in st.session_state: st.session_state.show_results = False # Rest of app... ``` ## Page Configuration Set page config at the top of each page file: ```python # pages/1_Stemwijzer.py import streamlit as st st.set_page_config( page_title="Stemwijzer", page_icon="πŸ—³οΈ", layout="centered", ) from explorer import build_mp_quiz_tab build_mp_quiz_tab("data/motions.db") ``` ## Thin Page Wrapper Pattern Pages delegate to shared functions in main modules: ```python # pages/2_Explorer.py import streamlit as st st.set_page_config( page_title="Explorer", page_icon="πŸ”­", layout="wide", ) from explorer import build_explorer_tab build_explorer_tab() ``` ```python # explorer.py def build_explorer_tab(): st.header("πŸ”­ Politiek Explorer") tab1, tab2, tab3 = st.tabs([ "Compass", "Trajectories", "Zoeken" ]) with tab1: render_compass() with tab2: render_trajectories() with tab3: render_search() ``` ## Sidebar Pattern Use sidebar for configuration and navigation: ```python # app.py def main(): with st.sidebar: st.header("Instellingen") motion_count = st.slider( "Aantal moties", min_value=5, max_value=25, value=10, ) policy_area = st.selectbox("Beleidsgebied", config.POLICY_AREAS) if st.button("Start Nieuwe Sessie"): start_new_session(motion_count, policy_area) ``` ## Callback Pattern for State Updates Use callbacks to handle user interactions: ```python def on_motion_vote(motion_id: int, vote: str): """Callback when user votes on a motion.""" st.session_state.user_votes[motion_id] = vote # Move to next motion if st.session_state.current_motion_index < len(st.session_state.motions) - 1: st.session_state.current_motion_index += 1 else: st.session_state.show_results = True st.rerun() # In UI col1, col2, col3 = st.columns(3) with col1: st.button("πŸ‘ Voor", on_click=on_motion_vote, args=(motion_id, "Voor")) with col2: st.button("πŸ‘Ž Tegen", on_click=on_motion_vote, args=(motion_id, "Tegen")) with col3: st.button("❓ Onthouden", on_click=on_motion_vote, args=(motion_id, "Onthouden")) ``` ## Container Pattern for Dynamic Content Use containers for dynamic rendering: ```python def show_motion_interface(): if not st.session_state.motions: st.warning("Geen moties geladen") return current_idx = st.session_state.current_motion_index motion = st.session_state.motions[current_idx] with st.container(): st.subheader(f"Motie {current_idx + 1} van {len(st.session_state.motions)}") st.markdown(f"**{motion['title']}**") st.caption(f"πŸ“… {motion['date']} | 🏷️ {motion['policy_area']}") if motion.get("layman_explanation"): st.info(motion["layman_explanation"]) # Voting buttons... ``` ## Expander Pattern for Details Use expanders for collapsible content: ```python with st.expander("Meer details"): st.markdown(f"**Beschrijving:** {motion.get('description', 'N/A')}") if motion.get("voting_results"): results = json.loads(motion["voting_results"]) st.json(results) ``` ## Form Pattern for Batch Updates Use forms for multiple related inputs: ```python with st.form("session_settings"): st.subheader("Sessie Instellingen") col1, col2 = st.columns(2) with col1: count = st.number_input("Aantal moties", min_value=5, max_value=25) with col2: area = st.selectbox("Beleidsgebied", config.POLICY_AREAS) submitted = st.form_submit_button("Start Sessie") if submitted: start_session(count, area) ``` ## Caching Pattern Cache expensive computations: ```python @st.cache_data(ttl=3600) # Cache for 1 hour def load_party_positions(window_id: str) -> Dict: """Load party positions from database.""" return db.get_party_positions(window_id) @st.cache_resource def init_database(): """Initialize database connection.""" return MotionDatabase(config.DATABASE_PATH) ``` ## Home Page Pattern Landing page with navigation: ```python # Home.py import streamlit as st st.set_page_config( page_title="Motief: de stematlas", page_icon="πŸ—ΊοΈ", layout="centered", ) def main(): st.title("πŸ—ΊοΈ Motief: de stematlas") st.markdown("**Motief** brengt de Nederlandse Tweede Kamer in kaart...") col1, col2 = st.columns(2) with col1: st.page_link("pages/1_Stemwijzer.py", label="Open Stemwijzer", icon="πŸ—³οΈ") with col2: st.page_link("pages/2_Explorer.py", label="Open Explorer", icon="πŸ”­") ```