DartVM服務(wù)器開發(fā)(第十七天)--Jaguar_websocket結(jié)合Flutter搭建簡單聊天室

上一篇文章我們詳細(xì)說明了如何使用JWT,今天告喊,我們來使用websocket做一個簡單的聊天室颁股!那就開始吧嫁审!

1.定義消息

在開始建立webSocket之前狗准,我們需要定義消息克锣,如:發(fā)送人,發(fā)送時間腔长,發(fā)送人id等..

import 'dart:convert';
class ChatMessageData {
  final String id;
  final String msg;
  final DateTime created;
  final String name;
  final int role;
  ChatMessageData(
    this.id,
    this.msg,
    this.name,
    this.role,
    this.created,
  );
  static ChatMessageData formMap(Map map) => ChatMessageData(
      map['id'],
      map['msg'],
      map['name'],
      map['role'],
      DateTime.fromMicrosecondsSinceEpoch(map['created']));

  Map toMap() => {
        "id": id,
        "msg": msg,
        "name": name,
        "role":role,
        "created": created.millisecondsSinceEpoch
      };
  String toJson() => jsonEncode(toMap());
  @override
  String toString() => toMap().toString();
}

我們這里定義了一個ChatMessageData袭祟,如果你想需要更多字段,可以再添加

2.添加消息訂閱

//控制消息的發(fā)送
final pub = StreamController<ChatMessageData>();
//當(dāng)pub調(diào)用add(data)方法捞附,該sub的listen會監(jiān)聽到
final Stream<ChatMessageData> sub = pub.stream.asBroadcastStream();

3. 定義接口

這里我們定義兩個接口巾乳,一個用于連接的接口,一個用于發(fā)送消息的接口

  • /mini/login 提交用戶的信息鸟召,如果不正確胆绊,返回相關(guān)的信息,不給連接
  • /min/connect 連接websocket,該接口獲取到websocket對象欧募,然后可以使用該對象進(jìn)行發(fā)送消息
    登陸接口
..post('/mini/login', (ctx) async{
      User user=await ctx.bodyAsJson(convert: User.forMap);
      String username = user.username;
      String password = user.password;
      if (username.isEmpty || password.isEmpty) {
        return Response.json(apiJson.errorMsgA(-1, '用戶名或密碼為空压状!').toMap());
      } else {
        User user = await userBean.findOneWhere(userBean.username.eq(username));
        if (user == null || user.password != password) {
          return Response.json(apiJson.errorMsgA(-2, '用戶名或密碼不正確!').toMap());
        } else {
          print('用戶:$username登陸成功');
          return Response.json(apiJson.successA().toMap());
        }
      }
    })

連接接口

..ws(
      '/mini/connect',
      onConnect: (ctx, ws) {
        var subscription = sub.listen((ChatMessageData data) {
          print(data.toJson());
          ws.add(data.toJson());
        });
        ws.done.then((_) {
          print('用戶已退出聊天房');
          subscription.cancel();
        });
        //連接上之后返回一條信息
        ws.add(new ChatMessageData('1', '歡迎登陸', '服務(wù)器', 1, DateTime.now()).toJson());
      },
      handler: (data) {
        //獲取用戶發(fā)送的消息
        ChatMessageData msg=ChatMessageData.formMap(json.decode(data));
        print(msg.toJson());
        //廣播一條消息
        pub.add(msg);
      },
    )

ok槽片,我們已經(jīng)搭建好一個簡單的聊天接口了,下面肢础,我們使用Flutter簡單的編輯一下客戶端平臺

4.Flutter建立一個簡單的聊天室

這部分代碼為Flutter下还栓,可簡單的編輯一個聊天室

mport 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(new FriendlychatApp());
}

final ThemeData kIOSTheme = new ThemeData(
  primarySwatch: Colors.orange,
  primaryColor: Colors.grey[100],
  primaryColorBrightness: Brightness.light,
);

final ThemeData kDefaultTheme = new ThemeData(
  primarySwatch: Colors.purple,
  accentColor: Colors.orangeAccent[400],
);

const String _name = "Your Name";

class FriendlychatApp extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
    return new MaterialApp(
      title: "Friendlychat",
      theme: defaultTargetPlatform == TargetPlatform.iOS
        ? kIOSTheme
        : kDefaultTheme,
      home: new ChatScreen(),
    );
  }
}

