Make a widget remember its value after it is hidden and shown again in later script runs

Hi,
I made a multipage app with say page A and page B. I implemented the multipage functionality in a similar fashion to Trick - Simple MultPage using the streamlit-option-menu component.
Now, on page A I included a st.number_input widget whose value I can change. After switching to page B and triggering some script re-runs on page B, I would like to return to page A and have the st.number_input widget to remember its value as I left it before. Note: I also want the value of the st.number_input widget to be persisted in the session_state.

Is there any way I can implement this behaviour or kind of “simulate” it?

The problem is: Because of how widgets work, if the st.number_input widget is not shown anymore when navigating to page B, its value gets removed from the session_state.
I can somehow make it work if I use a key and value argument for the st.number_input widget but this will trigger a “was created with a default value but also had its value set via the Session State API.” warning unless I hack st.elements.utils._shown_default_value_warning=True.

Thank you!

Example code:

import streamlit as st
from streamlit import session_state as session


def init_app() -> None:

    if 'first_run' not in session:
        session['first_run'] = True
    
    if session['first_run']:
        session['n'] = 3      # i would like the initial value of number_input to be 3 on the first run
        session['first_run'] = False

def main() -> None:

    hide = st.checkbox('hide?', value=False)
    if not hide:
        n = int ( st.number_input(label='Number', min_value=1, max_value=5, step=1, key='n') )
        st.write(n)

    st.button("click to trigger re-run")
    st.json(session)

if __name__ == "__main__":
    init_app()
    main()

Hi @Wally - Here’s a working version of your app. It doesn’t actually need an init() method as the number widget value is initialized from a session variable anyway when the program first starts. Then I use a callback to memoize the number session variable from the number widget value. This would also work in a multi-page app.

import streamlit as st

session = st.session_state
if 'N' not in session:
    session.N = 3

def main() -> None:

    def _set_number_cb():
        session.N = session.n

    hide = st.checkbox('hide?', value=False)
    if not hide:
        st.number_input(label='Number', min_value=1, max_value=5, step=1, value=session.N, key='n', on_change=_set_number_cb)
        st.write(session.N)

    st.button("click to trigger re-run")
    st.json(session)

if __name__ == "__main__":
    main()

Hi @asehmi,
thank you so much. This is great.
I always got a “was created with a default value but also had its value set via the Session State API.” warning because I tried value=session.n, key='n'. I didn’t realize the warning would not appear if you used another key within session_state as value. :exploding_head:

Regarding the init() function: You are totally right. I just copied a MWE from my code and deleted non-relevant lines. In my actual app I am doing some more work and checks inside this init() function.

Again, thank you!

1 Like