> ## 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.

> このガイドでは、Auth0 Flutter ソフトウェア開発キット (SDK) を使用して、Flutter アプリケーションに Auth0 を統合する方法を紹介します。

# Flutter アプリケーションにログイン機能を追加する

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: "デフォルトアプリ"
      }];
      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="汎用アプリケーション">
                {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)}>
            変更を保存しました。
          </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">新しいアプリケーションを作成</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">
                      既存のアプリケーションと統合
                    </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">サンプルアプリケーションを表示</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="アプリケーション名" placeholder="マイアプリ" value={name} onChange={setName} />
          <p className="text-sm text-zinc-500">
            後でアプリケーション設定から変更できます。
          </p>
          <div className="flex gap-3">
            <Button onClick={() => onSave(name)}>保存</Button>
            <Button variant="outline" onClick={onCancel}>
              キャンセル
            </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">
              アプリケーションを選択
            </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="コールバックURL" placeholder="http://localhost:3000" value={callbacks} onChange={setCallbacks} />
            <Input id="logout" name="allowed_logout_urls" label="ログアウトURL" placeholder="http://localhost:3000" value={logouts} onChange={setLogouts} />
            <Input id="origins" name="web_origins" label="許可されたWebオリジン" placeholder="http://localhost:3000" value={origins} onChange={setOrigins} />

            <div className="flex gap-3 pt-2">
              <Button type="submit">保存</Button>
              <Button variant="outline" type="button" onClick={onCancel}>
                キャンセル
              </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="Auth0アカウントにサインアップ" style={{
      width: "250px",
      height: "250px"
    }} />
        <span className="text-center" style={{
      width: "400px"
    }}>
          <a href="https://auth0.com/signup" target="_blank" rel="noopener noreferrer">
            Auth0アカウント
          </a>
          にサインアップするか、既存のアカウントに
          <span className="font-semibold text-primary dark:text-white cursor-pointer" onClick={() => console.log("log in")}>
            ログイン
          </span>
          して、お客様のテナントと直接統合できます。
        </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"
    }}>
          サインアップ
        </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: "Auth0 を構成する"
}, {
  id: "install-the-auth0-flutter-sdk",
  title: "Auth0 Flutter SDK をインストールする"
}, {
  id: "add-login-to-your-application",
  title: "アプリケーションにログイン機能を追加する"
}, {
  id: "add-logout-to-your-application",
  title: "アプリケーションにログアウト機能を追加する"
}, {
  id: "show-user-profile-information",
  title: "ユーザー プロファイル情報を表示する"
}];

