【问题标题】:Stream builder ConnectionState.active called multiple times and return widgets again and again in flutter流生成器 ConnectionState.active 多次调用并在颤动中一次又一次地返回小部件
【发布时间】:2021-11-04 21:58:11
【问题描述】:

在我当前的项目中,我正在努力实现实时聊天。在这里,当用户发送消息事件将触发并在流中获取此数据时,我订阅了一个频道。问题是当我单击消息发送框时 ConnectionState.active 再次被调用并返回小部件而不触发任何新事件或新流。这是屏幕截图:

在这里发送任何消息后,我将获得新的流并返回另一个消息视图。但是当我再次选择消息字段时,会调用 ConnectionState.active 并一次又一次地返回消息视图。

我的小部件类:

class SingleChatRoom extends StatefulWidget {
  final int singleRoomId;

  SingleChatRoom({@required this.singleRoomId});

  @override
  _SingleChatRoomState createState() => _SingleChatRoomState();
}

class _SingleChatRoomState extends State<SingleChatRoom> {
  TextEditingController _controller = TextEditingController();
  ChatBloc chatBloc;
  String msg;
  int userId;
  List<SocketRp> socketRps = [];
  PusherService pusherService = PusherService();

  initEvent() async {
    userId = await UserSharePreference.getInt(AppConstant.userId);
    print("user id-> $userId");
    chatBloc.add(
        ChatSingleRoomEvent(singleRoomId: widget.singleRoomId, userId: userId));

    ///call fire pusher, subscribe channel and bind on event
    pusherService = PusherService();
    pusherService.firePusher(
        'private-user-asyn-channel.$userId', 'App\\Events\\UserChannel');
  }

  @override
  void initState() {
    initEvent();
    chatBloc = context.read<ChatBloc>();
    super.initState();
  }

