Open1
Expo Gemini

健康診断をしてもらう
Geminiと対話するアプリを作る。
モジュールを追加しないと、.env
を読み込めない?
{
"name": "gemini-expo",
"version": "1.0.0",
"main": "index.ts",
"scripts": {
"start": "expo start",
"android": "expo start --android",
"ios": "expo start --ios",
"web": "expo start --web"
},
"dependencies": {
"@google/genai": "^1.8.0",
"@google/generative-ai": "^0.24.1",
"expo": "~53.0.17",
"expo-status-bar": "~2.2.3",
"react": "19.0.0",
"react-native": "0.79.5",
"react-native-dotenv": "^3.4.11"
},
"devDependencies": {
"@babel/core": "^7.25.2",
"@types/react": "~19.0.10",
"typescript": "~5.8.3"
},
"private": true
}
.env
にAPI KEYを設定しておく。
EXPO_PUBLIC_GEMINI_API_KEY=AIzaSyB7Y8Q***************
App.tsxにコードを記載しておく。
import React, { useState } from 'react';
import {
StyleSheet,
Text,
View,
TextInput,
TouchableOpacity,
ScrollView,
ActivityIndicator,
Alert,
KeyboardAvoidingView,
Platform,
} from 'react-native';
import { GoogleGenerativeAI } from '@google/generative-ai';
// @ts-ignore
const genAI = new GoogleGenerativeAI(process.env.EXPO_PUBLIC_GEMINI_API_KEY || '');
export default function App() {
const [mood, setMood] = useState('');
const [diagnosis, setDiagnosis] = useState('');
const [loading, setLoading] = useState(false);
const handleDiagnosis = async () => {
if (!mood.trim()) {
Alert.alert('エラー', '今日の気分を入力してください');
return;
}
setLoading(true);
setDiagnosis('');
try {
const model = genAI.getGenerativeModel({ model: 'gemini-1.5-flash' });
const prompt = `以下の気分から健康状態を診断し、アドバイスをください。
今日の気分: ${mood}
診断結果は以下の形式で回答してください:
1. 総合的な健康状態の評価
2. 心理的な状態の分析
3. 推奨される行動やアドバイス
4. 注意すべき点`;
const result = await model.generateContent(prompt);
const response = result.response;
const text = response.text();
setDiagnosis(text);
} catch (error) {
console.error('Error:', error);
Alert.alert('エラー', 'AI診断中にエラーが発生しました');
} finally {
setLoading(false);
}
};
return (
<KeyboardAvoidingView
style={styles.container}
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
>
<ScrollView contentContainerStyle={styles.scrollContainer}>
<View style={styles.header}>
<Text style={styles.title}>健康診断AI</Text>
<Text style={styles.subtitle}>今日の気分から健康状態を診断します</Text>
</View>
<View style={styles.inputContainer}>
<Text style={styles.label}>今日の気分を入力してください:</Text>
<TextInput
style={styles.textInput}
multiline
numberOfLines={6}
placeholder="例:朝起きた時少し頭が重く、仕事中も集中力が続かなかった。昼食後は少し楽になったが、夕方にかけて疲労感が強くなった..."
value={mood}
onChangeText={setMood}
textAlignVertical="top"
/>
</View>
<TouchableOpacity
style={[styles.button, loading && styles.buttonDisabled]}
onPress={handleDiagnosis}
disabled={loading}
>
{loading ? (
<ActivityIndicator color="#ffffff" />
) : (
<Text style={styles.buttonText}>診断する</Text>
)}
</TouchableOpacity>
{diagnosis !== '' && (
<View style={styles.resultContainer}>
<Text style={styles.resultTitle}>診断結果:</Text>
<ScrollView style={styles.resultScrollView}>
<Text style={styles.resultText}>{diagnosis}</Text>
</ScrollView>
</View>
)}
</ScrollView>
</KeyboardAvoidingView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
},
scrollContainer: {
flexGrow: 1,
padding: 20,
paddingTop: 60,
},
header: {
alignItems: 'center',
marginBottom: 30,
},
title: {
fontSize: 28,
fontWeight: 'bold',
color: '#333',
marginBottom: 8,
},
subtitle: {
fontSize: 16,
color: '#666',
},
inputContainer: {
marginBottom: 20,
},
label: {
fontSize: 16,
fontWeight: '600',
color: '#333',
marginBottom: 10,
},
textInput: {
backgroundColor: '#ffffff',
borderRadius: 12,
padding: 15,
minHeight: 150,
fontSize: 16,
borderWidth: 1,
borderColor: '#ddd',
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.1,
shadowRadius: 3.84,
elevation: 5,
},
button: {
backgroundColor: '#4285F4',
borderRadius: 25,
paddingVertical: 15,
paddingHorizontal: 40,
alignSelf: 'center',
marginBottom: 20,
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 3.84,
elevation: 5,
},
buttonDisabled: {
backgroundColor: '#cccccc',
},
buttonText: {
color: '#ffffff',
fontSize: 18,
fontWeight: 'bold',
},
resultContainer: {
flex: 1,
backgroundColor: '#ffffff',
borderRadius: 12,
padding: 20,
marginTop: 10,
borderWidth: 1,
borderColor: '#ddd',
maxHeight: 400,
},
resultTitle: {
fontSize: 20,
fontWeight: 'bold',
color: '#333',
marginBottom: 15,
},
resultScrollView: {
flex: 1,
},
resultText: {
fontSize: 16,
lineHeight: 24,
color: '#444',
},
});