Как аутентифицировать веб-сокет во флаттере - PullRequest
0 голосов
/ 07 марта 2020

Я использую Traccar , но не смог использовать веб-сокет во Flutter, поскольку он не предоставляет никаких данных. Я думаю, что websocket требует аутентификации или токенов для получения данных.

class _HomeState extends State<Home> {
  IOWebSocketChannel channel = IOWebSocketChannel.connect("ws://167.172.215.197:8082/api/socket");


  @override
  Widget build(BuildContext context) {
    print(channel);
    return new Scaffold(
      resizeToAvoidBottomPadding: false,
      appBar: AppBar(
        title: Text('Map'),
      ),
      body: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[

          // details(),
          StreamBuilder(
            stream: channel.stream,
            builder: (context, snapshot) {
              print(snapshot);
              return Padding(
                padding: const EdgeInsets.symmetric(vertical: 24.0),
                child: Container(
                  child:
                      Column(
                        children: <Widget>[
                          Text(snapshot.hasData ? '${snapshot.data.positions}' : 'No Data'),


                        ],
                      ),

                ),
              );
            },
          )
        ],
      ),

    );
  }

}

Я хочу получать данные с сервера с помощью веб-сокета во флаттере.

1 Ответ

1 голос
/ 08 мая 2020

да, вам нужно проверить подлинность и использовать некоторые переменные, например:

String _cookie;
String _email = "your_email";
String _password = "your_password" ;
final _dio = Dio();
String _cookie;
String serverUrl = "your_server_ip";
StreamSubscription<dynamic> _rawPosSub;
final _devicesMap = <int, Device>{};
final _positions = StreamController<Device>.broadcast();

Future<void> _getConnection({String protocol = "http", String email, String password}) async {

    final addr = "$protocol://$serverUrl/api/session";
    Map<String, String> body = {
      'email'   : '$email',
      'password': '$password',
    };

    final response = await Dio().post(addr, data: body, 
    options: new Options(contentType:"application/x-www-form-urlencoded"));
    _cookie = response.headers["set-cookie"][0];
    print(_cookie);
}

Далее вам необходимо создать класс устройств (не забудьте изменить имя проекта в строке импорта utils)

import 'package:geopoint/geopoint.dart';
import 'package:your_project/utils/utils.dart';

/// A class representing a device
class Device {
  /// Main constructor
  Device(
      {this.id,
      this.uniqueId,
      this.groupId,
      this.name,
      this.position,
      this.batteryLevel,
      this.keepAlive = 1,
      this.isActive,
      this.isDisabled,
      this.properties = const <String, dynamic>{}});

  /// The device database id
  final int id;

  /// The on device unique id
  String uniqueId;

  /// The group of the device
  int groupId;

  /// The device name
  String name;

  /// The device position
  DevicePosition position;

  /// The device battery level
  double batteryLevel;

  /// Minutes a device is considered alive
  int keepAlive;

  /// The device can be disabled
  bool isDisabled;

  /// false if the device has never updated one position
  bool isActive;

  /// Extra properties for the device
  Map<String, dynamic> properties;

  /// Is the device online
  bool get isAlive => _isDeviceAlive();

  /// Create a device from json data
  Device.fromPosition(Map<String, dynamic> data,
      {String timeZoneOffset = "0", int keepAlive = 1})
      : this.keepAlive = keepAlive,
        this.id = int.parse(data["deviceId"].toString()),
        this.position =
            DevicePosition.fromJson(data, timeZoneOffset: timeZoneOffset),
        this.batteryLevel =
            double.parse(data["attributes"]["batteryLevel"].toString());

  bool _isDeviceAlive() {
    if (position == null) {
      return false;
    }
    final now = DateTime.now();
    final dateAlive = now.subtract(Duration(minutes: keepAlive));
    bool isAlive = false;
    if (position.date.isAfter(dateAlive)) {
      isAlive = true;
    }
    return isAlive;
  }

  /// Print a description of the device
  void describe() {
    print("Device:");
    print(" - id : $id");
    print(" - uniqueId : $uniqueId");
    print(" - name : $name");
    print(" - batteryLevel: $batteryLevel");
    print(" - position : $position");
  }

  @override
  String toString() {
    String _name = "$uniqueId";
    if (name != null) {
      _name = name;
    }
    String res;
    if (position != null) {
      res = "$_name: $position";
    } else {
      res = "$_name";
    }
    return res;
  }
}