class ChatMessage extends StatelessWidget {
  ChatMessage({this.text, this.animationController});
  final String text;
  final AnimationController animationController;
  @override
  Widget build(BuildContext context) {
    return new SizeTransition(
      sizeFactor: new CurvedAnimation(
        parent: animationController,
        curve: Curves.easeOut
      ),
      axisAlignment: 0.0,
      child: new Container(
        margin: const EdgeInsets.symmetric(vertical: 10.0),
        child: new Row(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            new Container(
              margin: const EdgeInsets.only(right: 16.0),
              child: new CircleAvatar(child: new Text(_name[0])),
            ),
            new Expanded(
              child: new Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: <Widget>[
                  new Text(_name, style: Theme.of(context).textTheme.subhead),
                  new Container(
                    margin: const EdgeInsets.only(top: 5.0),
                    child: new Text(text),
                  ),
                ],
              ),
            ),
          ],
        ),
      )
    );
  }
}

class ChatScreen extends StatefulWidget {
  @override
  State createState() => new ChatScreenState();
}

class ChatScreenState extends State<ChatScreen> with TickerProviderStateMixin {
  final List<ChatMessage> _messages = <ChatMessage>[];
  final TextEditingController _textController = new TextEditingController();
  bool _isComposing = false;

  void _handleSubmitted(String text) {
    _textController.clear();
    setState(() {
      _isComposing = false;
    });
    ChatMessage message = new ChatMessage(
      text: text,
      animationController: new AnimationController(
        duration: new Duration(milliseconds: 700),
        vsync: this,
      ),
    );
    setState(() {
      _messages.insert(0, message);
    });
    message.animationController.forward();
  }

  void dispose() {
    for (ChatMessage message in _messages)
      message.animationController.dispose();
    super.dispose();
  }

   Widget _buildTextComposer() {
    return new IconTheme(
      data: new IconThemeData(color: Theme.of(context).accentColor),
      child: new Container(
          margin: const EdgeInsets.symmetric(horizontal: 8.0),
          child: new Row(children: <Widget>[
            new Flexible(
              child: new TextField(
                controller: _textController,
                onChanged: (String text) {
                  setState(() {
                    _isComposing = text.length > 0;
                  });
                },
                onSubmitted: _handleSubmitted,
                decoration:
                    new InputDecoration.collapsed(hintText: "Send a message"),
              ),
            ),
            new Container(
                margin: new EdgeInsets.symmetric(horizontal: 4.0),
                child: Theme.of(context).platform == TargetPlatform.iOS
                    ? new CupertinoButton(
                        child: new Text("Send"),
                        onPressed: _isComposing
                            ? () => _handleSubmitted(_textController.text)
                            : null,
                      )
                    : new IconButton(
                        icon: new Icon(Icons.send),
                        onPressed: _isComposing
                            ? () => _handleSubmitted(_textController.text)
                            : null,
                      )),
          ]),
          decoration: Theme.of(context).platform == TargetPlatform.iOS
              ? new BoxDecoration(
                  border:
                      new Border(top: new BorderSide(color: Colors.grey[200])))
              : null),
    );
  }

  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Friendlychat"),
        elevation:
            Theme.of(context).platform == TargetPlatform.iOS ? 0.0 : 4.0
      ),
      body: new Container(
        child: new Column(
          children: <Widget>[
          new Flexible(
            child: new ListView.builder(
              padding: new EdgeInsets.all(8.0),
              reverse: true,
              itemBuilder: (_, int index) => _messages[index],
              itemCount: _messages.length,
            )
          ),
          new Divider(height: 1.0),
          new Container(
            decoration: new BoxDecoration(
              color: Theme.of(context).cardColor),
            child: _buildTextComposer(),
          ),
         ]
       ),
       decoration: Theme.of(context).platform == TargetPlatform.iOS ? new BoxDecoration(border: new Border(top: new BorderSide(color: Colors.grey[200]))) : null),//new
   );
  }
}

上面就是簡單的聊天界面,我們還有主要跟服務(wù)器交互的方法

