How to change the position of st.form_submit_button?

Hi,

I am developing an app where I need to have a “st.form_submit_button”. My app has 3 columns inside the st.form context manager. Once I am done with everything, at the very end, I have to submit my results. So, I use st.form_submit_button outside of the third column; otherwise, I get an error to use st.form_submit_button inside the st.form. This is the layout of my codes:

c1, c2, c3 = st.columns([1, 1, 1], gap="large")
with st.form("main form", clear_on_submit=True):
     with c1:
          ##### some stuff #####
     with c2:
          ##### some stuff #####
     with c3:
          ##### some stuff #####
    
     if st.form_submit_button('"Submit"):
        #### Some function  ####

Once I do this, Streamlit positions the “Submit” button at the lower left (right below the column 1. However, I’d like to move the “Submit” button to lower right (at the button of the third col). The following shows the UI of my app:

Can anyone please help ?

I am developing my app locally, and using Streamlit veriosn 1.26.0, and python version 3.10.4.

Just put the submit within c3, and if you want it on the right of c3 then create additional columns under c3 and adjust their widths to make the button jam against the right edge. (Note you can only nest columns to two (or three?) levels.)

This is not possible.

StreamlitAPIException : st.form_submit_button() must be used inside an st.form() .

Move the column creation into the form so that the form holds the parent context.

import streamlit as st  # 🎈 streamlit development

with st.form("main form", clear_on_submit=True):
    c1, c2, c3 = st.columns([1, 1, 1], gap="large")
    with c1:
        with st.container(border=True):
            st.write("##### C1 #####")
            st.image("https://picsum.photos/200/300")
    with c2:
        with st.container(border=True):
            st.write("##### C2 #####")
            st.image("https://picsum.photos/200/300")
    with c3:
        with st.container(border=True):
            st.write("##### C3 #####")
            st.image("https://picsum.photos/200/300")

        c3_1, c3_2 = st.columns([1, 1])
        with c3_2:
            submitted = st.form_submit_button("Submit")

if submitted:
    st.write("##### some stuff #####")

1 Like

Thanks for your explanation. However, I ran into another problem. My st.form_submit_button function has on_change argumnet, that is, st.form_submit_button(“Submit”, on_change )

Also, I have another on_change argument in of the widgets under c2 column in the above code, so if I do what you said, Streamlit complains that the on_change cannot be in two places if I am using st.form_submit_button, So I get the following error:

streamlit.errors.StreamlitAPIException: With forms, callbacks can only be defined on the st.form_submit_button. Defining callbacks on other widgets inside a form is not allowed.

Unfortunately, it is very difficult to merge these two on_change functions under one widget, i.e., st.form_submit_button(“Submit”, on_change ).

Do you have any suggestion?

Thanks

The point about a form is that its widget values are not available to the rest of the app until the form is submitted. That’s why only the submit callback is allowed. Within that callback you can retrieve the form’s widgets’ values using each widget’s key, which you must define.

I provided a solution in another post that has some similarity in terms of the callback mechanisms you could implement to solve your problem. Read that here: Streamlit login solution need to click on "Login" button twice to login - #2 by asehmi. Put the callback on the submit button.

If that still doesn’t solve your problem, and you must have callbacks on the widget change events, then you can simulate a “form” using a container to hold the widgets (so you get a nice border) and a standard button for the submit. That’s the structure used in the above link.

Ok. Thanks. I’ll look at it and see what what I can do.

Thanks for the help.