/// A class to handle a device position
class DevicePosition {
  /// The position database id
  final int id;

  /// The geo data
  final GeoPoint geoPoint;

  /// The distance since previous point
  final double distance;

  /// The total distance for the device
  final double totalDistance;

  /// The address of the device position
  final String address;

  /// The date of the position
  DateTime date;

  /// Create a position from json
  DevicePosition.fromJson(Map<String, dynamic> data,
      {String timeZoneOffset = "0"})
      : this.id = int.parse(data["id"].toString()),
        this.geoPoint = GeoPoint(
            name: data["id"].toString(),
            latitude: double.parse(data["latitude"].toString()),
            longitude: double.parse(data["longitude"].toString()),
            speed: double.parse(data["speed"].toString()),
            accuracy: double.parse(data["accuracy"].toString()),
            altitude: double.parse(data["altitude"].toString())),
        this.distance = double.parse(data["attributes"]["distance"].toString()),
        this.totalDistance =
            double.parse(data["attributes"]["totalDistance"].toString()),
        this.address = data["address"].toString() {
    this.date = dateFromUtcOffset(data["fixTime"].toString(), timeZoneOffset);
  }

  @override
  String toString() {
    return "$date : ${geoPoint.latitude}, ${geoPoint.longitude}";
  }
}

Также вам следует использовать метод utils

/// parse a date
DateTime dateFromUtcOffset(String dateStr, String timeZoneOffset) {
  DateTime d = DateTime.parse(dateStr);
  if (timeZoneOffset.startsWith("+")) {
    final of = int.parse(timeZoneOffset.replaceFirst("+", ""));
    d = d.add(Duration(hours: of));
  } else if (timeZoneOffset.startsWith("-")) {
    final of = int.parse(timeZoneOffset.replaceFirst("-", ""));
    d = d.subtract(Duration(hours: of));
  }
  return d;
}

Наконец, для прослушивания позиций вам понадобятся следующие методы:

/// Get the device positions
  Future<Stream<Device>> positions() async {

    final posStream =
        await _positionsStream(serverUrl: serverUrl, email: _email, password: _password);

    _rawPosSub = posStream.listen((dynamic data) {
      print("DATA $data");
      final dataMap = json.jsonDecode(data.toString()) as Map<String, dynamic>;
      if (dataMap.containsKey("positions")) {

        DevicePosition pos;
        for (final posMap in dataMap["positions"]) {
          //print("POS MAP $posMap");
          pos = DevicePosition.fromJson(posMap as Map<String, dynamic>);
          final id = posMap["deviceId"] as int;
          Device device;
          if (_devicesMap.containsKey(id)) {
            device = _devicesMap[id];
          } else {
            device = Device.fromPosition(posMap as Map<String, dynamic>,
                keepAlive: 1);
          }
          device.position = pos;
          _devicesMap[id] = device;
          _positions.sink.add(device);
        }
      } else {
        for (final d in dataMap["devices"]) {
          if (!_devicesMap.containsKey(d["id"])) {
            final id = int.parse(d["id"].toString());
            d["name"] ??= d["id"].toString();
            final device = Device(id: id, name: d["name"].toString());
            _devicesMap[id] = device;
            //print(" - ${device.name}");
          }
        }
      }
    });
    return _positions.stream;
  }

Future<Stream<dynamic>> _positionsStream(
      {String serverUrl, String email, String password, String protocol = "http"}) async {
    if (_cookie == null) {
      await _getConnection(email: _email, password: _password);
    }
    final channel = IOWebSocketChannel.connect("ws://$serverUrl/api/socket",
        headers: <String, dynamic>{"Cookie": _cookie});
    return channel.stream;
  }

Когда вы закончите sh, вы можете позвонить

_init() async {
    _getConnection(email: _email, password: _password);
    final pos = await positions();
    print("Listening for position updates");
    pos.listen((device) {
      print("POSITION UPDATE: $device");
      print("${device.id}: ${device.position.geoPoint.latitude} / " +
         "${device.position.geoPoint.longitude}");
    });
}

Также я использую эти зависимости и стабильную версию 1.17.0 флаттера:

dio: ^3.0.9
web_socket_channel:
geopoint: ^0.7.1

Примечание: Я использую код из traccar_client 0.1.0 и измените его для доступа по электронной почте и паролю, но если вам нужно использовать токен, вы можете следовать примеру с https://github.com/synw/traccar_client. Кредиты им. :)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...