rajath_pai

ChatPage

Aug 4th, 2021
683
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import 'dart:async';
  2. import 'dart:convert';
  3. import 'dart:typed_data';
  4.  
  5. import 'package:flutter/material.dart';
  6. import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart';
  7.  
  8. class ChatPage extends StatefulWidget {
  9.   final BluetoothDevice server;
  10.  
  11.   const ChatPage({required this.server});
  12.  
  13.   @override
  14.   _ChatPage createState() => new _ChatPage();
  15. }
  16.  
  17. class _Message {
  18.   int whom;
  19.   String text;
  20.  
  21.   _Message(this.whom, this.text);
  22. }
  23.  
  24. class _ChatPage extends State<ChatPage> {
  25.   static final clientID = 0;
  26.   BluetoothConnection? connection;
  27.  
  28.   List<_Message> messages = List<_Message>.empty(growable: true);
  29.   String _messageBuffer = '';
  30.  
  31.   final TextEditingController textEditingController =
  32.       new TextEditingController();
  33.   final ScrollController listScrollController = new ScrollController();
  34.  
  35.   bool isConnecting = true;
  36.   bool get isConnected => (connection?.isConnected ?? false);
  37.  
  38.   bool isDisconnecting = false;
  39.  
  40.   @override
  41.   void initState() {
  42.     super.initState();
  43.  
  44.     BluetoothConnection.toAddress(widget.server.address).then((_connection) {
  45.       print('Connected to the device');
  46.       connection = _connection;
  47.       setState(() {
  48.         isConnecting = false;
  49.         isDisconnecting = false;
  50.       });
  51.  
  52.       connection!.input!.listen(_onDataReceived).onDone(() {
  53.         // Example: Detect which side closed the connection
  54.         // There should be `isDisconnecting` flag to show are we are (locally)
  55.         // in middle of disconnecting process, should be set before calling
  56.         // `dispose`, `finish` or `close`, which all causes to disconnect.
  57.         // If we except the disconnection, `onDone` should be fired as result.
  58.         // If we didn't except this (no flag set), it means closing by remote.
  59.         if (isDisconnecting) {
  60.           print('Disconnecting locally!');
  61.         } else {
  62.           print('Disconnected remotely!');
  63.         }
  64.         if (this.mounted) {
  65.           setState(() {});
  66.         }
  67.       });
  68.     }).catchError((error) {
  69.       print('Cannot connect, exception occured');
  70.       print(error);
  71.     });
  72.   }
  73.  
  74.   @override
  75.   void dispose() {
  76.     // Avoid memory leak (`setState` after dispose) and disconnect
  77.     if (isConnected) {
  78.       isDisconnecting = true;
  79.       connection?.dispose();
  80.       connection = null;
  81.     }
  82.  
  83.     super.dispose();
  84.   }
  85.  
  86.   @override
  87.   Widget build(BuildContext context) {
  88.     final List<Row> list = messages.map((_message) {
  89.       return Row(
  90.         children: <Widget>[
  91.           Container(
  92.             child: Text(
  93.                 (text) {
  94.                   return text == '/shrug' ? \\_(ツ)_/¯' : text;
  95.                 }(_message.text.trim()),
  96.                 style: TextStyle(color: Colors.white)),
  97.             padding: EdgeInsets.all(12.0),
  98.             margin: EdgeInsets.only(bottom: 8.0, left: 8.0, right: 8.0),
  99.             width: 222.0,
  100.             decoration: BoxDecoration(
  101.                 color:
  102.                     _message.whom == clientID ? Colors.blueAccent : Colors.grey,
  103.                 borderRadius: BorderRadius.circular(7.0)),
  104.           ),
  105.         ],
  106.         mainAxisAlignment: _message.whom == clientID
  107.             ? MainAxisAlignment.end
  108.             : MainAxisAlignment.start,
  109.       );
  110.     }).toList();
  111.  
  112.     final serverName = widget.server.name ?? "Unknown";
  113.     return Scaffold(
  114.       appBar: AppBar(
  115.           title: (isConnecting
  116.               ? Text('Connecting chat to ' + serverName + '...')
  117.               : isConnected
  118.                   ? Text('Live chat with ' + serverName)
  119.                   : Text('Chat log with ' + serverName))),
  120.       body: SafeArea(
  121.         child: Column(
  122.           children: <Widget>[
  123.             Flexible(
  124.               child: ListView(
  125.                   padding: const EdgeInsets.all(12.0),
  126.                   controller: listScrollController,
  127.                   children: list),
  128.             ),
  129.             Row(
  130.               children: <Widget>[
  131.                 Flexible(
  132.                   child: Container(
  133.                     margin: const EdgeInsets.only(left: 16.0),
  134.                     child: TextField(
  135.                       style: const TextStyle(fontSize: 15.0),
  136.                       controller: textEditingController,
  137.                       decoration: InputDecoration.collapsed(
  138.                         hintText: isConnecting
  139.                             ? 'Wait until connected...'
  140.                             : isConnected
  141.                                 ? 'Type your message...'
  142.                                 : 'Chat got disconnected',
  143.                         hintStyle: const TextStyle(color: Colors.grey),
  144.                       ),
  145.                       enabled: isConnected,
  146.                     ),
  147.                   ),
  148.                 ),
  149.                 Container(
  150.                   margin: const EdgeInsets.all(8.0),
  151.                   child: IconButton(
  152.                       icon: const Icon(Icons.send),
  153.                       onPressed: isConnected
  154.                           ? () => _sendMessage(textEditingController.text)
  155.                           : null),
  156.                 ),
  157.               ],
  158.             )
  159.           ],
  160.         ),
  161.       ),
  162.     );
  163.   }
  164.  
  165.   void _onDataReceived(Uint8List data) {
  166.     // Allocate buffer for parsed data
  167.     int backspacesCounter = 0;
  168.     data.forEach((byte) {
  169.       if (byte == 8 || byte == 127) {
  170.         backspacesCounter++;
  171.       }
  172.     });
  173.     Uint8List buffer = Uint8List(data.length - backspacesCounter);
  174.     int bufferIndex = buffer.length;
  175.  
  176.     // Apply backspace control character
  177.     backspacesCounter = 0;
  178.     for (int i = data.length - 1; i >= 0; i--) {
  179.       if (data[i] == 8 || data[i] == 127) {
  180.         backspacesCounter++;
  181.       } else {
  182.         if (backspacesCounter > 0) {
  183.           backspacesCounter--;
  184.         } else {
  185.           buffer[--bufferIndex] = data[i];
  186.         }
  187.       }
  188.     }
  189.  
  190.     // Create message if there is new line character
  191.     String dataString = String.fromCharCodes(buffer);
  192.     int index = buffer.indexOf(13);
  193.     if (~index != 0) {
  194.       setState(() {
  195.         messages.add(
  196.           _Message(
  197.             1,
  198.             backspacesCounter > 0
  199.                 ? _messageBuffer.substring(
  200.                     0, _messageBuffer.length - backspacesCounter)
  201.                 : _messageBuffer + dataString.substring(0, index),
  202.           ),
  203.         );
  204.         _messageBuffer = dataString.substring(index);
  205.       });
  206.     } else {
  207.       _messageBuffer = (backspacesCounter > 0
  208.           ? _messageBuffer.substring(
  209.               0, _messageBuffer.length - backspacesCounter)
  210.           : _messageBuffer + dataString);
  211.     }
  212.   }
  213.  
  214.   void _sendMessage(String text) async {
  215.     text = text.trim();
  216.     textEditingController.clear();
  217.  
  218.     if (text.length > 0) {
  219.       try {
  220.         connection!.output.add(Uint8List.fromList(utf8.encode(text + "\r\n")));
  221.         await connection!.output.allSent;
  222.  
  223.         setState(() {
  224.           messages.add(_Message(clientID, text));
  225.         });
  226.  
  227.         Future.delayed(Duration(milliseconds: 333)).then((_) {
  228.           listScrollController.animateTo(
  229.               listScrollController.position.maxScrollExtent,
  230.               duration: Duration(milliseconds: 333),
  231.               curve: Curves.easeOut);
  232.         });
  233.       } catch (e) {
  234.         // Ignore error, but notify state
  235.         setState(() {});
  236.       }
  237.     }
  238.   }
  239. }
RAW Paste Data