How to make st.button content stick/persist in its own section

Summary

I’m trying to create a web app that takes location input from the user and then uses googlemaps places_autocomplete function to provide autocomplete suggestions. I’m then using an st.button to have the user select his preferred location.
I’m getting two text_input from the user for both the pick-up location & the drop-off location.
I will then further use(scrape) the places_id to get the Location latitude & longitude.
But the problem is that when the user clicks on let’s say a suggestion for pick-up location and then clicks another button for drop-off location, I loose the previous st.button content. Likewise if I click a pick-up st.button, I loose the drop-off button content.
I just get the most current content and the previous st.button content doesn’t persist on page. I don’t want that happening

Steps to reproduce

Code snippet:

import streamlit as st
import googlemaps
import requests

# Create a Google Maps client
api_key = 'Google-Map-API-key'
gmaps = googlemaps.Client(key=api_key)
# -------------
#Place showcase & get Latitude + Longitude
# Update the search input field with the clicked suggestion
def get_location(input, place_id):
  #st.text("Pick-up Point:")
  st.write(input)
  st.text("Latitude, Longitude")
  try:
    url = f"https://maps.googleapis.com/maps/api/place/details/json?placeid={place_id}&key={api_key}"
    response = requests.get(url)
    data = response.json()
    if data['status'] == 'OK':
      latitude = data["result"]["geometry"]["location"]["lat"]
      longitude = data["result"]["geometry"]["location"]["lng"]
      return st.write([latitude, longitude])
    else:
      print('Error Occured')
  except:
    pass

# --------------
# Pick-up point
# Create a search box using Streamlit
search_input = st.text_input("Enter a Pick-up location")

# Call the places_autocomplete function as the user types
if search_input:
    predictions = gmaps.places_autocomplete(search_input)

    # Display the suggestions
    for prediction in predictions:
        if st.button(prediction['description']):
            search_input = prediction['description']
            place_id = prediction['place_id']
            st.write(get_location(search_input, place_id))


drop_input = st.text_input("Enter a Drop-off location")
# Call the places_autocomplete function as the user types
if drop_input:
    predictions = gmaps.places_autocomplete(drop_input)

    # Display the suggestions
    for prediction in predictions:
        if st.button(prediction['description']):
            drop_input = prediction['description']
            place_id = prediction['place_id']
            st.write(get_location(drop_input, place_id))

If applicable, please provide the steps we should take to reproduce the error or specified behavior.

Expected behavior:

I want the pick-up content to stick/persist on page even if I click on a drop-off button. I want both st.button content to stay on page.

Actual behavior:
Only one st.button content is being displayed on page

I need help on this

That’s a popular question. :grin: Buttons don’t retain state. They return True on the page load resulting from their click and then immediately go back to False. If you nest anything under a button, it will not execute/disappear when the user takes their next action.

Basically, you can use the button to set a flag in session state, then set a condition on that flag instead of the button directly.

Check out this blog post, point 1:

The blog also links to a stateful button available in streamlit-extras.

And....

:sweat_smile: You can also search for “buttons don’t” @mathcatsand for more posts…

I have done some research about session_state, but I honestly haven’t understood how it works and how make st.button content be persistent on page.

If I replace st.button with st.checkbox, the content stays persistent on page. And I have read that from a comment you made in one of the discussions. But I don’t want to use a checkbox for this because its not appropriate.

I would prefer to use a button to enable the user to select a location, but the challenge is that the button is not persisting on the page.

Could you kindly provide a sample code to do the persisting of button content on a page.

Here’s a snippet from the blog, which covers having multiple buttons made stateful:

import streamlit as st

# Initialize the key in session state
if 'clicked' not in st.session_state:
    st.session_state.clicked = {1:False,2:False}

# Function to update the value in session state
def clicked(button):
    st.session_state.clicked[button] = True

# Button with callback function
st.button('First Button', on_click=clicked, args=[1])

# Conditional based on value in session state, not the output
if st.session_state.clicked[1]:
    st.write('The first button was clicked.')
    st.button('Second Button', on_click=clicked, args=[2])
    if st.session_state.clicked[2]:
        st.write('The second button was clicked')

Here’s another option using stages:

import streamlit as st

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

def set_stage(stage):
    st.session_state.stage = stage

# Some code
st.button('First Button', on_click=set_stage, args=(1,))

if st.session_state.stage > 0:
    # Some code
    st.button('Second Button', on_click=set_stage, args=(2,))
if st.session_state.stage > 1:
    # More code, etc
    st.button('Third Button', on_click=set_stage, args=(3,))
if st.session_state.stage > 2:
    st.write('The end')
st.button('Reset', on_click=set_stage, args=(0,))

You can also make a button into a toggle:

import streamlit as st

# Initialize some state for showing recommendations
if 'show_recommendation' not in st.session_state:
    st.session_state.show_recommendation = False

# Callback function to make sure the state changes with each button click
def change_show():
    st.session_state.show_recommendation = not st.session_state.show_recommendation

# Independent button, with attached callback function
st.button('Show Recommendations', on_click=change_show)

# Conditional called on the key stored in session state instead of directly on the button value
if st.session_state.show_recommendation:
    st.write('Movie recommendations showing here')
    st.button('Another button here')
1 Like