import 'package:flutter/material.dart'; import 'dart:io'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( brightness: Brightness.light, ), darkTheme: ThemeData( brightness: Brightness.dark, ), themeMode: ThemeMode.dark, home: const MyHomePage(title: 'Socket Demo'), ); } } class MyHomePage extends StatefulWidget { const MyHomePage({super.key, required this.title}); final String title; @override State createState() => _MyHomePageState(); } class _MyHomePageState extends State { late Socket _socket; final TextEditingController _inputController = TextEditingController(); final List _msgs = []; String address = ""; @override void initState() { super.initState(); _connect(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( backgroundColor: Colors.purple, title: Text(widget.title), ), body: Column(children: [ Expanded( child: ListView.builder( padding: EdgeInsets.all(10.0), itemCount: _msgs.length, itemBuilder: (context, index) { var msg = _msgs[index]; String user = ""; String data = ""; int i; for (i = 0; i < msg.length; i++) { if (msg[i] == '=') { i++; break; } user += msg[i]; } for (; i < msg.length; i++) { data += msg[i]; } if (user == address) { return BubbleSend(message: data, user: user); } else { return BubbleReceive(message: data, user: user); } }, )), Padding( padding: const EdgeInsets.all(8.0), child: Row( children: [ Expanded( child: TextField( controller: _inputController, onSubmitted: (value) { _send(); }, decoration: const InputDecoration( border: OutlineInputBorder(), labelText: 'Enter message', ), ), ), IconButton( icon: Icon(Icons.send), onPressed: () { _send(); }, ), ], ), ), ]), ); } void _connect() async { try { _socket = await Socket.connect("127.0.0.1", 7654); address = "${_socket.address.address}:${_socket.port}"; print("my address: $address"); toast("Connected to 127.0.0.1:7654", 0); _socket.listen( (data) { final message = String.fromCharCodes(data).trim(); print('received: $message'); if (message[0] == '!') { String name = ""; for (int i = 1; i < message.length; i++) { name += message[i]; } address = name; } else { setState(() { _msgs.addAll(message.split('\n')); }); // print("_msgs: $_msgs"); } }, onDone: () { print('Server disconnected.'); toast('Server disconnected', 1); _socket.destroy(); }, onError: (error) { print('Error: $error'); toast('Error: $error', 1); _socket.destroy(); }, ); } catch (e) { print('Error: $e'); toast('An unexpected error occurred: $e', 1); } } void _send() { final value = _inputController.text; if (value.isNotEmpty) { _socket.write('$value\n'); _inputController.clear(); } } void toast(String msg, int type) { Color bg = Color(0x7aa2f7ff); Color fg = Colors.black; switch (type) { case 0: bg = Colors.green; break; case 1: bg = Colors.red; break; } ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Text(msg, style: TextStyle(color: fg)), backgroundColor: bg, )); } } class BubbleReceive extends StatelessWidget { final String message; final String user; const BubbleReceive({super.key, required this.message, required this.user}); @override Widget build(BuildContext context) { return Column( mainAxisAlignment: MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end, children: [ Row( mainAxisAlignment: MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end, children: [ Flexible( child: Container( padding: EdgeInsets.all(15), decoration: BoxDecoration( color: Colors.blueGrey, borderRadius: BorderRadius.circular(15), ), child: Text( message, style: const TextStyle(color: Colors.white, fontSize: 15), ), ), ), SizedBox( width: 20, height: 20, child: CustomPaint(painter: Triangle(false, Colors.blueGrey)), ), ], ), Text(user), SizedBox(height: 10), ]); } } class BubbleSend extends StatelessWidget { final String message; final String user; const BubbleSend({super.key, required this.message, required this.user}); @override Widget build(BuildContext context) { return Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.end, children: [ SizedBox( width: 20, height: 20, child: CustomPaint(painter: Triangle(true, Colors.indigo)), ), Container( padding: EdgeInsets.all(15), decoration: BoxDecoration( color: Colors.indigo, borderRadius: BorderRadius.circular(15), ), child: Text( message, style: const TextStyle(color: Colors.white, fontSize: 15), ), ), ], ), Text(user), SizedBox(height: 10), ]); } } class Triangle extends CustomPainter { final Color backgroundColor; final bool send; Triangle(this.send, this.backgroundColor); @override void paint(Canvas canvas, Size size) { var paint = Paint()..color = backgroundColor; var path = Path(); if (send) { path.moveTo(20, 0); path.lineTo(5, 15); path.lineTo(35, 15); } else { path.lineTo(15, 15); path.lineTo(-15, 15); } path.close(); canvas.drawPath(path, paint); } @override bool shouldRepaint(CustomPainter oldDelegate) { return false; } }