  @override
  void dispose() {
    pusherService.unbindEvent('App\\Events\\UserChannel');
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.grey[800],
      body: SafeArea(
        child: BlocBuilder<ChatBloc, ChatState>(builder: (context, state) {
          if (state is ChatLoading) {
            return Container(
              height: MediaQuery.of(context).size.height * 0.8,
              child: Center(
                child: CircularProgressIndicator(),
              ),
            );
          } else if (state is ChatFailure) {
            return Container(
              height: MediaQuery.of(context).size.height * 0.8,
              child: Center(child: Text('${state.errorMessage}')),
            );
          } else if (state is SingleChatSuccess) {
            return Stack(
              children: [
                SingleChildScrollView(
                  child: Column(
                    children: [
                      SizedBox(
                        height: 70,
                      ),
                      ListView.builder(
                          itemCount: state.singleChatRoomModel.messages.length,
                          physics: NeverScrollableScrollPhysics(),
                          shrinkWrap: true,
                          scrollDirection: Axis.vertical,
                          itemBuilder: (context, index) {
                            return Container(
                              decoration: BoxDecoration(
                                borderRadius: BorderRadius.circular(3.6),
                                color: Colors.grey[800],
                              ),
                              padding: EdgeInsets.fromLTRB(14, 4, 8, 4),
                              margin:
                                  EdgeInsets.only(bottom: 1, left: 8, right: 8),
                              child: MediaQuery.removePadding(
                                context: context,
                                removeBottom: true,
                                removeTop: true,
                                child: Column(children: [
                                  Row(
                                    children: [
                                      Container(
                                        height: 56,
                                        width: 56,
                                        decoration: BoxDecoration(
                                          shape: BoxShape.circle,
                                          border: Border.all(
                                              color: Colors.grey, width: 2),
                                        ),
                                        child: ClipRRect(
                                            borderRadius:
                                                BorderRadius.circular(50),
                                            child: Image.network(
                                              '${state.singleChatRoomModel.messages[index].user.profilePhotoUrl}',
                                              fit: BoxFit.fill,
                                            )),
                                      ),
                                      SizedBox(
                                        width: 10,
                                      ),
                                      Column(
                                        crossAxisAlignment:
                                            CrossAxisAlignment.start,
                                        children: [
                                          SizedBox(
                                            width: width / 2.4,
                                            child: Text(
                                              '${state.singleChatRoomModel.messages[index].user.name}',
                                              style: descriptionTextStyle(
                                                color: dayDateColor,
                                                fontWeight: FontWeight.w600,
                                              ),
                                            ),
                                          ),
                                          SizedBox(
                                            width: width / 3,
                                            child: Text(
                                              '7:30 AM | May 9',
                                              maxLines: 1,
                                              style: commonTextStyle(
                                                fontSize: 9,
                                                color: Colors.grey,
                                                fontWeight: FontWeight.w400,
                                              ),
                                            ),
                                          ),
                                          SizedBox(
                                            height: 6,
                                          ),
                                          SizedBox(
                                            width: width / 3,
                                            child: Text(
                                              '${state.singleChatRoomModel.messages[index].body}',
                                              maxLines: 1,
                                              style: sessionStickerTextStyle(
                                                color: Colors.white,
                                                fontWeight: FontWeight.w500,
                                              ),
                                            ),
                                          ),
                                        ],
                                      ),
                                    ],
                                  ),
                                  SizedBox(
                                    height: 10,
                                  ),
                                  Container(
                                    height: 1,
                                    width: double.infinity,
                                    color: Colors.grey,
                                  )
                                ]),
                              ),
                            );
                          }),
                      StreamBuilder(
                        stream: pusherService.eventStream,
                        builder:
                            (BuildContext context, AsyncSnapshot snapshot) {
                          switch (snapshot.connectionState) {
                            case ConnectionState.waiting:
                              return CircularProgressIndicator();
                            case ConnectionState.active:
                              print("ConnectionState.active called!");
                              return getMessages(snapshot.data);
                              break;
                            default:
                              return Container();
                          }
                        },
                      ),
                      SizedBox(
                        height: 60,
                      ),
                    ],
                  ),
                ),
                Positioned(
                  left: 0, right: 0, top: 0,
                  child: Container(
                    width: double.infinity,
                    color: Colors.grey,
                    child: Container(
                      height: 60,
                      width: double.infinity,
                      padding: EdgeInsets.symmetric(horizontal: 14),
                      child: Row(
                        mainAxisAlignment: MainAxisAlignment.spaceBetween,
                        children: [
                          Row(
                            children: [
                              InkWell(
                                onTap: () {
                                  chatBloc
                                      .add(ChatSuccessEvent(userId: userId));
                                  Navigator.pop(context);
                                },
                                child: Icon(
                                  Icons.arrow_back_ios,
                                  color: Colors.white,
                                ),
                              ),
                              SizedBox(
                                width: 6,
                              ),
                              Text(
                                '${state.singleChatRoomModel.user.name}',
                                style: commonTextStyle(
                                  fontSize: 16,
                                  fontWeight: FontWeight.w500,
                                  color: Colors.white,
                                ),
                              ),
                            ],
                          ),
                          InkWell(
                            onTap: () {
                              chatBloc.add(ChatSuccessEvent());
                              Navigator.pop(context);
                            },
                            child: Container(
                              height: 20,
                              width: 20,
                              decoration: BoxDecoration(
                                shape: BoxShape.circle,
                                color: redColor,
                              ),
                              child: Icon(
                                Icons.close,
                                size: 14,
                                color: Colors.white,
                              ),
                            ),
                          )
                        ],
                      ),
                    ),
                  ),
                ),
                Positioned(
                  bottom: 5,
                  left: 0,
                  right: 0,
                  child: Container(
                    height: 50,
                    margin: EdgeInsets.symmetric(horizontal: 12),
                    decoration: BoxDecoration(
                      borderRadius: BorderRadius.circular(35),
                      color: Colors.grey,
                    ),
                    child: Row(
                      children: [
                        SizedBox(
                          width: 8,
                        ),
                        Expanded(
                          child: TextField(
                            controller: _controller,
                            decoration: InputDecoration(
                              border: InputBorder.none,
                              hintText: 'Type your message',
                              hintStyle: commonTextStyle(
                                  fontSize: 12,
                                  fontWeight: FontWeight.w400,
                                  color: titleColor),
                            ),
                          ),
                        ),
                        GestureDetector(
                            onTap: () {
                              hideSoftKeyword(context);
                              SentMessageModel sentMessageModel =
                                  SentMessageModel(
                                      context: _controller.text,
                                      type: "user_room",
                                      roomId: state.singleChatRoomModel.id,
                                      isAvailableRoom: true,
                                      receiptId: 0);
                              chatBloc.add(SentMessageEvent(
                                  roomId: state.singleChatRoomModel.id,
                                  userId: userId,
                                  sentMessageModel: sentMessageModel,
                                  singleRoomId:
                                      state.singleChatRoomModel.user.id));
                              _controller.clear();
                            },
                            child: Icon(Icons.send)),
                        SizedBox(
                          width: 8,
                        ),
                      ],
                    ),
                  ),
                ),
              ],
            );
          }
          return Container();
        }),
      ),
    );
  }

  ///this method return live messages when get data from web socket
  Column getMessages(String socketData) {
    SocketRp socketRp =
    socketRpFromJson(socketData);
    socketRps.add(socketRp);
    print(socketRps.length.toString());
    List<Widget> list = [];
    socketRps.forEach((element) {
      list.add(Container(
        decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(3.6),
          color: Colors.grey[800],
        ),
        padding: EdgeInsets.fromLTRB(14, 4, 8, 4),
        margin: EdgeInsets.only(bottom: 1, left: 8, right: 8),
        child: MediaQuery.removePadding(
          context: navigatorKey.currentContext,
          removeBottom: true,
          removeTop: true,
          child: Column(children: [
            Row(
              children: [
                Container(
                  height: 56,
                  width: 56,
                  decoration: BoxDecoration(
                    shape: BoxShape.circle,
                    border: Border.all(color: Colors.grey, width: 2),
                  ),
                  child: ClipRRect(
                      borderRadius: BorderRadius.circular(50),
                      child: Image.network(
                        '${element.content.user.profilePhotoUrl}',
                        fit: BoxFit.fill,
                      )),
                ),
                SizedBox(
                  width: 10,
                ),
                Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    SizedBox(
                      width: width / 2.4,
                      child: Text(
                        '${element.content.user.name}',
                        style: descriptionTextStyle(
                          color: dayDateColor,
                          fontWeight: FontWeight.w600,
                        ),
                      ),
                    ),
                    SizedBox(
                      width: width / 3,
                      child: Text(
                        '7:30 AM | May 9',
                        maxLines: 1,
                        style: commonTextStyle(
                          fontSize: 9,
                          color: Colors.grey,
                          fontWeight: FontWeight.w400,
                        ),
                      ),
                    ),
                    SizedBox(
                      height: 6,
                    ),
                    SizedBox(
                      width: width / 3,
                      child: Text(
                        '${element.content.body}',
                        maxLines: 1,
                        style: sessionStickerTextStyle(
                          color: Colors.white,
                          fontWeight: FontWeight.w500,
                        ),
                      ),
                    ),
                  ],
                ),
              ],
            ),
            SizedBox(
              height: 10,
            ),
            Container(
              height: 1,
              width: double.infinity,
              color: Colors.grey,
            )
          ]),
        ),
      ));
    });
    return Column(
      children: list,
    );
  }
}

