Problems when iterating over items using form

Iโ€™m trying to make an annotation tool using streamlit, my problem boils down to this:

import streamlit as st

if 'idx' not in st.session_state:
    st.session_state.idx = 0

some_people = ['Anders', 'Anna', 'Gustav']

def get_next_item():
    st.session_state.idx += 1

with st.form(key='person', clear_on_submit=True):
    person = some_people[st.session_state.idx]
    st.write(f"Person is {person}")

    person_checks_box = st.checkbox(f"{person} checks box?")

    submitted = st.form_submit_button('next')

    if submitted:
        st.write(person_checks_box) # in reality this is a call to a database
        get_next_item()

I iterate over items in a list and try to gather data about every item (in this case only a checkbox, but it could be a text field or multiple checkboxes). The problem is that after being presented with โ€œPerson is Andersโ€ and I click โ€œnextโ€ for the first time, Anders appears again. But his only happens once, subsequent clicks on โ€œnextโ€ actually gives the next person.

I instead tried to make get_next_item a callback on the submit button, like this

import streamlit as st

if 'idx' not in st.session_state:
    st.session_state.idx = 0

some_people = ['Anders', 'Anna', 'Gustav']

def get_next_item():
    st.session_state.idx += 1

with st.form(key='person', clear_on_submit=True):
    person = some_people[st.session_state.idx]
    st.write(f"Person is {person}")

    person_checks_box = st.checkbox(f"{person} checks box?")

    submitted = st.form_submit_button('next', on_click=get_next_item)

    if submitted:
        st.write(person_checks_box) # in reality this is a call to a database

That solves the problem of showing Anders twice, but now person_checks_box is always false, even if I have checked the box.

I assume there is something Iโ€™m missing here?

Just an FYI, I am also new to stream lit but I am facing a similar problem.

My guess on what is happening in your second code with the callback is that on form submission, the callback runs first, so you increment the index. After the index is incremented, then the return from checkbox is processed. But a new checkbox is created since the label is different. I think it will work if you pass in

st.checkbox(f"{person} checks box?", key='checkbox')

I am not sure if this is the exact behavior you wanted but I am able to see person_checks_change after the submit button is clicked now.

1 Like

Youโ€™re right, thanks a lot!

Although I wonder if there might be some better way of doing this. Looping through items and store user input about every item must be a common pattern?