Flutter × Supabase で構築した勤怠アプリ

決裁者が、
楽になる勤怠アプリ。

既存の勤怠アプリは「決裁者・管理者側」が複雑すぎる。Kittan は 記録と表示だけに振り切り、給与計算はしない。 承認は月1回ワンタップ。中小企業の管理部門のための、引き算の勤怠アプリです。

🐦 Flutter (Dart) Supabase 🐘 PostgreSQL + RLS 🔁 Realtime
一括承認月1回・ワンタップ
🔁
リアルタイム同期申請→管理者に即反映
3フレーバー構成
9テーブル / PostgreSQL
25RLSポリシー
7テーブルをRealtime購読
¥0から運用可能
The Problem

勤怠アプリは「使う人」より
「決裁する人」がしんどい。

ユーザー側はどのサービスもシンプル。でも管理者・決裁者側は、労基法準拠の自動計算を入れるほど設定が増え、結局経理が手で再計算している——という現場の声から出発しました。

承認が終わらない

個別承認・多段階フロー

項目ごと・段階ごとに承認が発生し、月末に決裁者の手が止まる。一括で片付けたいのにボタンが分かれている。

設定が地獄

初期設定が複雑すぎる

残業ルール・36協定・手当の自動計算。設定項目が膨大で、導入する前に心が折れる。

結局Excel

自動計算が逆に邪魔

企業ごとに計算ルールが違いすぎて、アプリの自動計算を信用できず経理がExcelで再集計している。

だったら、計算はやめる。記録と表示だけに振り切る。

The Approach

記録と表示だけやる。計算はしない。
機能を足すのではなく、決裁者を楽にするために割り切ることを設計の中心に置きました。

やること

  • +ワンタップ打刻(出勤 / 退勤)
  • +カレンダーで勤怠一覧・シフト / 有給の自己申告
  • +月1回ワンタップで一括承認
  • +管理者が打刻を直接編集(申請フロー不要・監査ログ付き)
  • +共用タブレットでのみんなの打刻

あえて、やらないこと

  • ×残業代・給与の自動計算
  • ×有給残日数の自動管理(表示のみ)
  • ×多段階承認フロー
  • ×36協定アラート
  • ×交通費の経路検索・自動計算
「できない」のではなく「決裁者の負担になるからやらない」。何を載せないかを決めることも設計だと考えました。
Features

役割ごとに分けた、3つのアプリ

同一コードベースから、個人版・管理者版・共用タブレット版を別フレーバーとしてビルド。役割に必要なものだけを見せています。

FLAVOR 01 / 個人版(スマホ)

打刻も申告も、片手で完結。

従業員が毎日触る画面。大きな出勤 / 退勤ボタンと、カレンダーから直接できる自己申告に絞りました。

  • ワンタップ出退勤打刻(GPS位置を任意添付)
  • 月間カレンダーで勤怠・シフト・有給を一覧
  • 日付タップでシフト登録 / 有給申請 / 打刻修正
  • 複数日を選んで一括シフト登録・シフト種別の自由設定
FLAVOR 02 / 管理者版(PC・スマホ)

全社員の状況が、ひと目で。

決裁者のための画面。打刻漏れや未承認をバッジで可視化し、承認は選択 → ワンタップ。打刻の編集はすべて監査ログに残ります。

  • ダッシュボードで全社員の勤怠サマリー・打刻漏れ / 未承認バッジ
  • 選択承認 / 全件承認・有給の月一括承認
  • 社員別詳細から打刻を直接編集(before/after を監査ログ記録)
  • 有給付与(法定日数計算)・拠点管理・従業員アカウント登録
FLAVOR 03 / 共用タブレット版

玄関に置く、みんなの打刻機。

店舗・拠点の入口に据え置く共用端末。拠点でログインすると、その拠点に所属するメンバーだけが並びます。大人数でも素早く本人を選べる「あかさたな」インデックス付き。

  • 拠点ログイン → その拠点のメンバーだけを表示(RLSで保証)
  • あかさたな高速インデックス(人数規模に応じて3モード切替)
  • 大型デジタル時計・出勤 / 退勤の確認ダイアログで誤打刻防止
  • 管理者の拠点割当変更がタブレットへリアルタイム反映
Live Demo

ブラウザで、そのまま触れる。

下のボタンから、実際の Kittan(Flutter Web ビルド)がその場で起動します。Supabase バックエンドに接続したデモ環境で、3つのフレーバーをそれぞれ体験できます。ログイン情報はデモアカウントが入力済みです。

FLAVOR 01

個人版スマホ向け

毎日の出退勤打刻とカレンダー申告。従業員が触る画面です。

メールp@amati.jp
パスワードamati26
個人版を起動 ↗
FLAVOR 02

管理者版PC・スマホ向け

全社員の勤怠ダッシュボードと月1回ワンタップの一括承認。

メールa@amati.jp
パスワードamati26
管理者版を起動 ↗
FLAVOR 03

共用タブレット版据え置き端末

拠点の入口に置く共用打刻機。あかさたなインデックス付き。

メールt@amati.jp
パスワードamati26
タブレット版を起動 ↗

※ 共有のデモ環境です。実在する個人情報は入力しないでください。初回起動時は Flutter Web の読み込みに数秒かかる場合があります。

Tech Stack

技術スタック

