> ## Documentation Index
> Fetch the complete documentation index at: https://auth0.generaltranslation.app/llms.txt
> Use this file to discover all available pages before exploring further.

> This guide demonstrates how to integrate Auth0 with a Flutter application using the Auth0 Flutter SDK.

# Add Login to Your Flutter Application

export const SignUpForm = () => {
  const [isAuthenticated, setIsAuthenticated] = useState(false);
  const [storeReady, setStoreReady] = useState(false);
  useEffect(() => {
    let unsubscribe = null;
    function init() {
      setStoreReady(true);
      unsubscribe = window.autorun(() => {
        const authenticated = window.rootStore?.sessionStore?.isAuthenticated || false;
        setIsAuthenticated(authenticated);
      });
    }
    if (window.rootStore) {
      init();
    } else {
      window.addEventListener("adu:storeReady", init);
    }
    return () => {
      window.removeEventListener("adu:storeReady", init);
      unsubscribe?.();
    };
  }, []);
  function LoggedInForm({sampleApp}) {
    const LS_APPS_KEY = "auth_demo_apps";
    const LS_APP_CFG_KEY = "auth_demo_app_cfg";
    const CHANNEL = "auth_flows_sync_v1";
    const mkChannel = () => new BroadcastChannel(CHANNEL);
    function uid() {
      return Math.random().toString(36).slice(2) + Date.now().toString(36);
    }
    function loadApps() {
      const raw = localStorage.getItem(LS_APPS_KEY);
      if (raw) return JSON.parse(raw);
      const seeded = [{
        id: "{yourClientId}",
        name: "Default App"
      }];
      localStorage.setItem(LS_APPS_KEY, JSON.stringify(seeded));
      return seeded;
    }
    function saveApps(apps) {
      localStorage.setItem(LS_APPS_KEY, JSON.stringify(apps));
    }
    function loadCfg() {
      const raw = localStorage.getItem(LS_APP_CFG_KEY);
      return raw ? JSON.parse(raw) : {};
    }
    function saveCfg(cfg) {
      localStorage.setItem(LS_APP_CFG_KEY, JSON.stringify(cfg));
    }
    const RightChevron = ({className = "w-5 h-5", ...props}) => <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className} {...props}>
        <polyline points="9 18 15 12 9 6" />
      </svg>;
    const LightningIcon = () => <svg width="24" height="24" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
        <path fillRule="evenodd" clipRule="evenodd" className="fill-[#3F59E4] dark:fill-[#99A7F1]" d="M24.971 30.152H7.088c-1.786 0-2.745-2.103-1.574-3.453l19.07-21.988c1.33-1.532 3.835-.4 3.569 1.607L24.97 30.152z" />
        <path fillRule="evenodd" clipRule="evenodd" className="fill-[#3F59E4] dark:fill-[#99A7F1]" d="M23.201 17.885h17.885c1.787 0 2.746 2.102 1.575 3.453l-19.073 21.99c-1.33 1.532-3.835.4-3.568-1.607L23.2 17.885z" />
      </svg>;
    const LayersIcon = () => <svg width="24" height="24" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
        <path className="fill-[#3F59E4] dark:fill-[#99A7F1]" d="M34.54 29.135l6.373 3.183c1.566.782 1.566 3.017 0 3.8l-14.815 7.396a4.623 4.623 0 01-4.125 0L7.174 36.12c-1.565-.782-1.565-3.017 0-3.798l6.532-3.214" />
        <path className="fill-[#AAB6F3] dark:fill-[#3449BA]" d="M34.54 18.86l6.373 3.183c1.566.782 1.566 3.016 0 3.8L26.098 33.24a4.623 4.623 0 01-4.125 0L7.174 25.843c-1.565-.781-1.565-3.016 0-3.798l6.33-3.164" />
        <path className="fill-[#CFD6F8] dark:fill-[#22307C]" d="M21.94 23.058L7.306 15.745c-1.62-.81-1.62-3.123 0-3.932l14.631-7.319a4.693 4.693 0 014.194 0l14.648 7.319c1.622.81 1.62 3.124 0 3.932L26.13 23.058c-1.321.66-2.873.66-4.191 0z" />
      </svg>;
    const GithubIcon = () => <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="w-5 h-5">
        <path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"></path>
      </svg>;
    function IconTile({children}) {
      return <div className="
            shrink-0 grid place-items-center w-10 h-10 rounded-lg
            bg-indigo-50 ring-1 ring-indigo-200/60
            dark:bg-indigo-950/40 dark:ring-white/10
          ">
          {children}
        </div>;
    }
    function Card({className = "", children}) {
      return <div className={`rounded-2xl shadow-sm ring-1 ring-zinc-200 dark:ring-zinc-800 ${className}`}>{children}</div>;
    }
    function Button({variant = "primary", type = "button", onClick, children}) {
      const base = "inline-flex items-center justify-center gap-2 h-10 px-4 rounded-xl font-medium transition";
      let styles = "";
      if (variant === "primary") {
        styles = "mint-bg-indigo-600 text-white hover:mint-bg-indigo-700";
      } else if (variant === "outline") {
        styles = "border border-zinc-300 dark:border-zinc-700 mint-bg-transparent hover:mint-bg-zinc-50 dark:hover:mint-bg-zinc-800";
      } else if (variant === "ghost") {
        styles = "hover:mint-bg-zinc-100 dark:hover:mint-bg-zinc-800";
      }
      return <button type={type} onClick={onClick} className={`${base} ${styles}`}>
          {children}
        </button>;
    }
    function Input({id, label, value, onChange, placeholder, name}) {
      return <label className="block space-y-1">
          <span className="text-sm text-zinc-700 dark:text-zinc-300">{label}</span>
          <input id={id} name={name} className="w-full h-11 px-3 rounded-xl border border-zinc-300 dark:border-zinc-700 bg-white dark:bg-zinc-900 text-zinc-900 dark:text-zinc-100 focus:outline-none focus:ring-2 focus:ring-indigo-500" placeholder={placeholder} value={value} onChange={e => onChange(e.target.value)} />
        </label>;
    }
    function Select({label, value, onChange, options}) {
      return <label className="block space-y-1 max-w-[300px]">
          <span className="text-sm text-zinc-700 dark:text-zinc-300">{label}</span>
          <div className="relative">
            <select className="w-full h-11 appearance-none px-3 pr-9 rounded-xl border border-zinc-300 dark:border-zinc-700 bg-white dark:bg-zinc-900 text-zinc-900 dark:text-zinc-100 focus:outline-none focus:ring-2 focus:ring-indigo-500" value={value} onChange={e => onChange(e.target.value)}>
              <optgroup label="Generic Applications">
                {options.map(o => <option key={o.id} value={o.id}>
                    {o.name}
                  </option>)}
              </optgroup>
            </select>
            <svg className="pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 w-5 h-5 text-zinc-500" viewBox="0 0 24 24">
              <path d="M7 10l5 5 5-5z" fill="currentColor" />
            </svg>
          </div>
        </label>;
    }
    function Toast({open, onClose, children}) {
      useEffect(() => {
        if (!open) return;
        const t = setTimeout(onClose, 2200);
        return () => clearTimeout(t);
      }, [open, onClose]);
      return <div className={`fixed right-4 top-4 z-50 transition ${open ? "opacity-100 translate-y-0" : "opacity-0 -translate-y-2 pointer-events-none"}`}>
          <div className="flex items-center gap-2 rounded-xl shadow ring-1 ring-emerald-200 bg-white dark:bg-zinc-900 px-4 py-2">
            <span className="w-1.5 h-8 rounded-l bg-emerald-500" />
            <svg className="w-5 h-5 text-emerald-600" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
              <path d="M20 6L9 17l-5-5" />
            </svg>
            <span className="text-sm text-zinc-900 dark:text-zinc-100">{children}</span>
          </div>
        </div>;
    }
    function Flows() {
      const [route, setRoute] = useState("menu");
      const [apps, setApps] = useState(loadApps());
      const [cfg, setCfg] = useState(loadCfg());
      const [selected, setSelected] = useState(apps[0]?.id || "");
      const [toast, setToast] = useState(false);
      const [bc] = useState(() => mkChannel());
      useEffect(() => {
        if (!apps.find(a => a.id === selected)) {
          setSelected(apps[0]?.id || "");
        }
      }, [apps, selected]);
      useEffect(() => {
        const onMsg = e => {
          const {type, payload} = e.data || ({});
          switch (type) {
            case "NAV":
              setRoute(payload.route);
              break;
            case "SELECT":
              setSelected(payload.appId);
              break;
            case "APPS_UPDATED":
              setApps(loadApps());
              break;
            case "CFG_UPDATED":
              setCfg(loadCfg());
              setToast(true);
              break;
            default:
              break;
          }
        };
        bc.addEventListener("message", onMsg);
        return () => bc.removeEventListener("message", onMsg);
      }, [bc]);
      const nav = nextRoute => {
        setRoute(nextRoute);
        bc.postMessage({
          type: "NAV",
          payload: {
            route: nextRoute
          }
        });
      };
      const selectApp = appId => {
        setSelected(appId);
        bc.postMessage({
          type: "SELECT",
          payload: {
            appId
          }
        });
      };
      const onCreate = name => {
        const id = uid();
        const next = [...apps, {
          id,
          name: name || "Untitled"
        }];
        setApps(next);
        saveApps(next);
        bc.postMessage({
          type: "APPS_UPDATED"
        });
        selectApp(id);
        nav("integrate");
      };
      const onSaveCfg = (appId, data) => {
        const next = {
          ...cfg,
          [appId]: data
        };
        setCfg(next);
        saveCfg(next);
        setToast(true);
        bc.postMessage({
          type: "CFG_UPDATED"
        });
      };
      return <div>
          {route === "menu" && <Menu onCreate={() => nav("create")} onIntegrate={() => nav("integrate")} />}

          {route === "create" && <CreateForm onCancel={() => nav("menu")} onSave={onCreate} />}

          {route === "integrate" && <IntegrateForm apps={apps} selected={selected} onSelect={selectApp} saved={cfg[selected]} onSave={data => onSaveCfg(selected, data)} onCancel={() => nav("menu")} />}

          <Toast open={toast} onClose={() => setToast(false)}>
            Successfully saved your changes.
          </Toast>
        </div>;
    }
    function Menu({onCreate, onIntegrate}) {
      return <ul className="space-y-4 list-none login_list">
          <li className="list-none !px-0">
            <button onClick={onCreate} className="w-full text-left">
              <Card className="p-5 hover:shadow-md transition">
                <div className="flex items-center justify-between">
                  <div className="flex items-center gap-4">
                    <IconTile>
                      <LightningIcon />
                    </IconTile>
                    <h2 className="text-lg">Create a new application</h2>
                  </div>
                  <RightChevron className="w-4 h-4 text-zinc-500" />
                </div>
              </Card>
            </button>
          </li>
          <li className="list-none !px-0">
            <button onClick={onIntegrate} className="w-full text-left">
              <Card className="p-5 hover:shadow-md transition">
                <div className="flex items-center justify-between">
                  <div className="flex items-center gap-4">
                    <IconTile>
                      <LayersIcon />
                    </IconTile>
                    <h2 className="text-lg">Integrate with an existing application</h2>
                  </div>
                  <RightChevron className="w-4 h-4 text-zinc-500" />
                </div>
              </Card>
            </button>
          </li>
          <li className="list-none !px-0">
            <a className="no_external_icon block" href={sampleApp ? sampleApp : "/"} target="_blank" rel="noreferrer">
              <Card className="p-5 hover:shadow-md transition">
                <div className="flex items-center justify-between">
                  <div className="flex items-center gap-4">
                    <IconTile>
                      <GithubIcon />
                    </IconTile>
                    <h2 className="text-lg">View a sample application</h2>
                  </div>
                  <RightChevron className="w-4 h-4 text-zinc-500" />
                </div>
              </Card>
            </a>
          </li>
        </ul>;
    }
    function CreateForm({onSave, onCancel}) {
      const [name, setName] = useState("");
      return <div className="space-y-6">
          <Input id="app-name" label="Application Name" placeholder="My App" value={name} onChange={setName} />
          <p className="text-sm text-zinc-500">You can change this later in the application settings.</p>
          <div className="flex gap-3">
            <Button onClick={() => onSave(name)}>Save</Button>
            <Button variant="outline" onClick={onCancel}>
              Cancel
            </Button>
          </div>
        </div>;
    }
    function IntegrateForm({apps, selected, onSelect, saved, onSave, onCancel}) {
      const [callbacks, setCallbacks] = useState(saved?.callbacks ?? "");
      const [logouts, setLogouts] = useState(saved?.logouts ?? "");
      const [origins, setOrigins] = useState(saved?.origins ?? "");
      useEffect(() => {
        setCallbacks(loadCfg()[selected]?.callbacks ?? "");
        setLogouts(loadCfg()[selected]?.logouts ?? "");
        setOrigins(loadCfg()[selected]?.origins ?? "");
      }, [selected]);
      return <div className="space-y-6">
          <div>
            <span className="block text-sm text-zinc-600 dark:text-zinc-300 mb-1">Select your Application</span>
            <Select label="" value={selected} onChange={onSelect} options={apps} />
          </div>

          <form className="space-y-4" onSubmit={e => {
        e.preventDefault();
        onSave({
          callbacks,
          logouts,
          origins
        });
      }}>
            <Input id="callbacks" name="callbacks" label="Callback URLs" placeholder="http://localhost:3000" value={callbacks} onChange={setCallbacks} />
            <Input id="logout" name="allowed_logout_urls" label="Logout URLs" placeholder="http://localhost:3000" value={logouts} onChange={setLogouts} />
            <Input id="origins" name="web_origins" label="Allowed Web Origins" placeholder="http://localhost:3000" value={origins} onChange={setOrigins} />

            <div className="flex gap-3 pt-2">
              <Button type="submit">Save</Button>
              <Button variant="outline" type="button" onClick={onCancel}>
                Cancel
              </Button>
            </div>
          </form>
        </div>;
    }
    return <div className="w-full mx-auto py-8">
        <Flows />
      </div>;
  }
  ;
  function SignUpFormInternal() {
    return <div className="flex flex-col gap-2 items-center h-full">
        <img noZoom src="/docs/img/quickstarts/action_hero_dashboard.svg" alt="Sign up for an Auth0 account" style={{
      width: "250px",
      height: "250px"
    }} />
        <span className="text-center" style={{
      width: "400px"
    }}>
          Sign up for an{" "}
          <a href="https://auth0.com/signup" target="_blank" rel="noopener noreferrer">
            Auth0 account
          </a>{" "}
          or{" "}
          <span className="font-semibold text-primary dark:text-white cursor-pointer" onClick={() => console.log("log in")}>
            log in
          </span>{" "}
          to your existing account to integrate directly with your own tenant.
        </span>
        <button onClick={() => console.log("sign up")} className="bg-primary dark:bg-primary-light text-white dark:text-black px-4 py-2 rounded-md mt-4 font-medium" style={{
      width: "140px"
    }}>
          Sign up
        </button>
      </div>;
  }
  ;
  return <></>;
};

