I wanted a simple form which is displayed when the Show Form button is clicked.
User can change the data and either submit or close the form.
Example code:
def main():
st.button("Show Form", on_click=username_form, args=(["John Doe"]))
def username_form(name):
with st.form(key="test", clear_on_submit=True):
col1, col2 = st.columns(2)
fname = col1.text_input("Firstname", name.split()[0])
lname = col2.text_input("Lastname", name.split()[-1])
submit = st.form_submit_button("Submit", on_click=print, args=(fname, lname))
reset = st.form_submit_button("Close")
print(submit, reset)
return submit, reset
But No matter what you enter in the input text field the text isn’t saved and the values sent back are always the default values life the GIF shows below.

the return statement is executed as soon as the button is clicked and no changes being logged text input changes even after st.form_submit_button() click action
same behaviour with
def main():
st.checkbox("Show Form", on_change=username_form, args=(["John Doe"]))
Hi @MegaMind 
Inputs widgets inside forms do work. The piece of the puzzle missing from your code has to do with saving the output of the input widgets. Currently, you’re returning two submit buttons instead of the output of both text input widgets.
Session State provides the functionality to store variables across reruns. Widget state (i.e. the value of a widget) is also stored in a session. This convenience feature makes it super easy to read or write to the widget’s state anywhere in the app’s code. Session State variables mirror the widget value using the key argument.
Solution
In your code, we need to add the key argument to both the fname and lname text input widgets. The output of those widgets can then be accessed by st.session_state.key:
import streamlit as st
def main():
if "first" not in st.session_state:
st.session_state.first = "John"
st.session_state.last = "Doe"
st.button(
"Show Form",
on_click=username_form,
args=([st.session_state.first + " " + st.session_state.last]),
)
def show_names():
st.write(st.session_state.first, st.session_state.last)
def username_form(name):
with st.form(key="test", clear_on_submit=True):
col1, col2 = st.columns(2)
fname = col1.text_input("Firstname", name.split()[0], key="first")
lname = col2.text_input("Lastname", name.split()[-1], key="last")
submit = st.form_submit_button(
"Submit", on_click=show_names
)
if __name__ == "__main__":
main()
Output

Notes
This implementation, however, will persist values only across reruns for a session. The values will be lost when users exit the app or reload their browser tab. Learn more about Session State here.
If you want to store the values of fname and lname somewhere more permanent, you’ll need to replace the show_names() function that currently displays values to one that writes to a remote database. We have a number of tutorials on how to connect Streamlit to popular databases:
Happy Streamlit-ing! 
Snehan
Hi thanks for this. Was exactly what I needed.
But was just wondering if there was any way i could store a dict inside a session state for the above example
so instead of
if "first" not in st.session_state:
st.session_state.first = "John"
st.session_state.last = "Doe"
i would do something like
if "first" not in st.session_state:
st.session_state.name_data = {'first': 'Jhon', 'last': "Doe"}
and then somehow update the st.session_state.name_data dict when data in form changes ?
Yup, you definitely can! 
import streamlit as st
def main():
if "name_data" not in st.session_state:
st.session_state.name_data = {"first": "John", "last": "Doe"}
st.button(
"Show Form",
on_click=username_form,
args=([st.session_state.name_data]),
)
def update_name():
st.session_state.name_data["first"] = st.session_state.first
st.session_state.name_data["last"] = st.session_state.last
display_name()
def display_name():
st.write(st.session_state.name_data)
st.write(st.session_state.first, st.session_state.last)
def username_form(name):
with st.form(key="test", clear_on_submit=True):
col1, col2 = st.columns(2)
fname = col1.text_input("Firstname", name["first"], key="first")
lname = col2.text_input("Lastname", name["last"], key="last")
submit = st.form_submit_button("Submit", on_click=update_name)
if __name__ == "__main__":
main()
Yeah thought so.
Wrote a helper function to copy all keys that start with a unique name and move them to a dict inside session_state and then delete the keys from session state. so I can access the needed data easier and also keep session_state clean at the same time.