diff options
| author | BoredGuy <osome3717@gmail.com> | 2026-05-09 19:59:04 -0700 |
|---|---|---|
| committer | BoredGuy <osome3717@gmail.com> | 2026-05-09 19:59:04 -0700 |
| commit | 67427aec40ba094f1e7f3ee77eab09df73d752f1 (patch) | |
| tree | b1cbaff8f809f5b003977279b93a6bae262e5b0b /lib | |
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/main.dart | 34 | ||||
| -rw-r--r-- | lib/screens/chat_list_screen.dart | 152 | ||||
| -rw-r--r-- | lib/screens/chat_screen.dart | 176 | ||||
| -rw-r--r-- | lib/screens/login_screen.dart | 97 | ||||
| -rw-r--r-- | lib/screens/signup_screen.dart | 89 |
5 files changed, 548 insertions, 0 deletions
diff --git a/lib/main.dart b/lib/main.dart new file mode 100644 index 0000000..2831b49 --- /dev/null +++ b/lib/main.dart @@ -0,0 +1,34 @@ +import 'package:flutter/material.dart'; +import 'screens/login_screen.dart'; + +void main() { + runApp(const MessengerApp()); +} + +class MessengerApp extends StatelessWidget { + const MessengerApp({super.key}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Messenger Clone', + debugShowCheckedModeBanner: false, + theme: ThemeData( + colorScheme: ColorScheme.fromSeed( + seedColor: const Color(0xFF2AABEE), // Messenger light blue + brightness: Brightness.light, + ), + useMaterial3: true, + ), + darkTheme: ThemeData( + colorScheme: ColorScheme.fromSeed( + seedColor: const Color(0xFF2AABEE), + brightness: Brightness.dark, + ), + useMaterial3: true, + ), + themeMode: ThemeMode.system, + home: const LoginScreen(), + ); + } +} diff --git a/lib/screens/chat_list_screen.dart b/lib/screens/chat_list_screen.dart new file mode 100644 index 0000000..f3e5d13 --- /dev/null +++ b/lib/screens/chat_list_screen.dart @@ -0,0 +1,152 @@ +import 'package:flutter/material.dart'; +import 'chat_screen.dart'; +import 'login_screen.dart'; + +class ChatListScreen extends StatelessWidget { + const ChatListScreen({super.key}); + + @override + Widget build(BuildContext context) { + final chats = [ + {'name': 'Alice Smith', 'msg': 'Hey, how are you?', 'time': '10:30 AM', 'unread': 2, 'color': Colors.red}, + {'name': 'Bob Johnson', 'msg': 'Are we still on for tomorrow?', 'time': '9:45 AM', 'unread': 0, 'color': Colors.blue}, + {'name': 'Charlie Brown', 'msg': 'I sent you the files.', 'time': 'Yesterday', 'unread': 0, 'color': Colors.green}, + {'name': 'Design Team', 'msg': 'Dave: The new mocks look great.', 'time': 'Yesterday', 'unread': 5, 'color': Colors.purple}, + {'name': 'Eve', 'msg': 'Ok.', 'time': 'Tuesday', 'unread': 0, 'color': Colors.orange}, + ]; + + return Scaffold( + appBar: AppBar( + title: const Text('Messenger'), + backgroundColor: const Color(0xFF2AABEE), + foregroundColor: Colors.white, + actions: [ + IconButton(icon: const Icon(Icons.search), onPressed: () {}), + ], + ), + drawer: Drawer( + child: ListView( + padding: EdgeInsets.zero, + children: [ + const UserAccountsDrawerHeader( + accountName: Text('John Doe', style: TextStyle(fontWeight: FontWeight.bold)), + accountEmail: Text('johndoe@example.com'), + currentAccountPicture: CircleAvatar( + backgroundColor: Colors.white, + child: Text('J', style: TextStyle(fontSize: 24, color: Color(0xFF2AABEE))), + ), + decoration: BoxDecoration(color: Color(0xFF2AABEE)), + ), + ListTile( + leading: const Icon(Icons.group), + title: const Text('New Group'), + onTap: () {}, + ), + ListTile( + leading: const Icon(Icons.person), + title: const Text('Contacts'), + onTap: () {}, + ), + ListTile( + leading: const Icon(Icons.bookmark_border), + title: const Text('Saved Messages'), + onTap: () {}, + ), + ListTile( + leading: const Icon(Icons.settings), + title: const Text('Settings'), + onTap: () {}, + ), + const Divider(), + ListTile( + leading: const Icon(Icons.person_add), + title: const Text('Invite Friends'), + onTap: () {}, + ), + ListTile( + leading: const Icon(Icons.help_outline), + title: const Text('Messenger Features'), + onTap: () {}, + ), + const Divider(), + ListTile( + leading: const Icon(Icons.logout, color: Colors.red), + title: const Text('Log out', style: TextStyle(color: Colors.red)), + onTap: () { + Navigator.pushReplacement( + context, + MaterialPageRoute(builder: (context) => const LoginScreen()), + ); + }, + ), + ], + ), + ), + body: ListView.separated( + itemCount: chats.length, + separatorBuilder: (context, index) => const Divider(height: 1, indent: 72), + itemBuilder: (context, index) { + final chat = chats[index]; + return ListTile( + leading: CircleAvatar( + backgroundColor: chat['color'] as Color, + radius: 26, + child: Text( + (chat['name'] as String)[0], + style: const TextStyle(color: Colors.white, fontSize: 20), + ), + ), + title: Text( + chat['name'] as String, + style: const TextStyle(fontWeight: FontWeight.bold), + ), + subtitle: Text( + chat['msg'] as String, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + trailing: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text( + chat['time'] as String, + style: const TextStyle(color: Colors.grey, fontSize: 12), + ), + const SizedBox(height: 4), + if ((chat['unread'] as int) > 0) + Container( + padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), + decoration: BoxDecoration( + color: const Color(0xFF2AABEE), + borderRadius: BorderRadius.circular(10), + ), + child: Text( + '${chat['unread']}', + style: const TextStyle(color: Colors.white, fontSize: 12, fontWeight: FontWeight.bold), + ), + ) + else + const SizedBox(height: 16), + ], + ), + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ChatScreen(name: chat['name'] as String), + ), + ); + }, + ); + }, + ), + floatingActionButton: FloatingActionButton( + onPressed: () {}, + backgroundColor: const Color(0xFF2AABEE), + foregroundColor: Colors.white, + child: const Icon(Icons.edit), + ), + ); + } +} diff --git a/lib/screens/chat_screen.dart b/lib/screens/chat_screen.dart new file mode 100644 index 0000000..35ea4a2 --- /dev/null +++ b/lib/screens/chat_screen.dart @@ -0,0 +1,176 @@ +import 'package:flutter/material.dart'; + +class ChatScreen extends StatefulWidget { + final String name; + + const ChatScreen({super.key, required this.name}); + + @override + State<ChatScreen> createState() => _ChatScreenState(); +} + +class _ChatScreenState extends State<ChatScreen> { + final _messageController = TextEditingController(); + final List<Map<String, dynamic>> _messages = [ + {'text': 'Hello!', 'isMe': false, 'time': '10:00 AM'}, + {'text': 'Hi, how are you?', 'isMe': true, 'time': '10:05 AM'}, + {'text': 'I am good, thanks! Are we meeting later?', 'isMe': false, 'time': '10:06 AM'}, + ]; + + void _sendMessage() { + if (_messageController.text.trim().isEmpty) return; + + setState(() { + _messages.add({ + 'text': _messageController.text, + 'isMe': true, + 'time': 'Now', + }); + _messageController.clear(); + }); + } + + @override + Widget build(BuildContext context) { + final isDark = Theme.of(context).brightness == Brightness.dark; + + return Scaffold( + appBar: AppBar( + backgroundColor: const Color(0xFF2AABEE), + foregroundColor: Colors.white, + titleSpacing: 0, + title: Row( + children: [ + CircleAvatar( + radius: 18, + backgroundColor: Colors.white24, + child: Text(widget.name[0], style: const TextStyle(color: Colors.white, fontSize: 16)), + ), + const SizedBox(width: 12), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(widget.name, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), + const Text('last seen recently', style: TextStyle(fontSize: 12, color: Colors.white70)), + ], + ), + ], + ), + actions: [ + IconButton(icon: const Icon(Icons.call), onPressed: () {}), + IconButton(icon: const Icon(Icons.more_vert), onPressed: () {}), + ], + ), + body: Container( + color: isDark ? null : const Color(0xFFC8DCED), // Classic background color + child: Column( + children: [ + Expanded( + child: ListView.builder( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), + itemCount: _messages.length, + itemBuilder: (context, index) { + final msg = _messages[index]; + final isMe = msg['isMe'] as bool; + return Align( + alignment: isMe ? Alignment.centerRight : Alignment.centerLeft, + child: ConstrainedBox( + constraints: BoxConstraints( + maxWidth: MediaQuery.of(context).size.width * 0.75, + ), + child: Container( + margin: const EdgeInsets.only(bottom: 8), + padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8), + decoration: BoxDecoration( + color: isMe + ? (isDark ? const Color(0xFF2AABEE) : const Color(0xFFEEFFDE)) + : (isDark ? const Color(0xFF212D3B) : Colors.white), + borderRadius: BorderRadius.circular(16).copyWith( + bottomRight: isMe ? const Radius.circular(4) : const Radius.circular(16), + bottomLeft: !isMe ? const Radius.circular(4) : const Radius.circular(16), + ), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.05), + blurRadius: 2, + offset: const Offset(0, 1), + ), + ], + ), + child: Wrap( + crossAxisAlignment: WrapCrossAlignment.end, + alignment: WrapAlignment.end, + children: [ + Padding( + padding: const EdgeInsets.only(right: 8.0, bottom: 2.0), + child: Text( + msg['text'] as String, + style: TextStyle( + color: isDark + ? Colors.white + : (isMe ? Colors.black87 : Colors.black87), + fontSize: 16, + ), + ), + ), + Text( + msg['time'] as String, + style: TextStyle( + color: isDark + ? Colors.white54 + : (isMe ? const Color(0xFF55A65A) : Colors.grey), + fontSize: 11, + ), + ), + ], + ), + ), + ), + ); + }, + ), + ), + Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 8), + color: Theme.of(context).scaffoldBackgroundColor, + child: SafeArea( + child: Row( + children: [ + IconButton( + icon: const Icon(Icons.attach_file, color: Colors.grey), + onPressed: () {}, + ), + Expanded( + child: TextField( + controller: _messageController, + decoration: InputDecoration( + hintText: 'Message', + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(24), + borderSide: BorderSide.none, + ), + filled: true, + fillColor: isDark ? const Color(0xFF212D3B) : Colors.grey[200], + contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), + ), + onSubmitted: (_) => _sendMessage(), + ), + ), + const SizedBox(width: 8), + CircleAvatar( + backgroundColor: const Color(0xFF2AABEE), + child: IconButton( + icon: const Icon(Icons.send, color: Colors.white, size: 20), + onPressed: _sendMessage, + ), + ), + ], + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/screens/login_screen.dart b/lib/screens/login_screen.dart new file mode 100644 index 0000000..9716df0 --- /dev/null +++ b/lib/screens/login_screen.dart @@ -0,0 +1,97 @@ +import 'package:flutter/material.dart'; +import 'chat_list_screen.dart'; +import 'signup_screen.dart'; + +class LoginScreen extends StatefulWidget { + const LoginScreen({super.key}); + + @override + State<LoginScreen> createState() => _LoginScreenState(); +} + +class _LoginScreenState extends State<LoginScreen> { + final _emailController = TextEditingController(); + final _passwordController = TextEditingController(); + + void _login() { + // Navigate to chat list on successful login + Navigator.pushReplacement( + context, + MaterialPageRoute(builder: (context) => const ChatListScreen()), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Center( + child: SingleChildScrollView( + padding: const EdgeInsets.all(24.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const Icon( + Icons.chat_bubble_outline, + size: 100, + color: Color(0xFF2AABEE), + ), + const SizedBox(height: 32), + const Text( + 'Log in to Messenger', + style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold), + textAlign: TextAlign.center, + ), + const SizedBox(height: 8), + const Text( + 'Please enter your email and password.', + style: TextStyle(color: Colors.grey), + textAlign: TextAlign.center, + ), + const SizedBox(height: 32), + TextField( + controller: _emailController, + keyboardType: TextInputType.emailAddress, + decoration: const InputDecoration( + labelText: 'Email', + border: OutlineInputBorder(), + prefixIcon: Icon(Icons.email), + ), + ), + const SizedBox(height: 16), + TextField( + controller: _passwordController, + obscureText: true, + decoration: const InputDecoration( + labelText: 'Password', + border: OutlineInputBorder(), + prefixIcon: Icon(Icons.lock), + ), + ), + const SizedBox(height: 24), + ElevatedButton( + onPressed: _login, + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFF2AABEE), + foregroundColor: Colors.white, + padding: const EdgeInsets.symmetric(vertical: 16), + ), + child: const Text('Log In', style: TextStyle(fontSize: 16)), + ), + const SizedBox(height: 16), + TextButton( + onPressed: () { + Navigator.push( + context, + MaterialPageRoute(builder: (context) => const SignupScreen()), + ); + }, + child: const Text('Don\'t have an account? Sign up'), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/screens/signup_screen.dart b/lib/screens/signup_screen.dart new file mode 100644 index 0000000..1a0511d --- /dev/null +++ b/lib/screens/signup_screen.dart @@ -0,0 +1,89 @@ +import 'package:flutter/material.dart'; +import 'chat_list_screen.dart'; + +class SignupScreen extends StatefulWidget { + const SignupScreen({super.key}); + + @override + State<SignupScreen> createState() => _SignupScreenState(); +} + +class _SignupScreenState extends State<SignupScreen> { + final _nameController = TextEditingController(); + final _emailController = TextEditingController(); + final _passwordController = TextEditingController(); + + void _signup() { + Navigator.pushAndRemoveUntil( + context, + MaterialPageRoute(builder: (context) => const ChatListScreen()), + (route) => false, + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Sign Up'), + backgroundColor: Colors.transparent, + elevation: 0, + ), + body: Center( + child: SingleChildScrollView( + padding: const EdgeInsets.all(24.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const Icon( + Icons.chat_bubble_outline, + size: 80, + color: Color(0xFF2AABEE), + ), + const SizedBox(height: 32), + TextField( + controller: _nameController, + decoration: const InputDecoration( + labelText: 'Full Name', + border: OutlineInputBorder(), + prefixIcon: Icon(Icons.person), + ), + ), + const SizedBox(height: 16), + TextField( + controller: _emailController, + keyboardType: TextInputType.emailAddress, + decoration: const InputDecoration( + labelText: 'Email', + border: OutlineInputBorder(), + prefixIcon: Icon(Icons.email), + ), + ), + const SizedBox(height: 16), + TextField( + controller: _passwordController, + obscureText: true, + decoration: const InputDecoration( + labelText: 'Password', + border: OutlineInputBorder(), + prefixIcon: Icon(Icons.lock), + ), + ), + const SizedBox(height: 24), + ElevatedButton( + onPressed: _signup, + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFF2AABEE), + foregroundColor: Colors.white, + padding: const EdgeInsets.symmetric(vertical: 16), + ), + child: const Text('Sign Up', style: TextStyle(fontSize: 16)), + ), + ], + ), + ), + ), + ); + } +} |
