提问人:yogeshdinodia 提问时间:11/10/2023 最后编辑:yogeshdinodia 更新时间:11/11/2023 访问量:46
在 Flutter 中,我使用了带有 pusher flutter_bloc cupit,但是在获取新数据时 ui 没有更新
in flutter, i have used flutter_bloc cupit with pusher, but when getting new data ui not updated
问:
这是我的 bloc 代码
我想使用 Laravel API、推送器和一个信号创建实时聊天功能。我的所有代码都正常工作,除非我向聊天用户发送新消息,用户收到我的消息,并且我能够打印消息和数据,但收件人用户的 UI 没有更新新消息。
import 'package:flutter/material.dart';
import 'package:soulsphere/bloc/chat/chat_message_state.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:soulsphere/repositories/chats/chat_messages_repo.dart';
import 'package:soulsphere/model/chat_message.dart';
class ChatMessageCubit extends Cubit<ChatMessageState>{
final BuildContext context;
final String chatUserID;
ChatMessageCubit({
required this.context,
required this.chatUserID,
}) : super(ChatMessageLoadingState()){
fetchChatMessage(context, chatUserID);
}
ChatMessageRepository chatMessageRepository = ChatMessageRepository();
void fetchChatMessage(BuildContext context, String? chatUserID) async{
print("========================================");
print("in fetch chat message api");
try{
print("fetch message for: $chatUserID");
if(chatUserID != 0){
List<ChatMessage> chatMessages = await chatMessageRepository.fetchChatsFromApi(context, chatUserID);
emit(ChatMessageLoadedState(chatMessages));
}
else{
emit(ChatMessageErrorState("No Chat User Found"));
}
}
catch (ex){
emit(ChatMessageErrorState(ex.toString()));
}
}
void sendMessage(BuildContext context, String message, String chatUserID) async {
try{
List<ChatMessage> sentMessage = (await chatMessageRepository.sendMessage(context, message, chatUserID)).cast<ChatMessage>();
emit(ChatMessageLoadedState(sentMessage));
}
catch (ex){
emit(ChatMessageErrorState(ex.toString()));
}
}
void addNewMessageFromPusher(Map<String, dynamic> messageData) {
print("in add new message from pusher");
if (state is ChatMessageLoadedState) {
ChatMessage newMessage = ChatMessage.fromJson(messageData);
List<ChatMessage> currentMessages = List.from((state as ChatMessageLoadedState).chatMessages);
currentMessages.add(newMessage);
print("============================================================");
print("============================================================");
emit(ChatMessageLoadedState(currentMessages));
}
}
}
和我的聊天屏幕代码
import 'package:flutter/material.dart';
import 'package:pusher_client/pusher_client.dart';
import 'package:soulsphere/controller/laravel_echo/laravel_echo.dart';
import 'package:soulsphere/screens/users/user_view.dart';
import 'package:soulsphere/utils/app_constants.dart';
import 'package:soulsphere/model/user.dart';
import 'package:soulsphere/model/chat_message.dart';
import 'package:shimmer/shimmer.dart';
import 'dart:convert';
import 'package:soulsphere/utils/show_toast.dart';
import 'package:soulsphere/utils/shared_pref.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:soulsphere/bloc/chat/chat_message_cubit.dart';
import 'package:soulsphere/bloc/chat/chat_message_state.dart';
class ChatDetailsScreen extends StatefulWidget {
final User user;
const ChatDetailsScreen({
Key? key,
required this.user,
}) : super(key: key);
@override
State<ChatDetailsScreen> createState() => _ChatDetailsScreenState();
}
class _ChatDetailsScreenState extends State<ChatDetailsScreen> {
bool isLoading = true;
late String userID;
bool isChatDataFetched = true;
late ChatMessageCubit chatMessageCubit;
void listenChatChannel(chatID) {
LaravelEcho.instance.channel('chat.$chatID').listen(
'.message.sent',
(e) {
if (e is PusherEvent) {
if (e.data != null) {
Map<String, dynamic> messageData = json.decode(e.data!);
print("in function listen channel");
print(messageData);
chatMessageCubit.addNewMessageFromPusher(messageData);
}
}
},
).error((err) {
showToast(context, 'Pusher error: $err');
});
}
void leaveChatChannel(chatID){
try{
LaravelEcho.instance.leave('chat.$chatID');
}
catch(err){
showToast(context, err.toString());
}
}
@override
void initState() {
super.initState();
getUserDataFromSharedPreferences();
String chatID = widget.user.chatID.toString();
// Initialize chatMessageCubit before using it in listenChatChannel
chatMessageCubit = ChatMessageCubit(context: context, chatUserID: widget.user.userID.toString());
listenChatChannel(chatID);
}
@override
void dispose() {
String chatID = widget.user.chatID.toString();
leaveChatChannel(chatID);
chatMessageCubit.close();
super.dispose();
}
// ------- make UserID variable for using in build ------- //
Future<void> getUserDataFromSharedPreferences() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
userID = prefs.getString(CustomSharedConstants.userID) ?? '';
});
}
@override
Widget build(BuildContext context) {
User user = widget.user;
return BlocProvider(
create: (context) => ChatMessageCubit(context: context, chatUserID: widget.user.userID.toString()),
child: Scaffold(
appBar: AppBar(
title: Text(user.userName ?? AppConstants.noUserName),
backgroundColor: Colors.transparent,
flexibleSpace: Container(
decoration: const BoxDecoration(
gradient: AppColors.bgColorGradient,
),
),
actions: [
PopupMenuButton(
itemBuilder: (context) {
return [
const PopupMenuItem(
value: 'profile',
child: Text('View Profile'),
),
const PopupMenuItem(
value: 'settings',
child: Text('Settings'),
),
];
},
onSelected: (value){
if(value == 'profile'){
Navigator.push(context, MaterialPageRoute(builder: (context) => UserProfileScreen(user: user)));
}
},
),
],
),
body: BlocBuilder<ChatMessageCubit, ChatMessageState>(
key: const Key('chatMessagesKey'),
builder: (context, state) {
if(state is ChatMessageLoadingState){
print("in loading state");
return const Center(child: CircularProgressIndicator());
}
else if (state is ChatMessageLoadedState){
print("in loaded state");
return Column(
children: [
Expanded(
child: SingleChildScrollView(
child: ChatMessages(messages: state.chatMessages, userId: userID),
),
),
ChatInputField(
onSendMessage: (message, chatUserID) {
context.read<ChatMessageCubit>().sendMessage(context, message, chatUserID);
},
chatUserID: widget.user.userID.toString(),
onUpdateChatMessages: (updatedMessages) {
// Implement the logic to update messages if needed
},
),
],
);
}
else if(state is ChatMessageErrorState){
return Center(child: Text('Error: ${state.error}'));
}
else{
return const Center(child: Text('Failed to load data from API'));
}
},
),
),
);
}
}
class ShimmerEffect extends StatelessWidget {
const ShimmerEffect({super.key});
@override
Widget build(BuildContext context) {
return Shimmer.fromColors(
baseColor: Colors.grey[300]!,
highlightColor: Colors.grey[100]!,
child: ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: 10,
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Container(
width: 200.0,
height: 20.0,
color: Colors.white,
),
);
},
),
);
}
}
class ChatMessages extends StatelessWidget {
final List<ChatMessage> messages;
final String userId;
const ChatMessages({
Key? key,
required this.messages,
required this.userId,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: messages.length,
itemBuilder: (BuildContext context, int index) {
return ChatBubble(
message: messages[index].messageText!,
isMe: messages[index].userID.toString() == userId,
);
},
);
}
}
class ChatBubble extends StatelessWidget {
final String message;
final bool isMe;
const ChatBubble({
Key? key,
required this.message,
required this.isMe,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Align(
alignment: isMe ? Alignment.centerRight : Alignment.centerLeft,
child: Container(
margin: const EdgeInsets.all(8.0),
padding: const EdgeInsets.all(12.0),
decoration: BoxDecoration(
color: isMe ? Colors.blue : Colors.grey[300],
borderRadius: BorderRadius.circular(10.0),
),
child: Text(
message,
style: TextStyle(color: isMe ? Colors.white : Colors.black),
),
),
);
}
}
class ChatInputField extends StatefulWidget {
final Function(String, String) onSendMessage;
final String chatUserID;
final Function(List<ChatMessage>) onUpdateChatMessages;
const ChatInputField({
Key? key,
required this.onSendMessage,
required this.chatUserID,
required this.onUpdateChatMessages,
}) : super(key: key);
@override
State<ChatInputField> createState() => _ChatInputFieldState();
}
class _ChatInputFieldState extends State<ChatInputField> {
bool _isSending = false;
final _messageController = TextEditingController();
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
IconButton(
icon: const Icon(Icons.attach_file),
onPressed: () {
// Handle attachment button press
},
),
Expanded(
child: TextFormField(
controller: _messageController,
decoration: const InputDecoration(
contentPadding: EdgeInsets.all(15.0),
hintText: 'Type a message...',
border: InputBorder.none,
),
),
),
IconButton(
icon: _isSending
? const CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.blue),
)
: const Icon(Icons.send),
onPressed: _isSending
? null
: () async {
setState(() {
_isSending = true;
});
String message = _messageController.text;
await widget.onSendMessage(message, widget.chatUserID);
setState(() {
_isSending = false;
_messageController.clear();
});
},
),
],
),
);
}
}
我已经尝试了很多代码更改。
void listenChatChannel(chatID) {
LaravelEcho.instance.channel('chat.$chatID').listen(
'.message.sent',
(e) {
if (e is PusherEvent) {
if (e.data != null) {
Map<String, dynamic> messageData = json.decode(e.data!);
print("in function listen channel");
print(messageData);
chatMessageCubit.addNewMessageFromPusher(messageData);
}
}
},
).error((err) {
showToast(context, 'Pusher error: $err');
});
}
当目标用户收到新消息时触发此代码
答:
1赞
SaifAlmajd
11/10/2023
#1
你可以使用 flutter streambuilder 你可以在这里看到更多 (https://api.flutter.dev/flutter/widgets/StreamBuilder-class.html)
StreamBuilder 的示例
StreamBuilder<String>(
stream: generateNumbers, //put here your source of chats from database
builder: (
BuildContext context,
AsyncSnapshot<int> snapshot,
) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
} else if (snapshot.connectionState == ConnectionState.active
|| snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasError) {
return const Text('Error');
} else if (snapshot.hasData) {
return Text(
snapshot.data.toString(),
style: const TextStyle(color: Colors.teal, fontSize: 36)
);
} else {
return const Text('Empty data');
}
} else {
return Text('State: ${snapshot.connectionState}');
}
},
希望对您有所帮助
谢谢
0赞
dev
11/11/2023
#2
您正在使用 .you 可以将 ChatMessageCubit 的同一实例传递给所有 BlocBuilder。您正在 initState 中创建一个 chatMessageCubit 实例,只需将相同的实例传递给 BlocBuilders。示例代码如下。ChatMessageCubit
BlocProvider(
create: (context) => chatMessageCubit,
child: Scaffold(
appBar: AppBar(
title: Text(user.userName ?? AppConstants.noUserName),
backgroundColor: Colors.transparent,
flexibleSpace: Container(
decoration: const BoxDecoration(
gradient: AppColors.bgColorGradient,
),
),
评论
0赞
yogeshdinodia
11/11/2023
谢谢先生的帮助,它真的帮助了我,现在我的代码运行良好。
评论