「最近なんとなく調子が悪い」「先月は気持ちが安定していた気がする」——でも、何がその差を生み出しているのか、なかなか把握できないものです。
自分の感情を記録することで、睡眠・運動・天気・人間関係など、気分に影響する要因が見えてきます。今回はRorkを使って、ムードトラッカーアプリを作るチュートリアルをお届けします。
今回作るアプリの概要
完成するアプリの機能:
- 5段階の気分スコアを毎日記録(最高〜最悪)
- 気分に合った絵文字アイコンで選びやすいUI
- 今日の気分に対してひとこと日記メモを追加
- 過去の記録をカレンダー形式で振り返り
- 週間・月間の気分グラフで傾向を把握
Rorkでの開発手順
ステップ1:プロジェクト作成
Rork(rork.app)にログインして「新しいプロジェクト」を作成します。プロジェクト名は「MoodTracker」や「気分記録アプリ」としましょう。
ステップ2:アプリの仕様をRorkに伝える
Rorkのチャット入力欄に以下のプロンプトを入力してください:
気分・感情を記録するムードトラッカーアプリを作ってください。
【必要な機能】
1. 今日の気分記録
- 5段階の気分スコアを選択(1=最悪、2=悪い、3=普通、4=良い、5=最高)
- 各スコアに対応する絵文字アイコン(😢😟😐🙂😊)を表示
- ひとこと日記メモを自由入力できる(任意)
- カテゴリタグを複数選択できる(仕事・家族・健康・趣味・その他)
2. 記録の履歴表示
- カレンダービューで日ごとの気分を色分け表示
- リストビューで最近30件の記録を表示
- 各記録タップで詳細(スコア・メモ・タグ)を確認
3. 傾向グラフ
- 直近7日間の気分スコアを折れ線グラフで表示
- 月別の平均スコアを棒グラフで表示
4. データ保存
- ローカルに全データを保存
- アプリを閉じても記録が消えない
【デザイン】
- 落ち着いたパステルカラー(気持ちが和らぐ配色)
- 絵文字アイコンを大きく表示して選びやすいUI
- 1日1回の入力をストレスなく行えるシンプルさ
ステップ3:生成されたコードを確認する
Rorkが自動生成するコードの主要部分を見てみましょう。
気分データの型定義(TypeScript):
// types/mood.ts
export type MoodScore = 1 | 2 | 3 | 4 | 5;
export type MoodCategory =
'work' | 'family' | 'health' | 'hobby' | 'other';
export interface MoodEntry {
id: string;
date: string; // YYYY-MM-DD形式
score: MoodScore;
note: string;
categories: MoodCategory[];
createdAt: string;
}
// スコアに対応する表示設定
export const MOOD_CONFIG: Record<MoodScore, {
emoji: string;
label: string;
color: string;
bgColor: string;
}> = {
1: { emoji: '😢', label: '最悪', color: '#EF5350', bgColor: '#FFEBEE' },
2: { emoji: '😟', label: '悪い', color: '#FF7043', bgColor: '#FBE9E7' },
3: { emoji: '😐', label: '普通', color: '#FFA726', bgColor: '#FFF3E0' },
4: { emoji: '🙂', label: '良い', color: '#66BB6A', bgColor: '#E8F5E9' },
5: { emoji: '😊', label: '最高', color: '#42A5F5', bgColor: '#E3F2FD' },
};
export const CATEGORY_CONFIG: Record<MoodCategory, {
label: string;
icon: string;
}> = {
work: { label: '仕事', icon: '💼' },
family: { label: '家族', icon: '👨👩👧' },
health: { label: '健康', icon: '💪' },
hobby: { label: '趣味', icon: '🎨' },
other: { label: 'その他', icon: '⭐' },
};今日の気分入力画面:
// app/(tabs)/index.tsx
import React, { useState } from 'react';
import {
View, Text, TouchableOpacity, TextInput,
StyleSheet, ScrollView, Alert
} from 'react-native';
import { useMoodStore } from '@/store/moodStore';
import { MOOD_CONFIG, CATEGORY_CONFIG } from '@/types/mood';
import type { MoodScore, MoodCategory } from '@/types/mood';
import { getTodayString } from '@/utils/dateUtils';
export default function TodayScreen() {
const { entries, addEntry } = useMoodStore();
const today = getTodayString();
const todayEntry = entries.find(e => e.date === today);
const [selectedScore, setSelectedScore] = useState<MoodScore | null>(null);
const [note, setNote] = useState('');
const [selectedCategories, setSelectedCategories] = useState<MoodCategory[]>([]);
const toggleCategory = (cat: MoodCategory) => {
setSelectedCategories(prev =>
prev.includes(cat) ? prev.filter(c => c !== cat) : [...prev, cat]
);
};
const handleSave = () => {
if (!selectedScore) {
Alert.alert('気分を選んでください');
return;
}
addEntry({
id: Date.now().toString(),
date: today,
score: selectedScore,
note,
categories: selectedCategories,
createdAt: new Date().toISOString(),
});
Alert.alert('✅ 記録しました!');
};
if (todayEntry) {
const config = MOOD_CONFIG[todayEntry.score];
return (
<View style={[styles.container, { backgroundColor: config.bgColor }]}>
<Text style={styles.savedTitle}>今日の気分を記録済み</Text>
<Text style={styles.savedEmoji}>{config.emoji}</Text>
<Text style={[styles.savedLabel, { color: config.color }]}>
{config.label}
</Text>
{todayEntry.note ? (
<Text style={styles.savedNote}>「{todayEntry.note}」</Text>
) : null}
</View>
);
}
return (
<ScrollView style={styles.container}>
<Text style={styles.dateText}>{today}</Text>
<Text style={styles.questionText}>今日の気分はどうですか?</Text>
{/* 気分スコア選択 */}
<View style={styles.moodRow}>
{([1, 2, 3, 4, 5] as MoodScore[]).map(score => {
const config = MOOD_CONFIG[score];
const isSelected = selectedScore === score;
return (
<TouchableOpacity
key={score}
style={[
styles.moodButton,
{ backgroundColor: isSelected ? config.color : '#F5F5F5' }
]}
onPress={() => setSelectedScore(score)}
>
<Text style={styles.moodEmoji}>{config.emoji}</Text>
<Text style={[
styles.moodLabel,
{ color: isSelected ? '#fff' : '#666' }
]}>
{config.label}
</Text>
</TouchableOpacity>
);
})}
</View>
{/* カテゴリ選択 */}
<Text style={styles.sectionTitle}>今日の気分に関係すること(任意)</Text>
<View style={styles.categoryRow}>
{(Object.entries(CATEGORY_CONFIG) as [MoodCategory, { label: string; icon: string }][]).map(
([cat, { label, icon }]) => {
const isSelected = selectedCategories.includes(cat);
return (
<TouchableOpacity
key={cat}
style={[styles.categoryChip, isSelected && styles.categoryChipSelected]}
onPress={() => toggleCategory(cat)}
>
<Text>{icon} {label}</Text>
</TouchableOpacity>
);
}
)}
</View>
{/* メモ入力 */}
<Text style={styles.sectionTitle}>ひとこと日記(任意)</Text>
<TextInput
style={styles.noteInput}
placeholder="今日あったことや感じたことを..."
multiline
value={note}
onChangeText={setNote}
maxLength={200}
/>
<TouchableOpacity
style={[styles.saveButton, !selectedScore && styles.saveButtonDisabled]}
onPress={handleSave}
disabled={!selectedScore}
>
<Text style={styles.saveButtonText}>記録する</Text>
</TouchableOpacity>
</ScrollView>
);
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: '#FAFAFA', padding: 20 },
dateText: { fontSize: 14, color: '#888', marginBottom: 4 },
questionText: { fontSize: 22, fontWeight: '700', color: '#1a1a1a', marginBottom: 20 },
moodRow: { flexDirection: 'row', justifyContent: 'space-between', marginBottom: 24 },
moodButton: {
flex: 1, marginHorizontal: 4, borderRadius: 12, padding: 10,
alignItems: 'center',
},
moodEmoji: { fontSize: 28, marginBottom: 4 },
moodLabel: { fontSize: 11, fontWeight: '600' },
sectionTitle: { fontSize: 15, fontWeight: '600', color: '#333', marginBottom: 10 },
categoryRow: { flexDirection: 'row', flexWrap: 'wrap', gap: 8, marginBottom: 24 },
categoryChip: {
paddingHorizontal: 14, paddingVertical: 8, borderRadius: 20,
backgroundColor: '#F0F0F0',
},
categoryChipSelected: { backgroundColor: '#C8E6C9' },
noteInput: {
backgroundColor: '#fff', borderRadius: 12, padding: 14,
minHeight: 80, fontSize: 15, textAlignVertical: 'top',
borderWidth: 1, borderColor: '#E0E0E0', marginBottom: 24,
},
saveButton: {
backgroundColor: '#42A5F5', borderRadius: 14, padding: 16, alignItems: 'center',
},
saveButtonDisabled: { backgroundColor: '#BDBDBD' },
saveButtonText: { color: '#fff', fontSize: 17, fontWeight: '700' },
savedTitle: { fontSize: 18, color: '#555', textAlign: 'center', marginBottom: 16 },
savedEmoji: { fontSize: 72, textAlign: 'center', marginBottom: 12 },
savedLabel: { fontSize: 24, fontWeight: '700', textAlign: 'center', marginBottom: 8 },
savedNote: { fontSize: 16, color: '#666', textAlign: 'center', fontStyle: 'italic' },
});ステップ4:履歴・グラフ画面を追加する
チャットに続けて入力します:
気分の履歴を見られる画面を追加してください。
- 過去30日間の記録をリストで表示
- 各行に絵文字・スコア・日付・メモを表示
- タップすると詳細を確認できる
- カレンダービューで日ごとの気分を色で確認できるタブも追加
ステップ5:傾向グラフを実装する
統計・グラフ画面を追加してください。
- 直近7日間の気分スコアを折れ線グラフで表示(recharts または victory-native を使用)
- 今月の平均スコアを表示
- 最も多い気分カテゴリ(仕事・健康など)をランキング表示
ステップ6:プレビューで確認する
アプリをプレビューして動作確認してください。
3日分のダミーデータを入力して、履歴とグラフが正しく表示されるか確認します。
カスタマイズのアイデア
基本のアプリが完成したら、さらに深めてみましょう:
カスタマイズ例①:リマインダー通知
毎晩21時に「今日の気分を記録しましょう」という
プッシュ通知を送る機能を追加してください。
通知時間はユーザーが設定できるようにしてください。
カスタマイズ例②:AI感情コーチング
今日の気分スコアとメモをもとに、
AIが短いアドバイスや励ましのメッセージを生成する機能を追加してください。
APIは Rork の内蔵AI生成機能を使ってください。
カスタマイズ例③:データエクスポート
過去の気分記録をCSV形式でエクスポートできる機能を追加してください。
ファイルはシェアシートで共有・保存できるようにしてください。
全体を振り返って
ムードトラッカーアプリは、シンプルながら日々の生活を豊かにする実用的なツールです。
今回のポイントをまとめると:
- 感情の5段階分類と絵文字UIを組み合わせることで、記録のハードルを下げられる
- カテゴリタグを付けることで、気分に影響する要因を後から分析できる
- グラフ・カレンダーで傾向を可視化すると、自己理解が深まる
- カスタマイズは段階的に追加していくのが成功のコツ
自分の感情のパターンに気づくことが、より充実した毎日への第一歩です。ぜひ作って、使い続けてみてください。
Rorkのさらなる活用事例として、Rorkで習慣管理アプリを作るチュートリアルやRorkで日記アプリを作るチュートリアルも参考にしてください。
メンタルウェルネスとテクノロジーの活用について深く
12年の個人開発で見えてきたこと
リリース直後にチェックしているもの
- クラッシュレポートの上位エラーを24時間ごとに確認しているか
- AdMob/StoreKit の収益ダッシュボードに異常検知が出ていないか
- ユーザーレビューに技術的な指摘が含まれていないか