Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨Feat: Gemini 추가 #61

Merged
merged 9 commits into from
May 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_gemini/flutter_gemini.dart';
import 'package:intl/date_symbol_data_local.dart';
import 'package:earlips/main_app.dart';
import 'package:flutter/services.dart';
Expand All @@ -22,6 +23,8 @@ void main() async {
options: DefaultFirebaseOptions.currentPlatform,
);

// gemini
Gemini.init(apiKey: dotenv.env['GEMINI_API_KEY']!);
runApp(EasyLocalization(
// 지원 언어 리스트
saveLocale: true,
Expand Down
26 changes: 26 additions & 0 deletions lib/viewModels/word/word_viewmodel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:earlips/models/user_word_model.dart';
import 'package:earlips/models/word_card_model.dart';
import 'package:earlips/models/word_data_model.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter_gemini/flutter_gemini.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';

Expand All @@ -14,7 +15,9 @@ class WordViewModel extends GetxController {
RxList<WordData> wordList = RxList<WordData>([]);
RxInt currentIndex = 0.obs;
// loading
RxString description = ''.obs;
RxBool isLoading = true.obs;
RxBool isLoadingGemini = true.obs; // 로딩 상태를 관리하기 위한 변수

WordViewModel({required this.type});

Expand All @@ -29,6 +32,26 @@ class WordViewModel extends GetxController {
currentIndex.value = index;
}

// Gemini API 호출을 위한 메서드 정의
Future<void> generateDescription(String speaker) async {
isLoadingGemini.value = true;
final gemini = Gemini.instance;

// Gemini API를 호출하여 결과를 처리합니다.
await gemini
.text(
"If you pronounce it, it's $speaker, tell me in detail in English, not in Korean, so that the deaf can visually see and pronounce it ** Don't use boulders like this and tell me within 150 characters! Never Use Bold and **, Never Over 150 characters if u over this limit, it will be cut off!, never Use Bold and **, never over 150 characters if u over this limit, it will be cut off!")
.then((value) {
// 성공적으로 결과를 받았을 때 출력 및 상태 업데이트
description.value = value?.output ?? "";
print(value?.output); // output이 정확한 필드인지 확인하세요. API 응답에 따라 다를 수 있습니다.
}).catchError((error) {
// 에러 처리
print('Error occurred: $error');
});
isLoadingGemini.value = false;
}

// 단어 데이터 가져오기
Future<void> fetchWords(int type) async {
isLoading.value = true;
Expand All @@ -50,6 +73,7 @@ class WordViewModel extends GetxController {
.doc(uid)
.collection('words')
.get();
// 사용자의 단어 데이터를 UserWord로 변환
final userWords = userWordsQuery.docs
.map((doc) => UserWord.fromDocument(doc.data()))
.toList();
Expand All @@ -69,6 +93,8 @@ class WordViewModel extends GetxController {
}
}

// 단어 AI 생성 Description 가져오기, wordList[0]

// 단어 완료 처리
Future<void> markWordAsDone(WordCard word) async {
final uid = _auth.currentUser?.uid;
Expand Down
131 changes: 84 additions & 47 deletions lib/views/phoneme/phoneme_detail_screen.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:earlips/models/phoneme_model.dart';
import 'package:earlips/utilities/style/color_system.dart';
import 'package:earlips/viewModels/word/word_viewmodel.dart';
import 'package:earlips/views/phoneme/widget/phoneme_list_widget.dart';
import 'package:earlips/views/word/widget/blue_back_appbar.dart';
import 'package:flutter/cupertino.dart';
Expand All @@ -9,11 +10,17 @@ import 'package:get/get.dart';

class PhonemeDetailScreen extends StatelessWidget {
final Phoneme phoneme;
final String realString;

const PhonemeDetailScreen({super.key, required this.phoneme});
PhonemeDetailScreen(
{super.key, required this.phoneme, required this.realString});

final wordViewModel = Get.find<WordViewModel>();

@override
Widget build(BuildContext context) {
wordViewModel.generateDescription(phoneme.symbol);

return Scaffold(
appBar: PreferredSize(
preferredSize: const Size.fromHeight(kToolbarHeight),
Expand All @@ -31,36 +38,37 @@ class PhonemeDetailScreen extends StatelessWidget {
bottomRight: Radius.circular(24),
),
),
child: Column(
children: [
const SizedBox(height: 20),
// --------------------------- 발음 카드
PhonemeListWidget(phoneme: phoneme),
SizedBox(
height: 70,
child: Column(
children: [
const SizedBox(
height: 15,
),
// --------------------------- 발음 안내
Text(
"word_type_12_height".tr,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: ColorSystem.white,
),
),
child: Column(
children: [
const SizedBox(height: 20),
// --------------------------- 발음 카드
PhonemeListWidget(
phoneme: phoneme,
realString: realString,
),
SizedBox(
height: 70,
child: Column(
children: [
const SizedBox(
height: 15,
),
// --------------------------- 발음 안내
Text(
"word_type_12_height".tr,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: ColorSystem.white,
),
],
),
),
],
),
),

),
// --------------------------- 발음 세부 내용 기입
// image
const Padding(
padding: EdgeInsets.all(20),
child: Column(
Expand All @@ -87,28 +95,56 @@ class _BottomBox extends StatelessWidget {

final Phoneme phoneme;

@override
Widget build(BuildContext context) {
return Column(
children: [
Image.asset(
phoneme.imageSrc,
width: Get.width * 0.50,
),
const SizedBox(height: 25),
Container(
padding: const EdgeInsets.all(20.0),
width: Get.width - 40,
margin: EdgeInsets.only(bottom: 40),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15.0),
color: Color(0x80FFFFFF), // 80은 50%
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
spreadRadius: 0,
blurRadius: 20,
offset: const Offset(0, 4),
// <<<<<<< feature/gemini
// --------------------------- 발음 세부 내용 기입
// image
Padding(
padding: const EdgeInsets.all(20),
child: Column(
children: [
Obx(() {
// isLoading이 true이면 로딩 인디케이터를 보여줌
if (wordViewModel.isLoadingGemini.value) {
return const Center(
child: CircularProgressIndicator(), // 로딩 인디케이터
);
} else {
// isLoading이 false이면 설명 텍스트를 보여줌
return Text(
wordViewModel.description.value,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.black, // ColorSystem.black으로 가정
),
);
}
}),
],
// =======
// @override
// Widget build(BuildContext context) {
// return Column(
// children: [
// Image.asset(
// phoneme.imageSrc,
// width: Get.width * 0.50,
// ),
// const SizedBox(height: 25),
// Container(
// padding: const EdgeInsets.all(20.0),
// width: Get.width - 40,
// margin: EdgeInsets.only(bottom: 40),
// decoration: BoxDecoration(
// borderRadius: BorderRadius.circular(15.0),
// color: Color(0x80FFFFFF), // 80은 50%
// boxShadow: [
// BoxShadow(
// color: Colors.black.withOpacity(0.1),
// spreadRadius: 0,
// blurRadius: 20,
// offset: const Offset(0, 4),
// >>>>>>> develop
),
],
),
Expand All @@ -118,7 +154,8 @@ class _BottomBox extends StatelessWidget {
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
const Spacer(),
],
),
],
);
Expand Down
6 changes: 4 additions & 2 deletions lib/views/phoneme/widget/phoneme_list_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ import 'package:get/get.dart';

class PhonemeListWidget extends StatelessWidget {
final Phoneme phoneme;
final String realString;

const PhonemeListWidget({super.key, required this.phoneme});
const PhonemeListWidget(
{super.key, required this.phoneme, required this.realString});

@override
Widget build(BuildContext context) {
Expand All @@ -32,7 +34,7 @@ class PhonemeListWidget extends StatelessWidget {
ListTile(
tileColor: Colors.white,
title: Text(
phoneme.symbol,
realString,
style: const TextStyle(
fontSize: 19,
fontWeight: FontWeight.w600,
Expand Down
5 changes: 4 additions & 1 deletion lib/views/phoneme/widget/vowles_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ class VowelsWidget extends StatelessWidget {
itemBuilder: (context, index) {
return GestureDetector(
onTap: () {
Get.to(() => PhonemeDetailScreen(phoneme: phonemes[index]));
Get.to(() => PhonemeDetailScreen(
phoneme: phonemes[index],
realString: phonemes[index].symbol,
));
},
child: Card(
child: Center(
Expand Down
38 changes: 21 additions & 17 deletions lib/views/script/learning_session_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import 'package:intl/intl.dart'; // DateFormat을 사용하기 위해 추가

class LearningSessionScreen extends StatefulWidget {
final bool isStudyMode; // 스터디 모드 여부를 결정하는 변수
const LearningSessionScreen({super.key,this.isStudyMode = false});
const LearningSessionScreen({super.key, this.isStudyMode = false});

@override
State<LearningSessionScreen> createState() => _LearningSessionScreenState();
Expand All @@ -37,7 +37,8 @@ class _LearningSessionScreenState extends State<LearningSessionScreen> {

@override
Widget build(BuildContext context) {
var title = widget.isStudyMode ? 'study_main_title_4'.tr : 'home_script_title'.tr;
var title =
widget.isStudyMode ? 'study_main_title_4'.tr : 'home_script_title'.tr;
return Scaffold(
appBar: PreferredSize(
preferredSize: const Size.fromHeight(kToolbarHeight),
Expand Down Expand Up @@ -93,18 +94,20 @@ class _LearningSessionScreenState extends State<LearningSessionScreen> {
}
}),
//여기에 조건문 넣고싶어
floatingActionButton: !widget.isStudyMode ? FloatingActionButton(
onPressed: () => Get.to(() => const CreateScriptPage()),
child: const Icon(Icons.add),
) : null, // widget.isStudyMode가 true일 때는 null을 반환
floatingActionButton: !widget.isStudyMode
? FloatingActionButton(
onPressed: () => Get.to(() => const CreateScriptPage()),
child: const Icon(Icons.add),
)
: null, // widget.isStudyMode가 true일 때는 null을 반환
);
}

Widget _buildParagraphContainer(Paragraph paragraph) {
return InkWell(
onTap: () {
Get.to(() => CreateScriptPage(
title: paragraph.title, text: paragraph.text));
Get.to(() =>
CreateScriptPage(title: paragraph.title, text: paragraph.text));
},
child: Container(
width: Get.width * 0.9,
Expand All @@ -128,16 +131,18 @@ class _LearningSessionScreenState extends State<LearningSessionScreen> {
children: [
Container(
alignment: Alignment.centerLeft,
margin: EdgeInsets.only(left: 16.0,top: 16.0,bottom: 8.0),
margin:
const EdgeInsets.only(left: 16.0, top: 16.0, bottom: 8.0),
child: Text(
paragraph.dateFormat == "Example" ? "script example" : "${DateFormat('yyyy년 MM월 dd일 ').format(DateFormat('yyyy/MM/dd').parse(paragraph.dateFormat!))}진행한 학습",
style: TextStyle(
paragraph.dateFormat == "Example"
? "script example"
: "${DateFormat('yyyy년 MM월 dd일 ').format(DateFormat('yyyy/MM/dd').parse(paragraph.dateFormat!))}진행한 학습",
style: const TextStyle(
fontSize: 11,
color: Color(0xFF6E6A7C),
),
),
),

Container(
margin: const EdgeInsets.only(right: 12.0, top: 12.0),
child: SvgPicture.asset("assets/icons/book.svg",
Expand All @@ -147,7 +152,8 @@ class _LearningSessionScreenState extends State<LearningSessionScreen> {
),
Container(
alignment: Alignment.centerLeft,
margin: EdgeInsets.only(left: 16.0,bottom: 8.0, right: 16.0),
margin:
const EdgeInsets.only(left: 16.0, bottom: 8.0, right: 16.0),
child: Text(
overflow: TextOverflow.ellipsis,
paragraph.title,
Expand All @@ -157,7 +163,6 @@ class _LearningSessionScreenState extends State<LearningSessionScreen> {
),
),
),

Container(
margin: const EdgeInsets.only(left: 16, right: 12),
child: Row(
Expand All @@ -166,8 +171,7 @@ class _LearningSessionScreenState extends State<LearningSessionScreen> {
Row(
children: [
SvgPicture.asset("assets/icons/TimeCircle.svg",
width: 14,
color: const Color(0xFFC4C4D9)),
width: 14, color: const Color(0xFFC4C4D9)),
const SizedBox(width: 4),
Text(
paragraph.timeFormat!,
Expand Down Expand Up @@ -199,7 +203,7 @@ class _LearningSessionScreenState extends State<LearningSessionScreen> {
alignment: Alignment.center,
height: 17,
child: Text(
'대본의 총 글자수 : ${text}',
'대본의 총 글자수 : $text',
style: const TextStyle(
color: Color(0xFF34AF20),
fontSize: 9,
Expand Down
Loading
Loading