export const SideMenuSectionItem = ({id, children}) => {
  return <div id={`side-menu-item-${id}`} className="recipe-side-menu-item flex flex-col w-full h-full">
      {children}
    </div>;
};

export const SideMenu = ({sections, children}) => {
  const [visibleSection, setVisibleSection] = useState(sections[0]?.id ?? null);
  const checkVisibility = () => {
    let currentVisible = null;
    const viewportHeight = window.innerHeight;
    const scrollY = window.scrollY;
    sections.forEach(({id}) => {
      const section = document.getElementById(id);
      if (section) {
        const rect = section.getBoundingClientRect();
        const sectionTop = rect.top + scrollY;
        const sectionBottom = sectionTop + rect.height;
        const multiplier = viewportHeight > 1600 ? 0.34 : 0.22;
        if (scrollY + viewportHeight * multiplier >= sectionTop && scrollY <= sectionBottom) {
          currentVisible = id;
        }
      }
    });
    if (currentVisible && currentVisible !== visibleSection) {
      setVisibleSection(currentVisible);
    }
  };
  useEffect(() => {
    const throttledCheck = () => {
      setTimeout(checkVisibility, 100);
    };
    checkVisibility();
    window.addEventListener("scroll", throttledCheck);
    return () => {
      window.removeEventListener("scroll", throttledCheck);
    };
  }, [sections, visibleSection]);
  useEffect(() => {
    sections.forEach(({id}) => {
      const section = document.getElementById(id);
      const sideMenuItem = document.getElementById(`side-menu-item-${id}`);
      if (section) {
        if (id === visibleSection) {
          section.classList.add("active-section");
        } else {
          section.classList.remove("active-section");
        }
      }
      if (sideMenuItem) {
        if (id === visibleSection) {
          sideMenuItem.classList.add("active-side-menu-item");
        } else {
          sideMenuItem.classList.remove("active-side-menu-item");
        }
      }
    });
  }, [visibleSection, sections]);
  return <div className="recipe-side-menu sticky px-2 py-1" style={{
    height: "calc(100vh - 7rem)",
    top: "7rem",
    scrollMarginTop: "var(--scroll-mt)"
  }}>
      {children.map(child => {
    if (child.props.id === visibleSection) {
      return child;
    }
    return null;
  })}
    </div>;
};

