ã 감정표현 2회 재생 후 기본값으로 ë³돌 변경
|
Before Width: | Height: | Size: 3.7 MiB After Width: | Height: | Size: 548 KiB |
|
Before Width: | Height: | Size: 548 KiB |
|
Before Width: | Height: | Size: 814 KiB |
|
Before Width: | Height: | Size: 1.6 MiB |
|
Before Width: | Height: | Size: 174 KiB After Width: | Height: | Size: 928 KiB |
|
Before Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 1.2 MiB |
@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:video_player/video_player.dart';
|
import 'package:video_player/video_player.dart';
|
||||||
import '../models/chat_message.dart';
|
import '../models/chat_message.dart';
|
||||||
import '../services/chat_service.dart';
|
import '../services/chat_service.dart';
|
||||||
import 'dart:math'; //26.06.24 추가
|
import 'dart:math';
|
||||||
|
|
||||||
|
|
||||||
class ChatScreen extends StatefulWidget {
|
class ChatScreen extends StatefulWidget {
|
||||||
@@ -32,39 +32,36 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
_addInitialMessage();
|
_addInitialMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
//26.06.24 추가 시작
|
(String, int) _getVideoInfo(int emotionCategory) {
|
||||||
String _getVideoFileName(int emotionCategory) {
|
|
||||||
switch(emotionCategory) {
|
switch(emotionCategory) {
|
||||||
case 1:
|
case 1:
|
||||||
return 'default.mp4'; // DEFAULT
|
return ('default.mp4', 10); // DEFAULT
|
||||||
case 2:
|
case 2:
|
||||||
return 'joy.mp4'; // JOY
|
return ('joy.mp4', 8); // JOY
|
||||||
case 3:
|
case 3:
|
||||||
return 'thinking.mp4'; // THINKING
|
return ('thinking.mp4', 10); // THINKING
|
||||||
case 4:
|
case 4:
|
||||||
return 'realization.mp4'; // REALIZATION
|
return ('realization.mp4', 10); // REALIZATION
|
||||||
case 5:
|
case 5:
|
||||||
return 'angry.mp4'; // ANGER
|
return ('angry.mp4', 10); // ANGER
|
||||||
case 6:
|
case 6:
|
||||||
return 'thirst.mp4'; // THIRST
|
return ('thirst.mp4', 10); // THIRST
|
||||||
case 7:
|
case 7:
|
||||||
return 'cold.mp4'; // COLD
|
return ('cold.mp4', 8); // COLD
|
||||||
default:
|
default:
|
||||||
return 'default.mp4'; // 기본값
|
return ('default.mp4', 10); // 기본값
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _initializeVideo(int emotionCategory) {
|
void _initializeVideo(int emotionCategory) {
|
||||||
final videoFileName = _getVideoFileName(emotionCategory);
|
final (videoFileName, duration) = _getVideoInfo(emotionCategory);
|
||||||
|
|
||||||
print('비디오 변경: emotion=$emotionCategory → $videoFileName');
|
print('비디오 변경: emotion=$emotionCategory → $videoFileName (${duration}초)');
|
||||||
|
|
||||||
// 새 비디오 컨트롤러 생성
|
// 새 비디오 컨트롤러 생성
|
||||||
final newController = VideoPlayerController.asset('assets/videos/$videoFileName');
|
final newController = VideoPlayerController.asset('assets/videos/$videoFileName');
|
||||||
|
|
||||||
// 새 비디오를 먼저 로드
|
|
||||||
newController.initialize().then((_) {
|
newController.initialize().then((_) {
|
||||||
// 로드 완료 후에 기존 컨트롤러 교체
|
|
||||||
try {
|
try {
|
||||||
if (_videoController.hasListeners) {
|
if (_videoController.hasListeners) {
|
||||||
_videoController.dispose();
|
_videoController.dispose();
|
||||||
@@ -73,7 +70,6 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
print('기존 컨트롤러 dispose 스킵: $e');
|
print('기존 컨트롤러 dispose 스킵: $e');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 새 컨트롤러로 교체
|
|
||||||
_videoController = newController;
|
_videoController = newController;
|
||||||
_videoController.setLooping(true);
|
_videoController.setLooping(true);
|
||||||
_videoController.setVolume(0.0);
|
_videoController.setVolume(0.0);
|
||||||
@@ -86,7 +82,6 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
print('비디오 로드 오류: $error');
|
print('비디오 로드 오류: $error');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
//26.06.24 추가 끝
|
|
||||||
|
|
||||||
void _addInitialMessage() {
|
void _addInitialMessage() {
|
||||||
setState(() {
|
setState(() {
|
||||||
@@ -101,7 +96,6 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
//26.06.24 추가 시작
|
|
||||||
void _sendMessage(String text) async {
|
void _sendMessage(String text) async {
|
||||||
if (text.isEmpty) return;
|
if (text.isEmpty) return;
|
||||||
|
|
||||||
@@ -148,15 +142,27 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
if (emotionCategory != _currentEmotion) {
|
if (emotionCategory != _currentEmotion) {
|
||||||
print('감정 변경! $emotionCategory로 비디오 교체');
|
print('감정 변경! $emotionCategory로 비디오 교체');
|
||||||
_currentEmotion = emotionCategory;
|
_currentEmotion = emotionCategory;
|
||||||
// 백그라운드에서 비디오 로드 (화면 깜빡임 없음)
|
_isVideoInitialized = false;
|
||||||
_initializeVideo(_currentEmotion);
|
_initializeVideo(_currentEmotion);
|
||||||
|
|
||||||
|
// 영상별 길이에 맞춰 DEFAULT로 복원
|
||||||
|
final (_, duration) = _getVideoInfo(emotionCategory);
|
||||||
|
final delaySeconds = duration * 2; // 2번 재생
|
||||||
|
|
||||||
|
Future.delayed(Duration(seconds: delaySeconds), () {
|
||||||
|
if (mounted) {
|
||||||
|
print('DEFAULT로 복원');
|
||||||
|
_currentEmotion = 1; // DEFAULT = 1
|
||||||
|
_initializeVideo(1);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
_messages.add(
|
_messages.add(
|
||||||
ChatMessage(
|
ChatMessage(
|
||||||
text: responseMessage,
|
text: responseMessage,
|
||||||
isUser: false,
|
isUser: false,
|
||||||
timestamp: DateTime.now(),
|
timestamp: DateTime.now(),
|
||||||
emotionCategory: emotionCategory, // ← 추가
|
emotionCategory: emotionCategory,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
_isLoading = false;
|
_isLoading = false;
|
||||||
@@ -165,13 +171,13 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('메시지 전송 오류: $e');
|
print('메시지 전송 오류: $e');
|
||||||
setState(() {
|
setState(() {
|
||||||
_messages.removeLast(); // ← 로딩 메시지 제거
|
_messages.removeLast();
|
||||||
_messages.add(
|
_messages.add(
|
||||||
ChatMessage(
|
ChatMessage(
|
||||||
text: '오류가 발생했습니다. 다시 시도해주세요.',
|
text: '오류가 발생했습니다. 다시 시도해주세요.',
|
||||||
isUser: false,
|
isUser: false,
|
||||||
timestamp: DateTime.now(),
|
timestamp: DateTime.now(),
|
||||||
emotionCategory: _currentEmotion, // ← 추가
|
emotionCategory: _currentEmotion,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
_isLoading = false;
|
_isLoading = false;
|
||||||
@@ -179,7 +185,6 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
_scrollToBottom();
|
_scrollToBottom();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//26.06.24 추가 끝
|
|
||||||
|
|
||||||
void _scrollToBottom() {
|
void _scrollToBottom() {
|
||||||
Future.delayed(const Duration(milliseconds: 100), () {
|
Future.delayed(const Duration(milliseconds: 100), () {
|
||||||
@@ -213,7 +218,7 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
Container(
|
Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
image: DecorationImage(
|
image: DecorationImage(
|
||||||
image: AssetImage('assets/images/background1.png'),
|
image: AssetImage('assets/images/background.png'),
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -223,7 +228,6 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white.withOpacity(0.8), // 투명도 넣기
|
color: Colors.white.withOpacity(0.8), // 투명도 넣기
|
||||||
borderRadius: BorderRadius.circular(36), // 모서리 둥글게
|
borderRadius: BorderRadius.circular(36), // 모서리 둥글게
|
||||||
// border: Border.all(color: Color(0xFFE0E0E0), width: 1),
|
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
@@ -232,14 +236,6 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
flex: 5,
|
flex: 5,
|
||||||
child: Container(
|
child: Container(
|
||||||
margin: const EdgeInsets.fromLTRB(30, 0, 30, 50),
|
margin: const EdgeInsets.fromLTRB(30, 0, 30, 50),
|
||||||
// decoration: BoxDecoration(
|
|
||||||
// color: Colors.white,
|
|
||||||
// borderRadius: BorderRadius.circular(30),
|
|
||||||
// border: Border.all(
|
|
||||||
// color: Color(0xFFE0E0E0),
|
|
||||||
// width: 1,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
// 헤더
|
// 헤더
|
||||||
@@ -353,7 +349,7 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 55),
|
||||||
// 캐릭터 정보 + 설명 문구
|
// 캐릭터 정보 + 설명 문구
|
||||||
// 1. 가장 바깥쪽 박스 추가
|
// 1. 가장 바깥쪽 박스 추가
|
||||||
Container(
|
Container(
|
||||||
@@ -431,7 +427,12 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
Expanded(
|
Expanded(
|
||||||
flex: 5,
|
flex: 5,
|
||||||
child: Container(
|
child: Container(
|
||||||
margin: const EdgeInsets.all(50),
|
margin: const EdgeInsets.only(
|
||||||
|
top: 50,
|
||||||
|
bottom: 50,
|
||||||
|
right: 50,
|
||||||
|
left: 10,
|
||||||
|
),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Color(0xFFfbfdf8),
|
color: Color(0xFFfbfdf8),
|
||||||
borderRadius: BorderRadius.circular(30),
|
borderRadius: BorderRadius.circular(30),
|
||||||
@@ -533,7 +534,6 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
controller: _scrollController,
|
controller: _scrollController,
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
itemCount: _messages.length,
|
itemCount: _messages.length,
|
||||||
//26.06.24 추가 시작
|
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final message = _messages[index];
|
final message = _messages[index];
|
||||||
final isLoadingMessage = _isLoading &&
|
final isLoadingMessage = _isLoading &&
|
||||||
@@ -544,7 +544,6 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
isLoading: isLoadingMessage,
|
isLoading: isLoadingMessage,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
//26.06.24 추가 끝
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -822,7 +821,7 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
// Layer 이미지 (화면 전체)
|
// Layer 이미지 (화면 전체)
|
||||||
IgnorePointer(
|
IgnorePointer(
|
||||||
child: Image.asset(
|
child: Image.asset(
|
||||||
'assets/images/layer_img1.png',
|
'assets/images/layer_img.png',
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
height: double.infinity,
|
height: double.infinity,
|
||||||
@@ -836,11 +835,11 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
|
|
||||||
class _ChatBubble extends StatelessWidget {
|
class _ChatBubble extends StatelessWidget {
|
||||||
final ChatMessage message;
|
final ChatMessage message;
|
||||||
final bool isLoading; //26.06.24 추가
|
final bool isLoading;
|
||||||
|
|
||||||
const _ChatBubble({
|
const _ChatBubble({
|
||||||
required this.message,
|
required this.message,
|
||||||
this.isLoading = false //26.06.24 추가
|
this.isLoading = false
|
||||||
});
|
});
|
||||||
|
|
||||||
String _formatTime(DateTime dateTime) {
|
String _formatTime(DateTime dateTime) {
|
||||||
@@ -911,7 +910,6 @@ class _ChatBubble extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
// 26.06.24 추가 시작
|
|
||||||
child: isLoading
|
child: isLoading
|
||||||
? _LoadingAnimation() // 로딩 중
|
? _LoadingAnimation() // 로딩 중
|
||||||
: SelectableText(
|
: SelectableText(
|
||||||
@@ -924,7 +922,6 @@ class _ChatBubble extends StatelessWidget {
|
|||||||
height: 1.4,
|
height: 1.4,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
// 26.06.24 추가 끝
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (!message.isUser) const SizedBox(width: 8),
|
if (!message.isUser) const SizedBox(width: 8),
|
||||||
@@ -942,7 +939,6 @@ class _ChatBubble extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//26.06.24 추가 시작
|
|
||||||
class _LoadingAnimation extends StatefulWidget {
|
class _LoadingAnimation extends StatefulWidget {
|
||||||
const _LoadingAnimation();
|
const _LoadingAnimation();
|
||||||
|
|
||||||
@@ -1060,8 +1056,4 @@ class _DotPainter extends CustomPainter {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
bool shouldRepaint(_DotPainter oldDelegate) => false;
|
bool shouldRepaint(_DotPainter oldDelegate) => false;
|
||||||
}
|
}
|
||||||
//26.06.24 추가 끝
|
|
||||||
|
|
||||||
//26.06.24 추가 시작
|
|
||||||
//26.06.24 추가 끝
|
|
||||||
10
pubspec.yaml
@@ -72,17 +72,9 @@ flutter:
|
|||||||
|
|
||||||
- assets/images/profile.png
|
- assets/images/profile.png
|
||||||
- assets/images/background.png
|
- assets/images/background.png
|
||||||
- assets/images/background1.png
|
|
||||||
- assets/images/background2.png
|
|
||||||
- assets/images/background3.png
|
|
||||||
- assets/images/send.png
|
|
||||||
|
|
||||||
|
|
||||||
- assets/images/layer_img.png
|
- assets/images/layer_img.png
|
||||||
- assets/images/layer_img1.png
|
|
||||||
- assets/images/layer_img2.png
|
|
||||||
|
|
||||||
- assets/images/chat_img.png
|
- assets/images/chat_img.png
|
||||||
|
- assets/images/send.png
|
||||||
|
|
||||||
# To add assets to your application, add an assets section, like this:
|
# To add assets to your application, add an assets section, like this:
|
||||||
# assets:
|
# assets:
|
||||||
|
|||||||