void hideSoftKeyword(BuildContext context) {
  if (FocusScope.of(context).isFirstFocus) {
    FocusScope.of(context).requestFocus(new FocusNode());
  }
}

推送服务:

  class PusherService {
  Event lastEvent = Event();
  String lastConnectionState = '';
  Channel channel = Channel();

  StreamController<String> _eventData = StreamController<String>();

  Sink get _inEventData => _eventData.sink;

  Stream get eventStream => _eventData.stream;

  Future<void> initPusher() async {
    try {
      await Pusher.init(
          "app_key",
          PusherOptions(
              cluster: "ap2",
              encrypted: true,
              auth: PusherAuth(
                'my_brodcasting_url',
                headers: {
                  'Authorization':
                      'Bearer my_token',
                  'Content-Type': 'application/json',
                },
              )));
    } on PlatformException catch (e) {
      print(e.message);
    }
  }

  void connectPusher(String channelName, String eventName) {
    Pusher.connect(
        onConnectionStateChange: (ConnectionStateChange connectionState) async {
      lastConnectionState = connectionState.currentState;
      print("${connectionState.currentState}");
      if (connectionState.currentState == 'CONNECTED') {
        print("connected to $channelName");
        await subscribePusher(channelName);
        bindEvent(eventName);
      }
    }, onError: (ConnectionError e) {
      print(e.toJson());
    });
  }

  Future<void> subscribePusher(String channelName) async {
    print("subscribing to $channelName");
    channel = await Pusher.subscribe(channelName);
    print("subscribed to $channel");
  }

  void unSubscribePusher(String channelName) {
    Pusher.unsubscribe(channelName);
  }

  void bindEvent(String eventName) {
    print("binding to $eventName");

    channel.bind(eventName, (last) {
      final String data = last.data;
      final SocketRp socketRp = socketRpFromJson(last.data);
      print("message: ${socketRp.content.toJson()}");
      _inEventData.add(data);
    });
  }

  void unbindEvent(String eventName) {
    channel.unbind(eventName);
    _eventData.close();
  }

  Future<void> firePusher(String channelName, String eventName) async {
    await initPusher();
    connectPusher(channelName, eventName);
    //await subscribePusher(channelName);
    //bindEvent(eventName);
  }
}