<Recipe>
  <Content>
    Auth0を使用すると、アプリケーションに認証を迅速に追加し、ユーザープロファイル情報にアクセスできます。このガイドでは、[Auth0 Flutter SDK](https://github.com/auth0/auth0-flutter)を使用して、Auth0をFlutterアプリケーションに統合する方法を説明します。

    <Info>
      Flutter SDK は現在、Android、iOS、または Web 上で動作する Flutter アプリケーションのみをサポートしています。
    </Info>

    このクイックスタートは、[Flutter](https://flutter.dev/) アプリケーションがすでに稼働していることを前提としています。まだの場合は、[Flutter の「はじめに」ガイド](https://docs.flutter.dev/get-started/install)を参照して、シンプルなアプリを作成してください。

    [Flutterコマンドラインツール](https://docs.flutter.dev/reference/flutter-cli)についても理解しておく必要があります。

    <Section id={sections[0].id} title={sections[0].title} stepNumber="1">
      Auth0 にサインアップしたときに、新しいアプリケーションが作成されているか、もしくはご自身で新しくアプリケーションを作成しているはずです。
      Auth0 と連携するには、そのアプリケーションに関するいくつかの情報が必要になります。これらの情報は Auth0 Dashboard の [Application
      Settings](https://manage.auth0.com/#/applications) セクションから取得できます。

      <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>
        Native または Single Page Application で Default App を使用する場合は、**Token
        Endpoint Authentication Method** を `None` に更新し、**Application Type** を
        `SPA` または `Native` のいずれかに設定してください。
      </Info>

      次の情報が必要です。

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

      <Info>
        このページ上部からサンプルをダウンロードした場合、これらの情報はあらかじめ入力されています。
      </Info>

      ### コールバックURLの設定

      コールバックURLは、ユーザーが認証された後に Auth0 がアプリケーションへリダイレクトする URL です。
      アプリのコールバックURLは、[Application
      Settings](https://manage.auth0.com/#/applications) の **Allowed Callback URLs** フィールドに追加する必要があります。このフィールドが設定されていない場合、ユーザーはアプリケーションにログインできず、エラーが発生します。

      <Info>
        このページ上部からダウンロードしたサンプルプロジェクトに従っている場合、**Allowed Callback URL** を `http://localhost:3000` に設定してください。
      </Info>

      ### ログアウトURLの設定

      ログアウトURLは、ユーザーが認可サーバーからログアウトされた後に、Auth0 がアプリケーションにリダイレクトできる URL です。これは `returnTo` クエリパラメーターで指定されます。アプリのログアウトURLは、[Application Settings](https://manage.auth0.com/#/applications) の **Allowed Logout URLs** フィールドに追加する必要があります。このフィールドが設定されていない場合、ユーザーはアプリケーションからログアウトできず、エラーが発生します。

      <Info>
        このページ上部からダウンロードしたサンプルプロジェクトに従っている場合、**Allowed Logout URLs** フィールドに追加する必要があるログアウトURLは `http://localhost:3000` です。
      </Info>

      ### Allowed Web Origins の設定

      アプリの URL を [Application Settings](https://manage.auth0.com/#/applications/\{yourClientId}/settings) の **Allowed Web Origins** フィールドに追加する必要があります。ここにアプリケーションの URL を登録しない場合、アプリケーションは認証トークンをサイレントリフレッシュできず、ユーザーは次回アプリケーションにアクセスしたとき、またはページを更新したときにログアウトされてしまいます。

      <Info>
        このページ上部からダウンロードしたサンプルプロジェクトに従っている場合、**Allowed Web Origins** を `http://localhost:3000` に設定してください。
      </Info>
    </Section>

    <Section id={sections[1].id} title={sections[1].title} stepNumber="2">
      Auth0 Flutter ソフトウェア開発キット (SDK) をプロジェクトに追加します：

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

      次の `script` タグを `index.html` ページに追加します。

      ```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/ja-JP/authenticate/login/auth0-universal-login) は、
      アプリケーションに認証をセットアップする最も簡単な方法です。最高のエクスペリエンスとセキュリティ、そして最も充実した機能を利用するために、その使用を推奨します。

      `Auth0Web` クラスを使用して、Flutter Web アプリに Auth0 Universal Login を統合します。`loginWithRedirect()` を使用して、
      ユーザーを Auth0 Universal Login ページにリダイレクトします。

      <Info>
        通常は `loginWithRedirect` に対して `redirectUrl` パラメーターを指定する必要があります。これを省略すると、Auth0 は [既定のログインルート](/docs/ja-JP/authenticate/login/auth0-universal-login/configure-default-login-routes) を使用しますが、これはデフォルトでは構成されていません。
      </Info>

      ユーザーがログインすると、アプリケーションにリダイレクトされて戻ってきます。その後、起動時に `onLoad` を呼び出し、
      渡された認証情報を処理することで、このユーザーの ID トークンとアクセストークンにアクセスできます。

      ```js theme={null}
      auth0.onLoad().then((final credentials) => setState(() {
      // ここで認証情報を処理または保存する

      _credentials = credentials;

      }));
      ```

      <Note>
        ##### チェックポイント

        アプリに、`loginWithRedirect()` を呼び出してユーザーをアプリにログインさせるボタンを追加します。
        認証のために Auth0 にリダイレクトされ、その後アプリケーションに戻ってくることを確認します。

        `onLoad` の呼び出し結果として `credentials` にアクセスでき、
        ID トークンとアクセストークンにもアクセスできることを確認します。
      </Note>
    </Section>

    <Section id={sections[3].id} title={sections[3].title} stepNumber="4">
      ユーザーをログアウトさせるには、Auth0 Flutter ソフトウェア開発キット (SDK) の `logout()` を呼び出し、Auth0 のログアウトエンドポイントへリダイレクトしてログインセッションをクリアします。[Auth0 からのログアウトの詳細はこちらをご覧ください](/docs/ja-JP/authenticate/login/logout)。

      <Info>
        通常は `logout` を呼び出すときに `returnToUrl` を指定する必要があります。指定しない場合、
        Auth0 は[デフォルトで、許可されたログアウトURLのリスト内の最初の URL を使用します](/docs/ja-JP/authenticate/login/logout/redirect-users-after-logout)。
      </Info>

      <Note>
        ##### チェックポイント

        `logout()` を呼び出してアプリケーションからユーザーをログアウトさせるボタンを、アプリに追加します。
        そのボタンを選択したときに、Flutter アプリがログアウトエンドポイントにリダイレクトされ、再び戻ってくることを確認してください。
        アプリケーションにログインしたままになっていないことを確認してください。
      </Note>
    </Section>

    <Section id={sections[4].id} title={sections[4].title} stepNumber="5">
      ユーザープロファイルは、ページの読み込み時に自動的にユーザーのプロファイル情報を取得し、アプリケーションの起動時に `onLoad` を呼び出すことでアクセスおよび保存できます。`onLoad` から返されるオブジェクトには、すべてのユーザーのプロファイル情報を格納した `user` プロパティが含まれます。これは内部的に IDトークンをデコードして設定されます。

      <Note>
        ##### チェックポイント

        ログインして、結果の `user` プロパティを確認します。`email` や `name` など、現在のユーザーのプロファイル情報が正しいことを確認してください。
      </Note>
    </Section>

    ## 次のステップ

    素晴らしい!ここまで進めたなら、アプリケーションでログイン、ログアウト、ユーザープロファイル情報が動作しているはずです。<br /><br />

    これでクイックスタートチュートリアルは終了ですが、他にも多くの機能があります。Auth0でできることの詳細については、以下をご確認ください:

    * [Auth0 Dashboard](https://manage.auth0.com/dashboard/us/dev-s3674bdouue0bd73) - Auth0 テナントとアプリケーションの設定と管理について学びます
    * [auth0-flutter SDK](https://www.github.com/auth0/auth0-flutter/) - このチュートリアルで使用するソフトウェア開発キット (SDK) についてさらに詳しく確認してください
    * [Auth0 Marketplace](https://marketplace.auth0.com/) - 有効化して Auth0 の機能を拡張できるインテグレーションを見つける
  </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("ログイン"))
                else
                  Column(
                    children: [
                      ProfileView(user: _credentials!.user),
                      ElevatedButton(
                          onPressed: () async {
                            await auth0.logout(returnToUrl: 'http://localhost:3000');
                          },
                          child: const Text("ログアウト"))
                    ],
                  )
              ],
            );
          }
        }
        ```

        ```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("ログイン"))
                else
                  Column(
                    children: [
                      ProfileView(user: _credentials!.user),
                      ElevatedButton(
                          onPressed: () async {
                            await auth0.logout(returnToUrl: 'http://localhost:3000');
                          },
                          child: const Text("ログアウト"))
                    ],
                  )
              ],
            );
          }
        }
        ```

        ```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>
