Hey @jml,
This is how the flow would look like,
- Create a session state
- Store widget states in the state.conf as a dict.
- dump the state.conf to json and set as URL param.
I just whipped up a template for you 
import streamlit as st
from state import provide_state
import json
INITIAL_CONF = {
"checkbox": False,
"number": 0.0
}
@provide_state
def main(state):
query_params = st.experimental_get_query_params()
if "conf" in query_params:
state.conf = json.loads(query_params["conf"][0])
state.conf = state.conf or INITIAL_CONF
state.conf["checkbox"] = st.checkbox("Retain State !", value=state.conf["checkbox"])
state.conf["number"] = st.number_input("You Too Retain State !", value=state.conf["number"])
st.experimental_set_query_params(**{"conf": json.dumps(state.conf)})
main()
Get provide_state decorator from here ,https://gist.github.com/ash2shukla/ff180d7fbe8ec3a0240f19f4452acde7
The outcome looks like this,
I use something similar for a ML lifecycle management application I built for my client using streamlit to share experiments. The better thing to do would be to save this session state json in a mongodb or some where else and just set the uid as a URL param and pick up the state on run of main from DB.
Because the URL params cant hold more than 2048 chars 
Hope it helps !
PS. I think Austin is doing something similar, perhaps just dumping the JSONs on fs instead of a document store and all. 