From 35197ee5e45333d2de58bdf678e5e2b50200a004 Mon Sep 17 00:00:00 2001 From: bunju20 <85238126+bunju20@users.noreply.github.com> Date: Mon, 19 Feb 2024 18:20:41 +0900 Subject: [PATCH 1/4] =?UTF-8?q?fix:=20=EB=8C=80=EB=B3=B8=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EB=B0=9B?= =?UTF-8?q?=EC=9D=84=EC=88=98=20=EC=9E=88=EA=B2=8C=20=EC=84=B8=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/app/build.gradle | 2 + lib/viewModels/script/analyze_viewmodel.dart | 45 +++++++++++-------- .../script/create_script_viewmodel.dart | 12 +++-- lib/views/script/analyze_screen.dart | 6 +++ 4 files changed, 43 insertions(+), 22 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 1c8f9b8..bd095f4 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -66,3 +66,5 @@ flutter { } dependencies {} + +apply plugin: 'com.google.gms.google-services' diff --git a/lib/viewModels/script/analyze_viewmodel.dart b/lib/viewModels/script/analyze_viewmodel.dart index f2f831b..82d407a 100644 --- a/lib/viewModels/script/analyze_viewmodel.dart +++ b/lib/viewModels/script/analyze_viewmodel.dart @@ -1,26 +1,33 @@ import 'package:flutter/foundation.dart'; +import 'package:http/http.dart' as http; +import 'dart:convert'; // JSON 데이터를 다루기 위해 필요 class AnalyzeViewModel with ChangeNotifier { + List userWord = []; + List userSenten = []; + List wrongWordIndexes = []; + List wrongFastIndexes = []; - final List userWord = [ - "가", "나", "다", "어쩌구", "저쩌구", "입니다", "나는", "매일", "조깅을", "합니다", - "플러터로", "앱", "개발을", "배우고", "있어요", "이것은", "더미", "텍스트입니다", - "데이터를", "시각화하는", "것은", "중요합니다" - ]; - final List userSenten = [ - "가 나 다 어쩌구 저쩌구 입니다.", - "나는 매일 조깅을 합니다.", - "플러터로 앱 개발을 배우고 있어요.", - "이것은 더미 텍스트입니다.", - "데이터를 시각화하는 것은 중요합니다.", - ]; - final List wrongWordIndexes = [2, 14]; // "다", "앱"을 가리킴 - final List wrongFastIndexes = [1, 3]; // 두 번째와 네 번째 문장을 가리킴 - - // ViewModel 초기화 - AnalyzeViewModel() { - // 필요한 초기화 로직 추가 + // 비동기 데이터 로딩 함수는 그대로 유지 + Future fetchDataFromUrl(String url) async { + try { + final response = await http.get(Uri.parse(url)); + if (response.statusCode == 200) { + final data = json.decode(response.body); + updateUserLists(data); + } else { + print("Failed to load data"); + } + } catch (e) { + print("Error fetching data: $e"); + } } -// 여기에 필요한 기능을 추가하세요. + void updateUserLists(Map data) { + userWord = List.from(data['user_word']); + userSenten = List.from(data['user_sentence']); + wrongWordIndexes = List.from(data['wrong']); + wrongFastIndexes = List.from(data['speed'].map((x) => x.toDouble())); + notifyListeners(); + } } diff --git a/lib/viewModels/script/create_script_viewmodel.dart b/lib/viewModels/script/create_script_viewmodel.dart index d7d17b1..9ac4556 100644 --- a/lib/viewModels/script/create_script_viewmodel.dart +++ b/lib/viewModels/script/create_script_viewmodel.dart @@ -73,7 +73,7 @@ class CreateScriptViewModel extends ChangeNotifier { Future sendTextAndAudio() async { - String url = 'https://heheds.free.beeceptor.com'; + String url = 'https://962554f7-5348-4141-a3df-1a50c06b79b5-00-15gg2xcmiy7a0.sisko.replit.dev/upload'; String textToSend = writedTextController.text; if (audioFilePath == null) { print('Audio file is not available.'); @@ -90,10 +90,16 @@ class CreateScriptViewModel extends ChangeNotifier { var response = await request.send(); if (response.statusCode == 200) { - print('Data and audio sent successfully'); + // 서버로부터의 응답 스트림을 문자열로 변환하여 출력 + final respStr = await response.stream.bytesToString(); + print('Server response: $respStr'); } else { - print('Failed to send data and audio'); + print('Failed to send data and audio. Status code: ${response.statusCode}'); + // 에러 응답이 있을 경우 출력 + final respStr = await response.stream.bytesToString(); + print('Server error response: $respStr'); } + } catch (e) { print(e.toString()); } diff --git a/lib/views/script/analyze_screen.dart b/lib/views/script/analyze_screen.dart index d53608a..605e1d2 100644 --- a/lib/views/script/analyze_screen.dart +++ b/lib/views/script/analyze_screen.dart @@ -16,6 +16,12 @@ class AnalyzeScreen extends StatefulWidget { } class _AnalyzeScreenState extends State { + final viewModel = AnalyzeViewModel(); + @override + void initState() { + super.initState(); + viewModel.fetchDataFromUrl("https://486ec2d3-f415-4919-8ea8-e5ca6656f875-00-j9718y89efig.pike.replit.dev/data"); + } @override Widget build(BuildContext context) { From 3adf1e5d2f7da7652808e9fbcb6f7919fdd93ada Mon Sep 17 00:00:00 2001 From: bunju20 <85238126+bunju20@users.noreply.github.com> Date: Mon, 19 Feb 2024 23:01:41 +0900 Subject: [PATCH 2/4] =?UTF-8?q?feat:=20view=20model=EA=B9=8C=EC=A7=80=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/viewModels/script/analyze_viewmodel.dart | 36 +++++++++---------- .../script/create_script_viewmodel.dart | 36 +++++++++++-------- lib/views/script/analyze_screen.dart | 22 +++++++----- 3 files changed, 51 insertions(+), 43 deletions(-) diff --git a/lib/viewModels/script/analyze_viewmodel.dart b/lib/viewModels/script/analyze_viewmodel.dart index 82d407a..40ab365 100644 --- a/lib/viewModels/script/analyze_viewmodel.dart +++ b/lib/viewModels/script/analyze_viewmodel.dart @@ -1,33 +1,29 @@ import 'package:flutter/foundation.dart'; +import 'package:flutter_sound/flutter_sound.dart'; import 'package:http/http.dart' as http; import 'dart:convert'; // JSON 데이터를 다루기 위해 필요 - class AnalyzeViewModel with ChangeNotifier { List userWord = []; List userSenten = []; List wrongWordIndexes = []; List wrongFastIndexes = []; - // 비동기 데이터 로딩 함수는 그대로 유지 - Future fetchDataFromUrl(String url) async { - try { - final response = await http.get(Uri.parse(url)); - if (response.statusCode == 200) { - final data = json.decode(response.body); - updateUserLists(data); + void updateData(Map data) { + userWord = List.from(data['user_word'] as List? ?? []); + userSenten = List.from(data['user_sentence'] as List? ?? []); + wrongWordIndexes = List.from(data['wrong'] as List? ?? []); + wrongFastIndexes = (data['speed'] as List? ?? []).map((e) { + + if (e is double) { + return e; + } else if (e is int) { + return e.toDouble(); } else { - print("Failed to load data"); + // 로그 출력 또는 오류 처리 + print("Warning: Invalid type in 'speed' list, defaulting to 0.0"); + return 0.0; // 기본값 } - } catch (e) { - print("Error fetching data: $e"); - } - } - - void updateUserLists(Map data) { - userWord = List.from(data['user_word']); - userSenten = List.from(data['user_sentence']); - wrongWordIndexes = List.from(data['wrong']); - wrongFastIndexes = List.from(data['speed'].map((x) => x.toDouble())); - notifyListeners(); + }).toList(); + notifyListeners(); // 데이터가 업데이트되면 리스너들에게 알립니다. } } diff --git a/lib/viewModels/script/create_script_viewmodel.dart b/lib/viewModels/script/create_script_viewmodel.dart index 9ac4556..cda49c2 100644 --- a/lib/viewModels/script/create_script_viewmodel.dart +++ b/lib/viewModels/script/create_script_viewmodel.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:speech_to_text/speech_to_text.dart' as stt; @@ -8,6 +10,8 @@ import 'dart:async'; import 'dart:io'; import 'package:http/http.dart' as http; import 'package:earlips/views/script/analyze_screen.dart'; +import 'package:earlips/viewModels/script/analyze_viewmodel.dart'; + class CreateScriptViewModel extends ChangeNotifier { bool isRecording = false; @@ -28,6 +32,7 @@ class CreateScriptViewModel extends ChangeNotifier { } void _init() async { + Get.put(AnalyzeViewModel()); await requestPermission(); speechToText.initialize( onError: (val) => print('Error: $val'), @@ -75,37 +80,40 @@ class CreateScriptViewModel extends ChangeNotifier { Future sendTextAndAudio() async { String url = 'https://962554f7-5348-4141-a3df-1a50c06b79b5-00-15gg2xcmiy7a0.sisko.replit.dev/upload'; String textToSend = writedTextController.text; - if (audioFilePath == null) { + if (audioFilePath.isEmpty) { print('Audio file is not available.'); return; } try { var request = http.MultipartRequest('POST', Uri.parse(url)) - ..fields['text'] = textToSend // 텍스트 데이터 추가 - ..files.add(await http.MultipartFile.fromPath( - 'audio', // 서버에서 음성 파일을 식별하는 필드명 - audioFilePath, - )); + ..fields['text'] = textToSend + ..files.add(await http.MultipartFile.fromPath('audio', audioFilePath)); var response = await request.send(); if (response.statusCode == 200) { - // 서버로부터의 응답 스트림을 문자열로 변환하여 출력 final respStr = await response.stream.bytesToString(); + final jsonResponse = json.decode(respStr); print('Server response: $respStr'); + + if (jsonResponse != null && jsonResponse['data'] != null) { + final analyzeViewModel = Get.find(); + analyzeViewModel.updateData(jsonResponse['data']); + Get.to(() => AnalyzeScreen()); // AnalyzeScreen으로 이동 + } else { + print('Received null or invalid data from the server.'); + } } else { print('Failed to send data and audio. Status code: ${response.statusCode}'); - // 에러 응답이 있을 경우 출력 - final respStr = await response.stream.bytesToString(); - print('Server error response: $respStr'); } - } catch (e) { print(e.toString()); } } + + void _handleStatus(String status) { if (handDone) return; if (status == 'done') { @@ -155,11 +163,9 @@ class CreateScriptViewModel extends ChangeNotifier { notifyListeners(); } - void complete() { + void complete() async { print("완료버튼 눌렀습니다."); - sendTextAndAudio(); // 텍스트와 오디오 파일 전송 - //스크린을 이동 analyze_screen으로 이동하는 코드 - Get.to(() => AnalyzeScreen()); + await sendTextAndAudio(); // 비동기 호출로 수정 } diff --git a/lib/views/script/analyze_screen.dart b/lib/views/script/analyze_screen.dart index 605e1d2..a2b44f9 100644 --- a/lib/views/script/analyze_screen.dart +++ b/lib/views/script/analyze_screen.dart @@ -11,16 +11,20 @@ import '../../utilities/app_routes.dart'; class AnalyzeScreen extends StatefulWidget { + + AnalyzeScreen({Key? key}) : super(key: key); // 생성자에서 데이터를 받습니다. @override _AnalyzeScreenState createState() => _AnalyzeScreenState(); } class _AnalyzeScreenState extends State { - final viewModel = AnalyzeViewModel(); + late AnalyzeViewModel viewModel; + @override void initState() { super.initState(); - viewModel.fetchDataFromUrl("https://486ec2d3-f415-4919-8ea8-e5ca6656f875-00-j9718y89efig.pike.replit.dev/data"); + viewModel = Get.put(AnalyzeViewModel()); // 여기서 viewModel을 등록 + print(viewModel.userSenten); } @override @@ -89,7 +93,7 @@ class _AnalyzeScreenState extends State { } class TextStylingWidget extends StatelessWidget { - final AnalyzeViewModel model = Get.put(AnalyzeViewModel()); + final viewModel = AnalyzeViewModel(); @override Widget build(BuildContext context) { @@ -108,12 +112,12 @@ class TextStylingWidget extends StatelessWidget { List spans = []; int globalWordIndex = 0; // 전체 단어에 대한 인덱스를 추적합니다. - for (int i = 0; i < model.userSenten.length; i++) { - final List words = model.userSenten[i].split(' '); + for (int i = 0; i < viewModel.userSenten.length; i++) { + final List words = viewModel.userSenten[i].split(' '); List wordSpans = []; for (String word in words) { - final bool isWrongWord = model.wrongWordIndexes.contains(globalWordIndex); + final bool isWrongWord = viewModel.wrongWordIndexes.contains(globalWordIndex); wordSpans.add(TextSpan( text: "$word ", style: TextStyle( @@ -126,7 +130,7 @@ class TextStylingWidget extends StatelessWidget { spans.add(TextSpan( children: wordSpans, style: TextStyle( - decoration: model.wrongFastIndexes.contains(i) ? TextDecoration.underline : TextDecoration.none, + decoration: viewModel.wrongFastIndexes.contains(i) ? TextDecoration.underline : TextDecoration.none, ), )); spans.add(TextSpan(text: "\n")); // 문장 사이에 줄바꿈 추가 @@ -189,4 +193,6 @@ class _TopText extends StatelessWidget { ), ); } -} \ No newline at end of file + +} + From ca2190bfde8afe0f8fef13112e80bc6c68cccc1f Mon Sep 17 00:00:00 2001 From: bunju20 <85238126+bunju20@users.noreply.github.com> Date: Tue, 20 Feb 2024 04:07:36 +0900 Subject: [PATCH 3/4] =?UTF-8?q?feat:=20api=EB=A1=9C=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20=EB=B0=9B=EC=95=84=EC=84=9C=20=EB=B6=84=EC=84=9D=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=EB=A7=8C=EB=93=A4=EA=B8=B0=20?= =?UTF-8?q?=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/views/script/analyze_screen.dart | 36 +++++++++++++--------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/lib/views/script/analyze_screen.dart b/lib/views/script/analyze_screen.dart index a2b44f9..e3bae56 100644 --- a/lib/views/script/analyze_screen.dart +++ b/lib/views/script/analyze_screen.dart @@ -8,11 +8,8 @@ import 'package:permission_handler/permission_handler.dart'; import 'package:earlips/viewModels/script/analyze_viewmodel.dart'; import '../../utilities/app_routes.dart'; - - class AnalyzeScreen extends StatefulWidget { - - AnalyzeScreen({Key? key}) : super(key: key); // 생성자에서 데이터를 받습니다. + AnalyzeScreen({Key? key}) : super(key: key); @override _AnalyzeScreenState createState() => _AnalyzeScreenState(); } @@ -46,13 +43,13 @@ class _AnalyzeScreenState extends State { width: Get.width - 40, height: Get.height * 0.2, margin: EdgeInsets.all(20.0), - padding: EdgeInsets.all(10.0), // 내부 여백을 추가합니다. + padding: EdgeInsets.all(10.0), decoration: BoxDecoration( - color: Colors.white, // 배경색을 지정합니다. - borderRadius: BorderRadius.circular(15.0), // 테두리 둥글기를 지정합니다. - border: Border.all(color: Colors.white), // 테두리 색상을 지정합니다. 필요에 따라 변경 가능합니다. + color: Colors.white, + borderRadius: BorderRadius.circular(15.0), + border: Border.all(color: Colors.white), ), - child: _TopText(), + child: _TopText(), // 이 부분은 상태를 표시하지 않으므로 그대로 유지합니다. ), Stack( children: [ @@ -61,15 +58,14 @@ class _AnalyzeScreenState extends State { width: Get.width - 40, height: Get.height * 0.5, margin: EdgeInsets.all(20.0), - padding: EdgeInsets.all(10.0), // 내부 여백을 추가합니다. + padding: EdgeInsets.all(10.0), decoration: BoxDecoration( - color: Colors.white, // 배경색을 지정합니다. - borderRadius: BorderRadius.circular(15.0), // 테두리 둥글기를 지정합니다. - border: Border.all(color: Colors.white), // 테두리 색상을 지정합니다. 필요에 따라 변경 가능합니다. + color: Colors.white, + borderRadius: BorderRadius.circular(15.0), + border: Border.all(color: Colors.white), ), - child: TextStylingWidget(), + child: TextStylingWidget(viewModel: viewModel), // viewModel을 전달합니다. ), - ], ), ], @@ -78,13 +74,12 @@ class _AnalyzeScreenState extends State { floatingActionButton: Container( width: Get.width, alignment: Alignment.bottomCenter, - child: FloatingActionButton( onPressed: () { Get.toNamed(Routes.HOME); }, - child: Icon(Icons.home), // 홈 아이콘 사용 - tooltip: '홈으로', // 롱 프레스 시 표시되는 텍스트 + child: Icon(Icons.home), + tooltip: '홈으로', ), ), ), @@ -93,7 +88,9 @@ class _AnalyzeScreenState extends State { } class TextStylingWidget extends StatelessWidget { - final viewModel = AnalyzeViewModel(); + final AnalyzeViewModel viewModel; // viewModel을 받기 위한 생성자 파라미터를 추가합니다. + + TextStylingWidget({required this.viewModel}); // 생성자를 통해 viewModel을 초기화합니다. @override Widget build(BuildContext context) { @@ -108,6 +105,7 @@ class TextStylingWidget extends StatelessWidget { ); } + List _buildTextSpans() { List spans = []; int globalWordIndex = 0; // 전체 단어에 대한 인덱스를 추적합니다. From b876106fc47a40615579025e9d40dc00205c28ff Mon Sep 17 00:00:00 2001 From: bunju20 <85238126+bunju20@users.noreply.github.com> Date: Tue, 20 Feb 2024 04:19:23 +0900 Subject: [PATCH 4/4] =?UTF-8?q?feat:=20=EB=B9=A0=EB=A5=B4=EB=A9=B4=20?= =?UTF-8?q?=EB=B9=A8=EA=B0=95,=20=EB=8A=90=EB=A6=AC=EB=A9=B4=20=EB=B3=B4?= =?UTF-8?q?=EB=9D=BC=EC=83=89=EC=9C=BC=EB=A1=9C=20=ED=91=9C=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/views/script/analyze_screen.dart | 32 +++++++++++++++++----------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/lib/views/script/analyze_screen.dart b/lib/views/script/analyze_screen.dart index e3bae56..f806d8e 100644 --- a/lib/views/script/analyze_screen.dart +++ b/lib/views/script/analyze_screen.dart @@ -108,7 +108,7 @@ class TextStylingWidget extends StatelessWidget { List _buildTextSpans() { List spans = []; - int globalWordIndex = 0; // 전체 단어에 대한 인덱스를 추적합니다. + int globalWordIndex = 0; for (int i = 0; i < viewModel.userSenten.length; i++) { final List words = viewModel.userSenten[i].split(' '); @@ -122,16 +122,29 @@ class TextStylingWidget extends StatelessWidget { color: isWrongWord ? Colors.red : Colors.black, ), )); - globalWordIndex++; // 각 단어를 처리할 때마다 전체 단어 인덱스를 증가시킵니다. + globalWordIndex++; + } + + // `wrongFastIndexes`의 값에 따라 밑줄 색상을 결정합니다. + Color underlineColor = Colors.white; // 기본값은 투명색입니다. + if (viewModel.wrongFastIndexes[i] < 1) { + underlineColor = Colors.purple; // 1미만인 경우 보라색 밑줄 + } else if (viewModel.wrongFastIndexes[i] > 1) { + underlineColor = Colors.red; // 1초과인 경우 빨간색 밑줄 } spans.add(TextSpan( children: wordSpans, style: TextStyle( - decoration: viewModel.wrongFastIndexes.contains(i) ? TextDecoration.underline : TextDecoration.none, + decoration: viewModel.wrongFastIndexes[i] != 0 ? TextDecoration.underline : TextDecoration.none, + decorationColor: underlineColor, // 밑줄 색상을 지정합니다. + decorationStyle: TextDecorationStyle.solid, + decorationThickness: 3.0, + //밑줄을 밑으로 내리기 위한 값 + ), )); - spans.add(TextSpan(text: "\n")); // 문장 사이에 줄바꿈 추가 + spans.add(TextSpan(text: "\n")); } return spans; @@ -174,16 +187,9 @@ class _TopText extends StatelessWidget { text: TextSpan( style: TextStyle(fontSize: 16, color: Colors.black), children: [ - TextSpan(text: '문장의 빠르기가 빠르거나 느리면 밑줄이 표시됩니다. ex)'), + TextSpan(text: '문장이 빠르면 빨강, 느리면 보라색 밑줄로 표시됩니다.'), // 예시에 적용할 스타일 - TextSpan( - text: '강아지는 ', - style: TextStyle(decoration: TextDecoration.underline), - ), - TextSpan( - text: '뛴다', - style: TextStyle(decoration: TextDecoration.underline), - ), + //들여쓰기 ], ), ),