export const Section = ({id, title, stepNumber, children, isSingleColumn = false}) => {
  return <div id={id} className={`recipe-section flex flex-col transition-opacity duration-200`}>
      {}
      <Step title={title} stepNumber={stepNumber} titleSize="h3">
        {children}
      </Step>
    </div>;
};

export const Content = ({title, children}) => {
  return <div className="recipe-content flex flex-col">
      {title && <h1 className="text-3xl">{title}</h1>}
      {children}
    </div>;
};

export const Recipe = ({children, isSingleColumn = false}) => {
  return <div className={`pl-4 recipe-container mx-auto grid grid-cols-1 gap-10 relative ${isSingleColumn ? "md:grid-cols-1" : "md:grid-cols-2"}`}>
      {children}
    </div>;
};

export const AuthCodeGroup = ({children, dropdown}) => {
  const [processedChildren, setProcessedChildren] = useState(children);
  useEffect(() => {
    let unsubscribe = null;
    function init() {
      unsubscribe = window.autorun(() => {
        const processChildren = node => {
          if (typeof node === "string") {
            let processedNode = node;
            for (const [key, value] of window.rootStore.variableStore.values.entries()) {
              processedNode = processedNode.replace(new RegExp(key, "g"), value);
            }
            return processedNode;
          } else if (Array.isArray(node)) {
            return node.map(processChildren);
          } else if (node && node.props && node.props.children) {
            return {
              ...node,
              props: {
                ...node.props,
                children: processChildren(node.props.children)
              }
            };
          }
          return node;
        };
        setProcessedChildren(processChildren(children));
      });
    }
    if (window.rootStore) {
      init();
    } else {
      window.addEventListener("adu:storeReady", init);
    }
    return () => {
      window.removeEventListener("adu:storeReady", init);
      unsubscribe?.();
    };
  }, [children]);
  return <CodeGroup dropdown={dropdown}>{processedChildren}</CodeGroup>;
};

