first commit

This commit is contained in:
2026-03-13 17:14:24 +00:00
commit 39c67599d1
131 changed files with 5277 additions and 0 deletions

373
lib/chat_screen.dart Normal file
View File

@@ -0,0 +1,373 @@
import 'dart:ui';
import 'package:flutter/material.dart';
class ChatScreen extends StatefulWidget {
const ChatScreen({super.key});
@override
State<ChatScreen> createState() => _ChatScreenState();
}
class ChatMessage {
final String text;
final bool isAssistant;
ChatMessage({required this.text, this.isAssistant = false});
}
class _ChatScreenState extends State<ChatScreen> with TickerProviderStateMixin {
final List<ChatMessage> _messages = [];
final TextEditingController _textController = TextEditingController();
final ScrollController _scrollController = ScrollController();
bool _isTyping = false;
void _handleSubmitted(String text) {
if (text.trim().isEmpty) return;
_textController.clear();
setState(() {
_messages.insert(0, ChatMessage(text: text));
_isTyping = true;
});
// Simulate EPVChat! typing delay
Future.delayed(const Duration(milliseconds: 1500), () {
if (mounted) {
setState(() {
_isTyping = false;
_messages.insert(0, ChatMessage(
text: "This is a simulated modern response to: \"$text\"",
isAssistant: true,
));
});
}
});
}
Widget _buildMessage(ChatMessage message) {
bool isAssistant = message.isAssistant;
return Padding(
padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 16.0),
child: Row(
mainAxisAlignment: isAssistant ? MainAxisAlignment.start : MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
if (isAssistant)
Container(
margin: const EdgeInsets.only(right: 12),
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: const LinearGradient(
colors: [Color(0xFF8ad5c9), Color(0xFF57a7ed)], // Mint & Blue
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
boxShadow: [
BoxShadow(
color: const Color(0xFF8ad5c9).withOpacity(0.4),
blurRadius: 8,
offset: const Offset(0, 4),
),
],
),
child: const CircleAvatar(
backgroundColor: Colors.transparent,
radius: 16,
child: Icon(Icons.auto_awesome, color: Colors.white, size: 18),
),
),
Flexible(
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 14.0),
decoration: BoxDecoration(
gradient: isAssistant
? const LinearGradient(
colors: [Color(0xFFF1F5F9), Color(0xFFE2E8F0)], // Light bubbles
begin: Alignment.topLeft,
end: Alignment.bottomRight,
)
: const LinearGradient(
colors: [Color(0xFF8ad5c9), Color(0xFF57a7ed)], // Mint & Blue bubbles
begin: Alignment.centerLeft,
end: Alignment.centerRight,
),
borderRadius: BorderRadius.only(
topLeft: const Radius.circular(20),
topRight: const Radius.circular(20),
bottomLeft: Radius.circular(isAssistant ? 4 : 20),
bottomRight: Radius.circular(isAssistant ? 20 : 4),
),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.2),
blurRadius: 10,
offset: const Offset(0, 4),
),
],
border: Border.all(
color: isAssistant ? Colors.black.withOpacity(0.05) : Colors.transparent,
width: 1,
)
),
child: Text(
message.text,
style: TextStyle(
color: isAssistant ? Colors.black87 : Colors.white,
fontSize: 15,
height: 1.4,
),
),
),
),
if (!isAssistant)
Container(
margin: const EdgeInsets.only(left: 12),
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: const LinearGradient(
colors: [Color(0xFF57a7ed), Color(0xFF3A8BD1)], // User avatar gradient
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
boxShadow: [
BoxShadow(
color: const Color(0xFF57a7ed).withOpacity(0.3),
blurRadius: 8,
offset: const Offset(0, 4),
),
],
),
child: const CircleAvatar(
backgroundColor: Colors.transparent,
radius: 16,
child: Icon(Icons.person, color: Colors.white, size: 18),
),
),
],
),
);
}
Widget _buildTypingIndicator() {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
margin: const EdgeInsets.only(right: 12),
decoration: const BoxDecoration(
shape: BoxShape.circle,
gradient: LinearGradient(
colors: [Color(0xFF8ad5c9), Color(0xFF57a7ed)], // Mint & Blue
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
child: const CircleAvatar(
backgroundColor: Colors.transparent,
radius: 16,
child: Icon(Icons.auto_awesome, color: Colors.white, size: 18),
),
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 12.0),
decoration: BoxDecoration(
color: const Color(0xFFF1F5F9), // Typing bubble matching assistant light bubble
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20),
bottomLeft: Radius.circular(4),
bottomRight: Radius.circular(20),
),
border: Border.all(color: Colors.black.withOpacity(0.05)),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
_buildDot(0),
const SizedBox(width: 4),
_buildDot(1),
const SizedBox(width: 4),
_buildDot(2),
],
),
),
],
),
);
}
Widget _buildDot(int index) {
return TweenAnimationBuilder(
tween: Tween<double>(begin: 0, end: 1),
duration: const Duration(milliseconds: 600),
builder: (context, double value, child) {
return Opacity(
opacity: (value + (index * 0.3)) % 1.0,
child: Container(
width: 6,
height: 6,
decoration: const BoxDecoration(
color: Colors.black54,
shape: BoxShape.circle,
),
),
);
},
);
}
Widget _buildTextComposer() {
return ClipRRect(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 12.0),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.8), // Light backdrop
border: Border(
top: BorderSide(
color: Colors.black.withOpacity(0.05),
width: 0.5,
),
),
),
child: SafeArea(
child: Row(
children: [
Expanded(
child: Container(
decoration: BoxDecoration(
color: const Color(0xFFF1F5F9), // Light input box background
borderRadius: BorderRadius.circular(30.0),
border: Border.all(color: Colors.black.withOpacity(0.05)),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.2),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
),
child: TextField(
controller: _textController,
onSubmitted: _handleSubmitted,
style: const TextStyle(color: Colors.black87),
decoration: InputDecoration(
hintText: "Message EPVChat!...",
hintStyle: TextStyle(color: Colors.black.withOpacity(0.4)),
border: InputBorder.none,
contentPadding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 14.0),
),
),
),
),
const SizedBox(width: 12),
Container(
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [Color(0xFF8ad5c9), Color(0xFF57a7ed)], // Mint & Blue send button
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: const Color(0xFF8ad5c9).withOpacity(0.4),
blurRadius: 8,
offset: const Offset(0, 4),
),
],
),
child: IconButton(
icon: const Icon(Icons.send_rounded, color: Colors.white, size: 20),
onPressed: () => _handleSubmitted(_textController.text),
),
),
],
),
),
),
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
extendBodyBehindAppBar: true,
appBar: PreferredSize(
preferredSize: const Size.fromHeight(kToolbarHeight),
child: ClipRRect(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
child: AppBar(
title: ShaderMask(
shaderCallback: (bounds) => const LinearGradient(
colors: [Color(0xFF8ad5c9), Color(0xFF57a7ed)], // Text gradient header
begin: Alignment.topLeft,
end: Alignment.bottomRight,
).createShader(bounds),
child: const Text(
'EPVChat!',
style: TextStyle(
fontWeight: FontWeight.w800,
fontSize: 22,
letterSpacing: -0.5,
),
),
),
backgroundColor: Colors.white.withOpacity(0.7), // Light Header backgrop
elevation: 0,
centerTitle: true,
bottom: PreferredSize(
preferredSize: const Size.fromHeight(1.0),
child: Container(
color: Colors.black.withOpacity(0.05),
height: 1.0,
),
),
),
),
),
),
body: Container(
decoration: const BoxDecoration(
gradient: RadialGradient(
center: Alignment.topCenter,
radius: 1.5,
colors: [
Colors.white,
Color(0xFFF8FAFC), // Slate 50
],
)
),
child: SafeArea(
bottom: false,
child: Column(
children: [
Expanded(
child: ListView.builder(
controller: _scrollController,
padding: const EdgeInsets.symmetric(vertical: 20.0),
reverse: true,
itemCount: _messages.length + (_isTyping ? 1 : 0),
itemBuilder: (_, int index) {
if (_isTyping && index == 0) {
return _buildTypingIndicator();
}
int messageIndex = _isTyping ? index - 1 : index;
return _buildMessage(_messages[messageIndex]);
},
),
),
_buildTextComposer(),
],
),
),
),
);
}
}

29
lib/main.dart Normal file
View File

@@ -0,0 +1,29 @@
import 'package:flutter/material.dart';
import 'chat_screen.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'EPVChat! Clone',
theme: ThemeData(
brightness: Brightness.light,
scaffoldBackgroundColor: Colors.white,
colorScheme: const ColorScheme.light(
primary: Color(0xFF8ad5c9), // Mint
secondary: Color(0xFF57a7ed), // EPVC Light Blue
surface: Colors.white,
),
useMaterial3: true,
),
home: const ChatScreen(),
debugShowCheckedModeBanner: false,
);
}
}