WebSocket socket;
void login() {
    httpManager.post(
        url: 'http://192.168.1.101:8080/mini/login',
        body: json.encode({
          "username": "rhyme",
          "password": "123456",
        }),
        onSend: () {
//key為scaffold的key
          scaffoldKey?.currentState
              ?.showSnackBar(new SnackBar(content: Text('發(fā)送請求传轰,連接服務(wù)器')));
        },
        onSuccess: (data) {
          WebSocket.connect('ws://192.168.1.101:8080/mini/connect')
              .then((socket) {
            this.socket = socket;
            socket.listen((data) {
//該方法接收服務(wù)器信息
              print(data);
              Map map = json.decode(data);
              ChatMessageData msg=ChatMessageData.formMap(map);
              if(msg.id!=widget.user.uuid){
                _handleGetMessage(msg);
              }
            });
            socket.done.then((e){
//當(dāng)與服務(wù)器連接中斷調(diào)用
              scaffoldKey.currentState.showSnackBar(new SnackBar(content: Text('連接服務(wù)器中斷!')));
            });
          });
        },
        onError: (error) {
          print(error);
          scaffoldKey.currentState.showSnackBar(
              new SnackBar(content: Text('連接失敗!${error.toString()}')));
        });
  }

我們發(fā)送消息給服務(wù)端

    socket.add(new ChatMessageData(widget.user.uuid, value, widget.user.userName, widget.user.role, DateTime.now()).toJson());

最后我們來嘗試一下吧!


image.png

image.png

ok剩盒,我們是成功的!今天就到這里了慨蛙,我們明天見辽聊!

如果想繼續(xù)學(xué)習(xí)DartVM服務(wù)器開發(fā)纪挎,請關(guān)注我,學(xué)習(xí)更多騷操作跟匆!

下一篇:DartVM服務(wù)器開發(fā)(第十八天)--項目部署到云服務(wù)器

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末异袄,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子玛臂,更是在濱河造成了極大的恐慌烤蜕,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件迹冤,死亡現(xiàn)場離奇詭異讽营,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)泡徙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進(jìn)店門橱鹏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人堪藐,你說我怎么就攤上這事莉兰。” “怎么了庶橱?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵贮勃,是天一觀的道長。 經(jīng)常有香客問我苏章,道長寂嘉,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任枫绅,我火速辦了婚禮泉孩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘并淋。我一直安慰自己寓搬,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布县耽。 她就那樣靜靜地躺著句喷,像睡著了一般。 火紅的嫁衣襯著肌膚如雪兔毙。 梳的紋絲不亂的頭發(fā)上唾琼,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天,我揣著相機(jī)與錄音澎剥,去河邊找鬼锡溯。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的祭饭。 我是一名探鬼主播芜茵,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼倡蝙!你這毒婦竟也來了九串?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤悠咱,失蹤者是張志新(化名)和其女友劉穎蒸辆,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體析既,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡躬贡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了眼坏。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拂玻。...
    茶點(diǎn)故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖宰译,靈堂內(nèi)的尸體忽然破棺而出檐蚜,到底是詐尸還是另有隱情,我是刑警寧澤沿侈,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布闯第,位于F島的核電站焕梅,受9級特大地震影響零聚,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜砰粹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一蛛淋、第九天 我趴在偏房一處隱蔽的房頂上張望咙好。 院中可真熱鬧,春花似錦褐荷、人聲如沸勾效。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽层宫。三九已至,卻和暖如春其监,著一層夾襖步出監(jiān)牢的瞬間萌腿,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工棠赛, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留哮奇,地道東北人。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓睛约,卻偏偏與公主長得像鼎俘,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子辩涝,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評論 2 345

推薦閱讀更多精彩內(nèi)容

  • Spring Web MVC Spring Web MVC 是包含在 Spring 框架中的 Web 框架贸伐,建立于...
    Hsinwong閱讀 22,313評論 1 92
  • 點(diǎn)擊查看原文 Web SDK 開發(fā)手冊 SDK 概述 網(wǎng)易云信 SDK 為 Web 應(yīng)用提供一個完善的 IM 系統(tǒng)...
    layjoy閱讀 13,675評論 0 15
  • 我想先給老師說聲對不起,最近思考了很多怔揩,認(rèn)識到了先前的問題捉邢,即使大家不記得了,我還是為我的不恰當(dāng)發(fā)言感到愧疚商膊。從小...
    Aries是你閱讀 151評論 2 1
  • 世界再大伏伐,大不過一顆心,走的再遠(yuǎn)晕拆,遠(yuǎn)不過一場夢藐翎。 ???The world is big, big but a h...
    Tibetan隨緣閱讀 236評論 0 0
  • 朋友問我:“你都結(jié)婚怎么不發(fā)朋友圈曬照片呀吝镣?”我只幽幽的說了一句:“我沒有虐狗的習(xí)慣±ケ樱”說完瞬間感覺脊背一陣...
    如果再見親愛的卡七閱讀 364評論 0 1