上一篇文章我們詳細(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());
最后我們來嘗試一下吧!
ok剩盒,我們是成功的!今天就到這里了慨蛙,我們明天見辽聊!
如果想繼續(xù)學(xué)習(xí)DartVM服務(wù)器開發(fā)纪挎,請關(guān)注我,學(xué)習(xí)更多騷操作跟匆!