MORE

BLOG

Flutter × Firestoreで社内アンケートアプリを作成した話。|質問作成・回答まで解説

  • Firestore
  • Flutter
  • WEBアプリ
  • アンケート
  • バックオフィス
  • プログラミング
  • 社内
  • 開発
25.06.16

はじめに

「Slackや口頭でアンケートはちょっと煩雑…」

そんな思いから、Flutter と Firebase Firestoreを使って、社内向けのアンケートアプリをゼロから開発してみました。Flutterの学習も兼ねてます!

課題: 手間なく快捷に匿名性を担保したままアンケートを行い内容を簡単に集計したい。

実装した機能:

・アプリ上で質問を作成・編集・削除ができる

・Firestore上で設問を管理

・ユーザーが簡単に回答

・回答をFirestore上に保存

・複数の質問種別に対応(テキスト・ラジオ・チェックボックス・日時)※ファイルは今回なし拡張は可能

実際に作成したアプリのUI:

Flutter × Firestoreで社内アンケートアプリを作成した話。|質問作成・回答まで解説 - 株式会社テックディレクション
Flutter × Firestoreで社内アンケートアプリを作成した話。|質問作成・回答まで解説 - 株式会社テックディレクション
Flutter × Firestoreで社内アンケートアプリを作成した話。|質問作成・回答まで解説 - 株式会社テックディレクション
Flutter × Firestoreで社内アンケートアプリを作成した話。|質問作成・回答まで解説 - 株式会社テックディレクション

1. アプリ概要と機能

・アプリ上で質問内容を管理できる管理画面の実装 → ノーコード感覚で運用可能にするため、UIから質問を追加・編集・削除できる構成に。エンジニア以外の利用も想定。

答え画面で質問に答える → ユーザーが迷わず操作できるよう、1つの質問ごとにカード化。選択肢に応じた入力UI(ラジオ・チェックボックスなど)を自動表示。

・FirestoreのデータをもとにUIを自動生成 → 質問データをFirestoreに保存し、回答画面で読み込んで動的にレンダリング。アンケートの種類が増えてもコード改修なしで対応可能。

・答えをデータベースに保存 → 回答結果は時系列・ラウンドID付きでFirestoreに保存。後の集計や可視化(例:CSV出力、BI連携)を見据えたデータ設計。

2. Firestoreのデータ構成

コレクション: rounds

{
  "title": "社内満足度調査6月版",
  "questions": [
    {
      "id": "q1",
      "type": "radio",
      "text": "現在の業務内容に満足していますか?",
      "options": ["満足", "やや満足", "不満"]
    },
    {
      "id": "q2",
      "type": "checkbox",
      "text": "使用しているコミュニケーションツールは?",
      "options": ["Slack", "Teams", "Zoom"]
    },
    {
      "id": "q3",
      "type": "datetime_range",
      "text": "集中したい時間帯は?"
    }
  ]
}

解説:

title:アンケートのタイトル(管理画面の一覧や結果集計時に利用)

questions:複数の質問を配列で定義。各質問には一意のidとtype、質問文text、選択肢optionsが含まれます。

typeによって、回答画面でのUIコンポーネント(例:ラジオボタン、チェックボックス、日付ピッカー)を切り替える仕組みです。

設計意図:Firestoreに設問構造を柔軟に持たせることで、アンケートの変更・追加をコード修正なしで可能にし、運用性を高めています。

コレクション: answers

{
  "roundId": "survey_202406",
  "createdAt": Timestamp,
  "answers": {
    "q1": "満足",
    "q2": ["Slack", "Zoom"],
    "q3": {
      "start": "2024-06-10T09:00:00",
      "end": "2024-06-10T11:00:00"
    }
  }
}

解説:

roundId:どのアンケートの回答かを識別するためのID(roundsのドキュメントIDと対応)

createdAt:回答時刻(集計・時系列分析に活用)

answers:設問IDごとの回答をMap形式で保存(選択肢、複数回答、日付範囲など対応)

設計意図:集計処理をしやすいように設問IDをキーとして構造化。選択肢型・複数回答型・日時型など、拡張しやすいスキーマを意識しています。

Firestoreはスキーマレスながらも構造化しておくことで、今後の管理画面のグラフ描画やCSV出力、さらにはLooker Studioとの連携などにもスムーズに対応できる基盤となります。

3. Flutterの答え画面実装

回答画面では、質問の種類(type)に応じてUIを動的に切り替えられるようにしています。 質問は1つずつカード風に表示することで視認性を高め、回答しやすい設計にしています。

質問をカード風

Container(
  margin: const EdgeInsets.symmetric(vertical: 12, horizontal: 24),
  padding: const EdgeInsets.all(16),
  decoration: BoxDecoration(
    color: Colors.white,
    borderRadius: BorderRadius.circular(16),
    boxShadow: [BoxShadow(color: Colors.black26, blurRadius: 6)],
  ),
  child: _buildQuestionWidget(id, data),
);

解説:

・1問1カード構成にすることで、「質問が独立して表示される」ためユーザーが迷いにくく。

・BoxDecoration を使って影や角丸をつけることで、UIにメリハリをつけて直感的な回答体験を意識。

radio

RadioListTile<String>(
  title: Text(opt),
  value: opt,
  groupValue: _answers[id],
  onChanged: (val) => setState(() => _answers[id] = val!),
);

解説:

・RadioListTile を使うことで、タップしやすくデザイン性のある単一選択UIが簡単に実装可能。

・選択済みの状態が視覚的にわかるため、確実な回答がしやすく。

checkbox

