Help with multiple buttons in app

Hello, having problems with including multiple buttons in my app. I initially have a login button that authenticates users into the app, and then I would like to perform more functions for users that are logged in, however, when I click a button after the login, the app does not run correctly, and won’t let me include more into the app. Below is my code. Not sure if it has anything to do with the session states, or how can I get more functions that include buttons to work following the login button?

import streamlit as st
import pandas as pd
import yaml
import bcrypt


def load_users(filename):
    with open(filename, 'r') as file:
        return yaml.safe_load(file)

def authenticate(username, password):
    for user in users:
        if user['username'] == username and bcrypt.checkpw(password.encode('utf-8'), user['password'].encode('utf-8')):
            return True
    return False

users_data = load_users('myusers.yaml')
users = users_data['users']

username = st.text_input("Username")
password = st.text_input("Password", type="password")

if st.button("Login"):
    if authenticate(username, password):
        st.success("Login successful!")
        st.write(f"Welcome, {username}!")
        st.subheader("")
        st.header("Instructions")
        st.subheader("...Once human interface is chosen, and instructions are clear on how to use device, enter instructions here...")
        st.header("")
        if "button_clicked" not in st.session_state:
                    st.session_state.button_clicked = False
        def begin_callback():
            # "Yes" button was clicked
            st.session_state.button_clicked = True
        def no_callback():
            # "No" button was clicked
            alert = st.warning("Please go back to the main page")
            #time.sleep(3)
            #alert.empty()
        def yes_callback():
            st.balloons()
            st.title("Your data collection has begun")
            st.session_state.button_clicked = False
        st.subheader("Would you like to begin data collection?")
        if not st.session_state.button_clicked:
            st.button("Click Here to Begin Data Collection", on_click=begin_callback)
        else:
            st.button("Yes", on_click=yes_callback)
            st.button("No", on_click=no_callback)
else:
        st.error("Invalid username or password.")

In Streamlit, buttons aren’t stateful. They return True on the rerun when you click them and go back to False. Therefore, you generally don’t want to include other widgets nested inside buttons. Your “Click Here to Begin Data Collection” button is nested inside your “Login” button.

Try reading this: Button behavior and examples - Streamlit Docs

Would I be able to have nested check boxes inside the login button? Or is there no way to go about this issue without having a problem with the login button?

You generally don’t want any widgets inside a button. Instead, use the button as a trigger process (perform some authentication and set a value in session state, like st.session_state.logged_in), then (after and outside of the button) do more things conditioned on that value in session state.

How would I create a login with authentication in that way, with a button as a trigger process? If I were to do that, then can all the functions work properly following that?

Here’s a basic example where you can handle the authentication as a callback to the button, then have everything conditioned on that thereafter:

import streamlit as st

USERS = ["sam", "alex"]

if "logged_in_user" not in st.session_state:
    st.session_state.logged_in_user = None

def authenticate(user):
    """A simple function to simulate an authentication check.
    Replace with a more complex function that checks username, password, token, etc....
    """
    if user in USERS:
        st.session_state.logged_in_user = user
    else:
        st.session_state.logged_in_user = None

def logout():
    st.session_state.logged_in_user = None

if st.session_state.logged_in_user is None:
    # Show login prompt
    user = st.sidebar.text_input("Username")
    st.sidebar.button("Log in", on_click=authenticate, args=[user])
    st.stop()

st.sidebar.button("Log out", on_click=logout)
st.write(f"Hello, {st.session_state.logged_in_user}")
# Do stuff

In the above example st.stop() tells Streamlit to stop (because we don’t have an authenticated user here). You could nest the entire rest of the code in an else block instead, but st.stop() saves a bit of whitespace. It’s like using return in functions to avoid some of the indented code.

Some other examples that might be helpful are:

In the button article I linked to, you might focus on the staging example, where buttons take you between different stages of a process (login, makes a selection, respond to the result, etc.)