【发布时间】: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