export const sections = [{
  id: "configure-auth0",
  title: "Configure Auth0"
}, {
  id: "install-the-auth0-flutter-sdk",
  title: "Install the Auth0 Flutter SDK"
}, {
  id: "add-login-to-your-application",
  title: "Add login to your application"
}, {
  id: "add-logout-to-your-application",
  title: "Add logout to your application"
}, {
  id: "show-user-profile-information",
  title: "Show user profile information"
}];

<Recipe>
  <Content>
    Auth0 allows you to quickly add authentication and access user profile information in your application. This guide
    demonstrates how to integrate Auth0 with a Flutter application using the [Auth0 Flutter SDK](https://github.com/auth0/auth0-flutter).

    <Info>
      The Flutter SDK currently only supports Flutter applications running on Android, iOS, or Web platforms.
    </Info>

    This quickstart assumes you already have a [Flutter](https://flutter.dev/) application up and running. If not, check out the [Flutter "getting
    started" guides](https://docs.flutter.dev/get-started/install) to get started with a simple app.

    You should also be familiar with the [Flutter command line tool](https://docs.flutter.dev/reference/flutter-cli).

    <Section id={sections[0].id} title={sections[0].title} stepNumber="1">
      When you signed up for Auth0, a new application was created for you, or you could have created a new one. You
      will need some details about that application to communicate with Auth0. You can get these details from the [Application
      Settings](https://manage.auth0.com/#/applications) section in the Auth0 dashboard.

      <Frame>
        <img src="https://mintcdn.com/generaltranslationinc/3E5d0TEwC25OF1Ek/docs/images/cdy7uua7fh8z/6SC7KnyzCyO8cwXQfril1X/c0d6756c28de0ddcbe938307e529d47a/client_settings.png?fit=max&auto=format&n=3E5d0TEwC25OF1Ek&q=85&s=dde658df066c916342ed5d9af59befc4" width="1105" height="673" data-path="docs/images/cdy7uua7fh8z/6SC7KnyzCyO8cwXQfril1X/c0d6756c28de0ddcbe938307e529d47a/client_settings.png" />
      </Frame>

      <Info>
        When using the Default App with a Native or Single Page Application, ensure to update the **Token
        Endpoint Authentication Method** to `None` and set the **Application Type** to either
        `SPA` or `Native`.
      </Info>

      You need the following information:

      * **Domain**
      * **Client ID**

      <Info>
        If you download the sample from the top of this page, these details are filled out for you.
      </Info>

      ### Configure Callback URLs

      A callback URL is a URL in your application where Auth0 redirects the user after they have authenticated. The
      callback URL for your app must be added to the **Allowed Callback URLs** field in your [Application
      Settings](https://manage.auth0.com/#/applications). If this field is not set, users will be unable to log in to the application and will get an error.

      <Info>
        If you are following along with the sample project you downloaded from the top of this page, you should
        set the **Allowed Callback URL** to `http://localhost:3000`.
      </Info>

      ### Configure Logout URLs

      A logout URL is a URL in your application that Auth0 can return to after the user has been logged out of the
      authorization server. This is specified in the `returnTo` query parameter. The logout URL for your app
      must be added to the **Allowed Logout URLs** field in your [Application Settings](https://manage.auth0.com/#/applications). If this field is not set, users will be
      unable to log out from the application and will get an error.

      <Info>
        If you are following along with the sample project you downloaded from the top of this page, the logout
        URL you need to add to the **Allowed Logout URLs** field is `http://localhost:3000`.
      </Info>

      ### Configure Allowed Web Origins

      You need to add the URL for your app to the **Allowed Web Origins** field in your [Application Settings](https://manage.auth0.com/#/applications/\{yourClientId}/settings). If you don't register your application URL here, the
      application will be unable to silently refresh the authentication tokens and your users will be logged out the
      next time they visit the application, or refresh the page.

      <Info>
        If you are following along with the sample project you downloaded from the top of this page, you should
        set the **Allowed Web Origins** to `http://localhost:3000`.
      </Info>
    </Section>

    <Section id={sections[1].id} title={sections[1].title} stepNumber="2">
      Add the Auth0 Flutter SDK into the project:

      ```bash theme={null}
      flutter pub add auth0_flutter
      ```

      Add the following script tag to your `index.html` page:

      ```html theme={null}
      <script src="https://cdn.auth0.com/js/auth0-spa-js/2.0/auth0-spa-js.production.js" defer></script>
      ```
    </Section>

    <Section id={sections[2].id} title={sections[2].title} stepNumber="3">
      [Universal Login](/docs/authenticate/login/auth0-universal-login) is
      the easiest way to set up authentication in your application. We recommend using it for the best experience, best
      security, and the fullest array of features.

      Integrate Auth0 Universal Login in your Flutter Web app by using the `Auth0Web` class. Redirect your
      users to the Auth0 Universal Login page using `loginWithRedirect()`.

      <Info>
        You will normally need to specify the `redirectUrl` parameter to
        `loginWithRedirect`. Omitting this will cause Auth0 to use the [default login route](/docs/authenticate/login/auth0-universal-login/configure-default-login-routes), which is not configured by default.
      </Info>

      When a user logs in, they are redirected back to your application. You are then able to access the ID and access
      tokens for this user by calling `onLoad` during startup and handling the credentials that are given to
      you:

      ```js theme={null}
      auth0.onLoad().then((final credentials) => setState(() {
      // Handle or store credentials here

      _credentials = credentials;

      }));
      ```

      <Note>
        ##### Checkpoint

        Add a button to your app that calls `loginWithRedirect()` and logs the user into your app.
        Verify that you are redirected to Auth0 for authentication and then back to your application.

        Verify that you can access `credentials` as a result of calling `onLoad` and that
        you're able to access the ID and access tokens.
      </Note>
    </Section>

    <Section id={sections[3].id} title={sections[3].title} stepNumber="4">
      To log users out, redirect them to the Auth0 logout endpoint to clear their login session by calling the Auth0
      Flutter SDK `logout()`. [Read
      more about logging out of Auth0](/docs/authenticate/login/logout).

      <Info>
        You will normally want to specify `returnToUrl` when calling `logout`, otherwise
        Auth0 [will default to the first URL in the Allowed Logout URLs list](/docs/authenticate/login/logout/redirect-users-after-logout).
      </Info>

      <Note>
        ##### Checkpoint

        Add a button to your app that calls `logout()` and logs the user out of your application. When
        you select it, verify that your Flutter app redirects you to the logout endpoint and back again. You
        should not be logged in to your application.
      </Note>
    </Section>

    <Section id={sections[4].id} title={sections[4].title} stepNumber="5">
      The user profile automatically retrieves user profile properties for you when the page loads, and can be accessed
      and stored by calling `onLoad` during application startup. The returned object from `onLoad`
      contains a `user` property with all the user profile properties. This is internally populated by
      decoding the ID token.

      <Note>
        ##### Checkpoint

        Log in and inspect the `user` property on the result. Verify the current user's profile
        information, such as `email` or `name`.
      </Note>
    </Section>

    ## Next Steps

    Excellent work! If you made it this far, you should now have login, logout, and user profile information running in your application.<br /><br />

    This concludes our quickstart tutorial, but there is so much more to explore. To learn more about what you can do with Auth0, check out:

    * [Auth0 Dashboard](https://manage.auth0.com/dashboard/us/dev-s3674bdouue0bd73) - Learn how to configure and manage your Auth0 tenant and applications
    * [auth0-flutter SDK](https://www.github.com/auth0/auth0-flutter/) - Explore the SDK used in this tutorial more fully
    * [Auth0 Marketplace](https://marketplace.auth0.com/) - Discover integrations you can enable to extend Auth0’s functionality
  </Content>

  <SideMenu sections={sections}>
    <SideMenuSectionItem id={sections[0].id}>
      <SignUpForm />
    </SideMenuSectionItem>

    <SideMenuSectionItem id={sections[1].id}>
      <SignUpForm />
    </SideMenuSectionItem>

    <SideMenuSectionItem id={sections[2].id}>
      <AuthCodeGroup>
        ```dart main_view.dart lines theme={null}
        import 'package:auth0_flutter/auth0_flutter.dart';
        import 'package:auth0_flutter/auth0_flutter_web.dart';
        import 'package:flutter/material.dart';
        import 'profile_view.dart';

        class MainView extends StatefulWidget {
          const MainView({Key? key}) : super(key: key);

          @override
          State<MainView> createState() => _MainViewState();
        }

        class _MainViewState extends State<MainView> {
          Credentials? _credentials;

          late Auth0Web auth0;

          @override
          void initState() {
            super.initState();
            auth0 = Auth0Web('dev-s3674bdouue0bd73.us.auth0.com', 'QqpqsvIQHjLodaUIPpabJcIHoG41tbAv');

            auth0.onLoad().then((final credentials) => setState(() {
                _credentials = credentials;
              }));
          }

          @override
          Widget build(BuildContext context) {
            return Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                if (_credentials == null)
                  ElevatedButton(
                      onPressed: () => auth0.loginWithRedirect(redirectUrl: 'http://localhost:3000'),
                      child: const Text("Log in"))
                else
                  Column(
                    children: [
                      ProfileView(user: _credentials!.user),
                      ElevatedButton(
                          onPressed: () async {
                            await auth0.logout(returnToUrl: 'http://localhost:3000');
                          },
                          child: const Text("Log out"))
                    ],
                  )
              ],
            );
          }
        }
        ```

        ```dart profile_view.dart lines theme={null}
        import 'package:auth0_flutter/auth0_flutter.dart';
        import 'package:flutter/material.dart';

        class ProfileView extends StatelessWidget {
          const ProfileView({Key? key, required this.user}) : super(key: key);

          final UserProfile user;

          @override
          Widget build(BuildContext context) {
            return Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                if (user.name != null) Text(user.name!),
                if (user.email != null) Text(user.email!)
              ],
            );
          }
        }
        ```
      </AuthCodeGroup>
    </SideMenuSectionItem>

    <SideMenuSectionItem id={sections[3].id}>
      <SignUpForm />
    </SideMenuSectionItem>

    <SideMenuSectionItem id={sections[4].id}>
      <AuthCodeGroup>
        ```dart main_view.dart lines theme={null}
        import 'package:auth0_flutter/auth0_flutter.dart';
        import 'package:auth0_flutter/auth0_flutter_web.dart';
        import 'package:flutter/material.dart';
        import 'profile_view.dart';

        class MainView extends StatefulWidget {
          const MainView({Key? key}) : super(key: key);

          @override
          State<MainView> createState() => _MainViewState();
        }

        class _MainViewState extends State<MainView> {
          Credentials? _credentials;

          late Auth0Web auth0;

          @override
          void initState() {
            super.initState();
            auth0 = Auth0Web('dev-s3674bdouue0bd73.us.auth0.com', 'QqpqsvIQHjLodaUIPpabJcIHoG41tbAv');

            auth0.onLoad().then((final credentials) => setState(() {
                _credentials = credentials;
              }));
          }

          @override
          Widget build(BuildContext context) {
            return Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                if (_credentials == null)
                  ElevatedButton(
                      onPressed: () => auth0.loginWithRedirect(redirectUrl: 'http://localhost:3000'),
                      child: const Text("Log in"))
                else
                  Column(
                    children: [
                      ProfileView(user: _credentials!.user),
                      ElevatedButton(
                          onPressed: () async {
                            await auth0.logout(returnToUrl: 'http://localhost:3000');
                          },
                          child: const Text("Log out"))
                    ],
                  )
              ],
            );
          }
        }
        ```

        ```dart profile_view.dart lines theme={null}
        import 'package:auth0_flutter/auth0_flutter.dart';
        import 'package:flutter/material.dart';

        class ProfileView extends StatelessWidget {
          const ProfileView({Key? key, required this.user}) : super(key: key);

          final UserProfile user;

          @override
          Widget build(BuildContext context) {
            return Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                if (user.name != null) Text(user.name!),
                if (user.email != null) Text(user.email!)
              ],
            );
          }
        }
        ```
      </AuthCodeGroup>
    </SideMenuSectionItem>
  </SideMenu>
</Recipe>