フロントは Flutter 単一コードベースで Web / Android を出力。バックエンドは Supabase をフル活用し、認可をデータベース層(RLS)に寄せています。

FRONTEND

Flutter (Dart)

単一コードで Web / Android を出力。3フレーバー構成。

  • Riverpod(状態管理)
  • go_router(宣言的ルーティング)
  • table_calendar
BACKEND

Supabase

BaaS。認証・DB・リアルタイムを一括で利用。

  • Auth(メール / パスワード)
  • Realtime(Publication購読)
  • RPC(SECURITY DEFINER関数)
DATABASE

PostgreSQL + RLS

9テーブル。認可は行レベルセキュリティで実装。

  • Row Level Security 25ポリシー
  • role / company による行制御
  • 監査ログ(JSONB before/after)
PLATFORM

マルチプラットフォーム

同一バックエンドへ複数クライアントから接続。

  • Web(管理者・デモ)
  • Android(個人 / タブレット)
  • geolocator(GPS打刻)
supabase_flutter ^2.5 flutter_riverpod ^2.5 go_router ^14 table_calendar ^3.1 geolocator ^10 intl ・ csv ・ shared_preferences
Architecture

設計思想

「役割ごとにアプリを分ける」「認可はデータベースに持たせる」「状態はリアルタイムで一方向に流す」——この3点を軸に設計しました。

個人版main_personal.dart
管理者版main_admin.dart
共用タブレット版main_tablet.dart
↓ 同一コードベース / Riverpod Provider 層 ↓
🐘 PostgreSQL + Row Level Security

role(personal / admin / tablet)と company_id をもとに、SELECT/INSERT/UPDATE を行レベルで制御。認可ロジックをクライアントに持たせず、SECURITY DEFINER のヘルパー関数 user_role() / user_company_id() でDB側に集約。

🔁 Realtime Publication StreamProvider

7テーブルを supabase_realtime publication に登録し、クライアントは StreamProvider.family で購読。個人版の申請が管理者版に、管理者の拠点割当変更が共用タブレットに即時反映される。

01

フレーバーによる役割分離

個人 / 管理者 / タブレットを別エントリポイントとしてビルド。各フレーバーは起動時に該当ロール以外のセッションを自動クリアし、役割の境界を保つ。

02

認可をDBに寄せる

「誰が何を見られるか」をアプリ側の if 文ではなく RLS ポリシーで表現。クライアントを信用しない前提で、不正なクエリはDBが弾く。

03

打刻整合性を RPC で担保

出退勤は rpc_clock_in / rpc_clock_out(SECURITY DEFINER)経由。ON CONFLICT (user_id, date) により同日二重打刻を防止し、サーバー時刻で記録する。

04

編集の追跡可能性

管理者による打刻編集は audit_logs テーブルに before/after を JSONB で記録。「誰が・いつ・何を変えたか」を後から追える。

Engineering Notes

工夫した点・苦労した点

個人開発で実際にハマった所と、その解決アプローチ。誇張なく、実装の実態に即して書いています。

REALTIME

申請が管理者画面に出てこない

課題:個人版で出した有給申請が、管理者版にリアルタイム反映されない不具合。原因は supabase_realtime publication に対象テーブルが1件も登録されていなかったこと。

対処:購読しているテーブル(users / leave_requests / shifts ほか計7つ)を publication に追加するマイグレーションを作成。再実行しても安全なよう pg_publication_tables を確認して未登録のみ追加する冪等な構成にした。

AUTHZ / RLS

認可をアプリに書きたくない

課題:「管理者は自社の社員だけ見える」「個人は自分の記録だけ」をクライアントのフィルタで実装すると、抜け道とロジック散在のリスクがある。

対処:認可をRLSに集約し25ポリシーで表現。user_role() / user_company_id() を SECURITY DEFINER 関数化してポリシー内で再利用し、ポリシー定義を読みやすく保った。

MULTI-FLAVOR

別アプリにセッションが残る

課題:同一デバイスで管理者版にログインした後に個人版を開くと、前のセッションが引き継がれて役割が混ざる。

対処:各フレーバーの起動処理で現在ユーザーの role を確認し、自フレーバーに該当しなければ即サインアウト。通信エラー時もログイン画面で復帰できるようグレースフルに握りつぶす。

UX / SCALE

共用端末で目的の人が探せない

課題:共用タブレットは大人数が並ぶと本人を探すのに時間がかかり、毎朝の打刻が渋滞する。

対処:「あかさたな」行インデックスを実装し、人数規模に応じて simple / medium / large の3モードを切替。表示モードは SharedPreferences に永続化し、端末ごとの運用に馴染ませた。

Positioning

競合との立ち位置

多機能で勝負するのではなく、「決裁者が楽か」で差別化。機能を増やすほど決裁者が苦しくなる、という仮説に賭けた設計です。

HRMOS KING OF TIME フリーウェイ Kittan
ユーザー側のシンプルさシンプルシンプルシンプルシンプル
管理者側のシンプルさ複雑複雑古いシンプル
初期設定中程度地獄簡単最小限
承認個別 / 設定要個別 / 多段階基本的一括ワンタップ
有給の月一括承認×××
給与の自動計算ありありなしなし(割り切り)
月額 / 人100円〜300円無料〜無料〜

※ 競合情報は 2026-05 時点の調査に基づく一般的な傾向。Kittan は給与計算・36協定など一部機能を意図的に持たない設計です。