控制台:

    ConnectionState.active called!
    ConnectionState.active called!
    ConnectionState.active called!
    ConnectionState.active called!
    ConnectionState.active called!
    ConnectionState.active called!

谁能告诉我这背后的问题?

【问题讨论】:

  • 我的意思是当我发送任何消息时,我都会收到新的流。但是当我再次选择 Textfeild 之前的流重建时没有发送任何消息,这就是问题所在。
  • pusherService.eventStream 我们需要这方面的信息。 TextField 代码没用。
  • 添加推送服务类@HamdamMuqimov 请再次查看。
  • @YousufAli 你能分享你的streambuilder页面代码吗?当您打开键盘时,构建方法调用并重新渲染 streambuilder。共享整个页面代码,以便我了解如何为您提供帮助。谢谢
  • @HamdamMuqimov 我已经附加了我的小部件类,你能再检查一下吗。

标签: flutter chat pusher stream-builder


【解决方案1】:

因为每次打开/关闭键盘时,flutter 都会运行 build()。我猜streambuilder多次重新订阅频道。我在使用 StreamBuilder() 时也遇到了同样的问题,我收到了重复的数据。

我有两个选择:

  1. 尝试在 initEvent() 中监听流,而不是使用 Streambuilder。

     late List<dynamic> listData;
     late StreamSubscription _streamSubscription;
       @override
       void initState() {
         // TODO: implement initState
         _streamSubscription = pusherService.eventStream.listen((event){ 
          setState(() {
       //... do your staff here .. maybe like this listData.add(event.data) cuz I don't know what you are receiving from websocket channel 
     });
         })
         super.initState();
       }
     void dispose(){
     _streamSubscription.cancel()// close stream    
    

    }

  2. 根据您的事件流创建另一个重建唯一 listView 的 Bloc。这是获得良好架构的最佳方法。

here 是一个很好解释的视频,介绍了如何收听另一个流事件。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-05-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-04
    • 1970-01-01
    • 2013-11-04
    • 1970-01-01
    相关资源
    最近更新 更多