Flutter `socket_io_client` конвертирует socket.on (событие, данные) в поток и используется в динамически сгенерированном конструкторе потоков - PullRequest
0 голосов
/ 07 мая 2020

У меня есть список людей, у каждого из которых есть собственное подключение к сокету. Я использую ListBuilder для создания списка виджетов для них, но я хочу, чтобы каждый из этих виджетов слушал, получило ли соединение сокета человека событие от сервера, и обновлял его соответствующим образом. Я перепробовал все, что мог найти в Интернете, и до сих пор не могу найти решение.

Это то, что я пробовал с одним из решений, опубликованных ниже

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:socket_io_client/socket_io_client.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  List<Person> sockets = [
    Person(
        name: 'Jon',
        socket: io('http://localhost:3000', {
          'transports': ['websocket'],
          'autoConnect': false
        }),
        controller: StreamController<String>()),
    Person(
        name: 'Tim',
        socket: io('http://localhost:3000', {
          'transports': ['websocket'],
          'autoConnect': false
        }),
        controller: StreamController<String>()),
    Person(
        name: 'Bob',
        socket: io('http://localhost:3000', {
          'transports': ['websocket'],
          'autoConnect': false
        }),
        controller: StreamController<String>()),
  ];

// function to sink the data
  void sinkToController(data, Person person) {
    var message = '$data';
    person.controller.sink.add(message);
  }

  void streamPerson(Person person) {
    person.socket.connect();
    person.socket.on('connect', (data) {
      print('connected');
      person.socket.on('serverMessage', (data) {
        print('$data');
        sinkToController(data, person);
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Socket test app"),
      ),
      body: Container(
          child: ListView.builder(
              itemCount: 3,
              itemBuilder: (context, index) {
                streamPerson(sockets[index]);

                return GestureDetector(
                  onTap: () => sockets[index].socket.emit('msg', 'this is me: ${sockets[index].name}'),
                  child: Container(
                    height: 300,
                    width: 350,
                    child: Column(
                      children: [
                        Text(sockets[index].name),
                        StreamBuilder<String>(
                          initialData: 'initial',
                          stream: sockets[index].controller.stream,
                          builder: (context, snapshot) {
                            return Text('${snapshot.data}');
                          },
                        )
                      ],
                    ),
                  ),
                );
              })),
    );
  }
}

class Person {
  String name;
  Socket socket;
  StreamController<String> controller;

  Person({this.name, this.socket, this.controller});
}

Однако это не ' t работает, поэтому я надеялся, что существует способ динамически преобразовать socket.on ('event') в поток и использовать его в сгенерированном построителе потока.

Результат:

A listBuilder where each person **should** have there own stream of data with their own name

Заранее спасибо

Ответы [ 2 ]

0 голосов
/ 08 мая 2020
// List of people to generate
var people = [
Person(name = 'Tina', email = 't@t.com', socket = IO.Socket('$host/events?userId=userID'))
Person(name = 'Bob', email = 'b@b.com', socket = IO.Socket('$host/events?userId=userID'))
Person(name = 'James', email = 'j@j.com', socket = IO.Socket('$host/events?userId=userID'))
];

//creating a controller for each
final _controllers = people.map((_) => StreamController<Message>()).toList();

//socketIO
user.websocket.on('connect', (_) {
    user.websocket.on('LocationChange', (data) {
        // NOTE: before sink convert data in to Message class
        sinkToController(data,user)
    });
});

// function to sink the data
void sinkToController(data,user){
   // create a helper function to convert json data to Message object
   var message = convertToMessage(data);
   _controllers[people.indexOf(user)].sink.add(message);
}
// listView builder will looks like this
ListView.builder(
    itemCount: people.length,
    itemBuilder: (BuildContext ctxt, int index) {
      return  StreamBuilder<Message>(
          stream: _controllers[index],
          builder: (BuildContext context, AsyncSnapshot<Message> snapshot) {
               // here you can play with your data now
               // var message = snapshot.data;
               // var user = people[index];
          });
    }
)

Надеюсь, это то, что вы ищете. Это не лучший ответ (практика). потому что я поддерживаю 2 массива для управления ситуацией. Я предлагаю вам создать еще один класс-оболочку с пользователем и контроллером потока. и не забудьте закрыть соединения после завершения работы.

для примера я использовал данные json в качестве сообщения, которое вы преобразовали в класс модели. Вы можете легко преобразовать json в классы моделей дротиков, используя это vs расширение кода .

0 голосов
/ 07 мая 2020

Вы должны использовать StreamController для создания потока для каждого пользователя. Вы можете узнать больше об этом здесь: https://dart.dev/articles/libraries/creating-streams#using -a-streamcontroller

^^^ Отредактировано, чтобы добавить лучший ответ выше

Ниже был мой первый выстрел в это с генераторами. Хотя, вероятно, это не сработает.

  Stream userToLocationStream(User user) async* {
    user.websocket.on('connect', (_) {
      user.websocket.on('LocationChange', (data) {
        yield data;
      });
    });
  }

Может быть, если вы решите следовать этому маршруту генератора вместо StreamController, мой доклад, посвященный генераторам, может быть полезным: https://youtu.be/vPHxckiSmKY?t=4112

...