CheckboxListTile(
  title: Text(opt),
  value: _answers[id].contains(opt),
  onChanged: (val) {
    setState(() {
      if (val!) {
        _answers[id].add(opt);
      } else {
        _answers[id].remove(opt);
      }
    });
  },
);

解説:

・複数回答が可能な設問用に、チェックボックス型のウィジェットを使用。

・回答は配列(List)で管理し、チェック状態に応じて追加・削除を制御しています。

・UI操作とデータ更新の同期がわかりやすく、直感的に使える設計

datetime_range

ElevatedButton(
  onPressed: () => _selectDateTime(context, true),
  child: Text(startTime == null ? '開始日時を選択' : startTime.toString()),
);

解説:

・このタイプの質問では、開始時刻と終了時刻の範囲をユーザーに選んでもらう設計です。

・showDatePicker + showTimePicker を組み合わせて、日時を段階的に選べるように実装します。

・回答結果は Firestore に “start”・”end” の形で保存され、時間帯ごとの傾向分析などに使えます。

工夫した点:

・開始と終了の入力をそれぞれ個別のボタンで操作できるようにし、誤入力を防止。

・選択状態が Text に反映されるため、ユーザー自身も選択内容を確認しやすい。

このように、質問タイプごとに適したUIウィジェットを選びつつ、ユーザー体験を損なわずに正確な回答を促す構成を意識して実装しています。

質問が増えても共通ロジックで処理できるよう、設計段階から「拡張性」も考慮しています。

Flutter × Firestoreで社内アンケートアプリを作成した話。|質問作成・回答まで解説 - 株式会社テックディレクション

4. Firestoreへの保存

ユーザーがすべての質問に回答し、「送信」ボタンを押すと、Firestoreの answers コレクションにデータが保存されます。

保存処理のコード

FirebaseFirestore.instance.collection('answers').add({
  'roundId': widget.roundId,
  'createdAt': Timestamp.now(),
  'answers': _answers,
});

解説:

collection(‘answers’)

・回答データはすべて answers コレクションに保存。

・各ドキュメントが1人分の回答に対応し、ユニークなIDが自動付与されます(add()により)。

roundId

・どのアンケート(=どの回の設問セット)への回答かを示すID。

・rounds コレクションのドキュメントIDと一致させることで、設問と回答を正確に紐づけできます。

createdAt

・回答時刻を Timestamp 型で保存。

・後から時系列で並べたり、集計期間を限定する際に活用します(例:月次レポート・トレンド分析)。

answers

・各質問の id をキーに、ユーザーの回答をMap形式で保持。

・質問種別ごとに以下のように保存形式が異なります:

 radio: 文字列(例: “満足”)

 checkbox: 配列(例: [“Slack”, “Zoom”])

 datetime_range: オブジェクト形式(例: {start: “…”, end: “…”})

設計上のポイント

構造化された回答データにすることで、将来的にCSV出力・グラフ集計・BIツール(Looker Studioなど)との連携が可能。

・roundId 単位でフィルタリングすることで、アンケートごとの集計が簡単に。

・Firestoreのスキーマレス特性を活かしつつ、意図的に構造を統一しておくことで、クエリ効率や保守性も確保しています。

注意点と工夫

・回答送信後は SnackBar でフィードバックを表示し、二重送信や不安を防止。

・null や未回答の項目が混在してもクラッシュしないよう、Firestoreに保存する前に _answers を検証・整形するようにしています。

このように、「使いやすさ」と「後から集計しやすい構造」の両立を意識した保存設計にしています。 回答ログが蓄積されていくことで、社内での継続的なフィードバックループが実現できる土台となることを目指してます。

5. UI/UXの工夫

・カード表示で見やすく

・中央寄せと平均な空間

・スマホもWebも対応

6. 管理画面

社内運用を見据えて、非エンジニアでもアンケートを扱えるようにするための管理画面を設計中です。

Firebase Consoleだけに依存せず、Webアプリとして完結するUIを用意することを目指しています。

主な機能

機能説明
アンケート作成UI付きで質問を編集できるようにし、Firestoreのドキュメント構造を気にせず直感的に操作可能にします。将来的にはドラッグ&ドロップで並び替えも対応予定。
作成済みアンケートの一覧表示過去に作成したアンケートを一覧で表示し、回答数や公開ステータスなどを可視化。管理しやすくすることで社内で継続利用される仕組みに。
アンケートタイトルの変更タイトル変更のみのために編集画面を開く必要がないように。

7. 展望: フェーズごとの設計

フェーズ内容
Phase 1アンケート作成機能(設問・選択肢の追加、プレビュー、Firestore反映)を実装。これにより非エンジニアでもアンケートを作成できるようになります。
Phase 2答え画面(本記事で紹介済み)。質問の種類に応じて動的にUIを生成し、ユーザーが迷わず回答できる体験を重視した実装。
Phase 3結果集計画面。Firestoreの回答データをもとに、選択肢別の件数・比率を可視化(円グラフ・棒グラフなど)。CSV出力やLooker Studio連携も検討中。

おわりに

Flutter と Firebaseで、シンプルで使いやすい社内向けアンケートアプリが作れました!

これからは管理画面やダッシュボード連携なども進め、社内でのフィードバックを正しく回せる環境を整備していきます。

「自分も作ってみたい」と思って頂けたら幸いです。

意見や質問があれば気軽にご連絡ください!

著者:Miyazawa

「自分も作ってみたい!」と思った方へ

コード共有・GitHub公開をご希望の方はお気軽にコメントまたはご連絡ください!

コメントはこちら