Flutter 使用 websocket

37 min read

一、 Flutter 中的 Websocket

Flutter 提供了 web_socket_channel 这个包来处理 WebSocket 消息监听和发送

使用 web_socket_channel 引入如下包即可:

import 'package:web_socket_channel/io.dart';

创建 WebScoketChannel 实例可以使用上面包提供的 IOWebSocketChannel.connect 连接到一个 websocket 服务

  IOWebSocketChannel _channel = IOWebSocketChannel.connect("ws://echo.websocket.org");

connect() 方法接收 url 作为参数,除此之外还支持传入 protocal 和 header 等

  factory IOWebSocketChannel.connect(url,
      {Iterable<String> protocols,
      Map<String, dynamic> headers,
      Duration pingInterval})

二、数据监听和数据发送

1、监听

监听 websocket 服务的消息基于 Stream.listen 方法

  StreamSubscription<T> listen(void onData(T event),
      {Function onError, void onDone(), bool cancelOnError});

上面创建好的 _channel 可以监听服务端发送过来的 message

    // 监听消息
    _channel.stream.listen((message) {
      print(message);
    });

2、发送

websocket 本身就是双向通信的,如果要发送给服务端,借助的则是 WebSocketSink.add 的能力

  void add(T data) {
    _sink.add(data);
  }

因此使用上面的 _channel 发送数据可以如下:

  void _sendHandle() {
    if (_message.isNotEmpty) {
      _channel.sink.add(_message);
    }
  }

3、关闭链接

Widget 生命周期中,需要将 socketChannel 关闭,通过 WebSocketSink.close()

 _channel.sink.close();

三、websocket 实践

  • statefulWidget 通过一个 list 存储数据
  • 连接 websocket ,这里使用 ws://echo.websocket.org
  • 监听数据传入
  • 通过 文本框 向 服务端推送数据
class WebSocketDemo extends StatefulWidget {
  WebSocketDemo({Key key}) : super(key: key);

  _WebSocketDemoState createState() => _WebSocketDemoState();
}

class _WebSocketDemoState extends State<WebSocketDemo> {
  List _list = new List();
  String _message;
  IOWebSocketChannel _channel =
  IOWebSocketChannel.connect("ws://echo.websocket.org");

  void _onChangedHandle(value) {
    setState(() {
      _message = value.toString();
    });
  }

  _WebSocketDemoState() {
    print(_channel);
  }
  @override
  void initState() {
    super.initState();
    setState(() {
      _list.add('[Info] Connected Successed!');
    });

    // 监听消息
    _channel.stream.listen((message) {
      print(message);
      setState(() {
        _list.add('[Received] ${message.toString()}');
      });
    });
  }

  void _sendHandle() {
    if (_message.isNotEmpty) {
      _list.add('[Sended] $_message');
      _channel.sink.add(_message);
    }
  }

  Widget _generatorForm() {
    return Column(
      children: <Widget>[
        TextField(onChanged: _onChangedHandle),
        SizedBox(height: 10),
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: <Widget>[
            RaisedButton(
              child: Text('Send'),
              onPressed: _sendHandle,
            )
          ],
        )
      ],
    );
  }

  List<Widget> _generatorList() {
    List<Widget> tmpList = _list.map((item) => ListItem(msg: item)).toList();
    List<Widget> prefix = [_generatorForm()];
    prefix.addAll(tmpList);
    return prefix;
  }

  @override
  Widget build(BuildContext context) {
    return ListView(
      padding: EdgeInsets.all(10),
      children: _generatorList(),
    );
  }

  @override
  void dispose() {
    super.dispose();
    _channel.sink.close();
  }
}

class ListItem extends StatelessWidget {
  final String msg;
  ListItem({Key key, this.msg}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Text(msg);
  }
}