Skip to content

Commit

Permalink
♻️refactor : #53 실시간 발음테스트 리팩토링, 뒤로가기 해도 작동하도록 수정
Browse files Browse the repository at this point in the history
  • Loading branch information
bunju20 committed Apr 9, 2024
1 parent 8adfe0f commit a1c3f5a
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 173 deletions.
3 changes: 3 additions & 0 deletions lib/bindings/root_binding.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:get/get.dart';
import 'package:earlips/viewModels/home/home_viewmodel.dart';
import 'package:earlips/viewModels/root/root_viewmodel.dart';
import 'package:earlips/viewModels/study/study_viewmodel.dart';
import 'package:earlips/viewModels/realtime/real_create_viewmodel.dart';

class RootBinding extends Bindings {
@override
Expand All @@ -12,5 +13,7 @@ class RootBinding extends Bindings {
// ChildViewModel is singleton
Get.put(HomeViewModel());
Get.put(StudyViewModel());
Get.put(RealCreateViewModel());

}
}
82 changes: 82 additions & 0 deletions lib/viewModels/realtime/real_create_viewmodel.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:speech_to_text/speech_to_text.dart' as stt;
import 'package:permission_handler/permission_handler.dart';

class RealCreateViewModel extends GetxController {
final speechToText = stt.SpeechToText();
var isRecording = false.obs;
var handDone = false.obs; // 추가
var text = ''.obs;

@override
void onInit() {
super.onInit();
requestPermission();
}

void toggleRecording() {
print("녹음 버튼 클릭" );
if (isRecording.value) {
print("이제 끌게용");
handDone.value = true;
stopListening();
} else {
print("이제 시작할게용");
handDone.value = false;
isRecording.value = true;
startListening();
}
}
void startListening() async {
bool available = await speechToText.initialize(onStatus: handleStatus);
if (available) {
speechToText.listen(onResult: (result) {
if (result.finalResult) {
// 기존 텍스트에 이어서 새로 인식된 텍스트를 추가합니다.
text.value += "${result.recognizedWords} ";
}
},
listenFor: const Duration(minutes: 5),
pauseFor: const Duration(seconds: 3),
);
isRecording.value = true;
}
}

void stopListening() {
speechToText.stop();
isRecording.value = false;
}

void handleStatus(String status) {
print("현재 상태: $status");
print("handDone: $handDone");
print("isRecording: $isRecording");
if (handDone.value) {
return;
}
if (status == 'done') {
stopListening();
Future.delayed(Duration(milliseconds: 100), () {
startListening();
});
if (status == 'notListening') {
startListening();
}
}
}

Future<void> requestPermission() async {
var status = await Permission.microphone.status;
if (!status.isGranted) {
await Permission.microphone.request();
}
}

@override
void onClose() {
speechToText.stop();
super.onClose();
}
}
2 changes: 1 addition & 1 deletion lib/views/home/home_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class HomeScreen extends BaseScreen<HomeViewModel> {
isLoggedIn: isLoggedIn,
vm: viewModel,
),
const MidWidget(),
MidWidget(),
// 로그인 상태에 따라 _Bottom 클래스의 컨테이너 색상을 변경
BottomWidget(isLoggedIn: isLoggedIn),
],
Expand Down
2 changes: 1 addition & 1 deletion lib/views/home/widget/mid_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class MidWidget extends StatelessWidget {
InkWell(
onTap: () {
Get.to(() =>
RealCreateScriptPage()); // Adjust the screen name as necessary
RealCreateScriptPage(),preventDuplicates: false); // Adjust the screen name as necessary
},
child: Container(
margin: const EdgeInsets.only(left: 20.0),
Expand Down
228 changes: 57 additions & 171 deletions lib/views/realtime/real_create_script_screen.dart
Original file line number Diff line number Diff line change
@@ -1,189 +1,75 @@
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:speech_to_text/speech_to_text.dart' as stt;
import 'package:permission_handler/permission_handler.dart';
import 'package:get/get_utils/src/extensions/internacionalization.dart';

class RealCreateScriptPage extends StatefulWidget {
const RealCreateScriptPage({super.key});

@override
_RealCreateScriptPageState createState() => _RealCreateScriptPageState();
}
import 'package:get/get.dart';
import 'package:earlips/viewModels/realtime/real_create_viewmodel.dart';
import 'package:earlips/views/base/base_screen.dart';

class _RealCreateScriptPageState extends State<RealCreateScriptPage> {
bool isRecording = false;
bool handDone = false;
TextEditingController textEditingController = TextEditingController();
stt.SpeechToText speechToText = stt.SpeechToText();

@override
void initState() {
super.initState();
requestPermission();
speechToText.initialize(
onStatus: (status) {
handleStatus(status); // 상태 처리를 위한 함수 호출
},
);
}

void handleStatus(String status) {
if (handDone) {
return;
}
if (status == 'done') {
stopListening();
// 잠시 후 다시 시작하기 위해 delay를 사용
Future.delayed(Duration(milliseconds: 100), () {
startListening();
});
if (status == 'notListening') {
startListening();
}
}
// 필요한 경우 여기에 다른 상태에 대한 처리를 추가할 수 있습니다.
}
class RealCreateScriptPage extends BaseScreen<RealCreateViewModel> {
const RealCreateScriptPage({Key? key}) : super(key: key);

@override
@override
void dispose() {
// SpeechToText 리스닝을 멈춥니다.
speechToText.stop();

// SpeechToText 리소스를 정리합니다.
speechToText.cancel();
// 텍스트 컨트롤러를 정리합니다.
textEditingController.dispose();

super.dispose();
}

Future<void> requestPermission() async {
var microphoneStatus = await Permission.microphone.status;
if (!microphoneStatus.isGranted) {
await Permission.microphone.request();
}
}

void toggleRecording() {
if (isRecording) {
//완전히 끝내겠다고 설정하는 부분.
handDone = true;
stopListening();
} else {
handDone = false;
startListening();
}
}
Widget buildBody(BuildContext context) {
final RealCreateViewModel speechController = Get.put(RealCreateViewModel());

Future<void> startListening() async {
bool available = await speechToText.initialize(onStatus: (status) {
handleStatus(status);
});

if (!available) {
return;
}
speechToText.listen(
onResult: (result) {
if (mounted) {
setState(() {
if (result.finalResult) {
// 기존 텍스트에 이어서 새로 인식된 텍스트를 추가합니다.
textEditingController.text += "${result.recognizedWords} ";
}
});
}
},
listenFor: const Duration(minutes: 5),
pauseFor: const Duration(seconds: 3),
);
if (mounted) {
setState(() => isRecording = true);
}
}

Future<void> stopListening() async {
bool available = await speechToText.initialize(onStatus: (status) {
handleStatus(status);
});
speechToText.stop();
if (mounted) {
setState(() => isRecording = false);
}
}

@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
FocusScope.of(context).requestFocus(FocusNode());
},
child: Scaffold(
appBar: AppBar(
title: Text('live_script_title'.tr),
centerTitle: true,
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () {
// 뒤로 가기 버튼을 눌렀을 때 텍스트 필드를 초기화합니다.
textEditingController.text = "";
Navigator.of(context).pop();
handDone = false;
},
),
return Scaffold(
appBar: AppBar(
title: Text('Live Script Title'),
centerTitle: true,
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () => Get.back(),
),
body: Stack(
children: [
Padding(
padding: const EdgeInsets.fromLTRB(25, 20, 25, 100),
child: TextField(
controller: textEditingController,
expands: true,
maxLines: null,
decoration: InputDecoration(
hintText: 'voice_recognition'.tr,
fillColor: Colors.white,
filled: true,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(15.0),
borderSide: BorderSide.none,
),
),
body: Stack(
children: [
Padding(
padding: const EdgeInsets.fromLTRB(25, 20, 25, 100),
child: Obx(() => TextField(
controller: TextEditingController(text: speechController.text.value),
expands: true,
maxLines: null,
decoration: InputDecoration(
hintText: 'Voice Recognition',
fillColor: Colors.white,
filled: true,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(15.0),
borderSide: BorderSide.none,
),
textAlignVertical: TextAlignVertical.top,
),
),
Positioned(
bottom: 20,
left: 0,
right: 0,
child: Align(
alignment: Alignment.bottomCenter,
child: Ink(
decoration: BoxDecoration(
color: isRecording ? Colors.red : Colors.blue,
borderRadius: BorderRadius.circular(40),
),
child: InkWell(
borderRadius: BorderRadius.circular(40),
onTap: toggleRecording,
child: Padding(
padding: const EdgeInsets.all(20),
child: Icon(
isRecording ? Icons.stop : Icons.mic,
size: 30,
color: Colors.white,
),
textAlignVertical: TextAlignVertical.top,
)),
),
Positioned(
bottom: 20,
left: 0,
right: 0,
child: Align(
alignment: Alignment.bottomCenter,
child: Obx(() => Ink(
decoration: BoxDecoration(
color: speechController.isRecording.value ? Colors.red : Colors.blue,
borderRadius: BorderRadius.circular(40),
),
child: InkWell(
borderRadius: BorderRadius.circular(40),
onTap: () => speechController.toggleRecording(),
child: Padding(
padding: const EdgeInsets.all(20),
child: Icon(
speechController.isRecording.value ? Icons.stop : Icons.mic,
size: 30,
color: Colors.white,
),
),
),
),
),
),
],
),
),
],
),
);
}

}

0 comments on commit a1c3f5a

Please sign in to comment.