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
That’s a popular question. 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.
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')