firebase 在聊天中检索数据时的进度指示器 - Flutter
Posted
技术标签:
【中文标题】firebase 在聊天中检索数据时的进度指示器 - Flutter【英文标题】:Progress Indicator when firebase is retrieving data on chat - Flutter 【发布时间】:2022-01-09 15:44:23 【问题描述】:我有一个与 firebase 实时数据库聊天的颤振项目,当我打开与用户的聊天时,firebase 需要 1-3 秒来加载对话。
所以,我必须在 firebase 检索打开的聊天数据时插入进度指示器。
这是课程:
class ChatView extends StatefulWidget
const ChatView(
Key key,
@required this.itemId,
@required this.chatFlag,
@required this.buyerUserId,
@required this.sellerUserId,
) : super(key: key);
final String itemId;
final String chatFlag;
final String buyerUserId;
final String sellerUserId;
// final String isOffer;
@override
_ChatViewState createState() => _ChatViewState();
enum ChatUserStatus active, in_active, offline
class _ChatViewState extends State<ChatView>
with SingleTickerProviderStateMixin, WidgetsBindingObserver
AnimationController animationController;
DatabaseReference _messagesRef;
DatabaseReference _chatRef;
DatabaseReference _userPresence;
final bool _anchorToBottom = true;
FirebaseApp firebaseApp;
PsValueHolder psValueHolder;
String sessionId;
ChatHistoryRepository chatHistoryRepository;
NotificationRepository notiRepo;
UserUnreadMessageRepository userUnreadMessageRepository;
GalleryRepository galleryRepo;
ProductRepository productRepo;
GetChatHistoryProvider getChatHistoryProvider;
UserUnreadMessageProvider userUnreadMessageProvider;
ChatHistoryListProvider chatHistoryListProvider;
ItemDetailProvider itemDetailProvider;
GalleryProvider galleryProvider;
NotificationProvider notiProvider;
SyncChatHistoryParameterHolder holder;
GetChatHistoryParameterHolder getChatHistoryParameterHolder;
PsResource<ChatHistory> chatHistory;
String lastTimeStamp;
int lastAddedDateTimeStamp;
String status = '';
String itemId;
String receiverId;
String senderId;
String otherUserId;
ChatUserStatus isActive;
TextEditingController messageController = TextEditingController();
Future<FirebaseApp> configureDatabase() async
WidgetsFlutterBinding.ensureInitialized();
final FirebaseApp app = await Firebase.initializeApp(
options: Platform.isios
? const FirebaseOptions(
appId: Config.iosGoogleAppId,
messagingSenderId: Config.iosGcmSenderId,
databaseURL: Config.iosDatabaseUrl,
projectId: Config.iosProjectId,
apiKey: Config.iosApiKey)
: const FirebaseOptions(
appId: Config.androidGoogleAppId,
apiKey: Config.androidApiKey,
projectId: Config.androidProjectId,
messagingSenderId: Config.androidGcmSenderId,
databaseURL: Config.androidDatabaseUrl,
),
);
return app;
@override
void initState()
super.initState();
configureDatabase().then((FirebaseApp app)
firebaseApp = app;
);
final FirebaseDatabase database = FirebaseDatabase(app: firebaseApp);
_messagesRef = database.reference().child('Message');
_chatRef = database.reference().child('Current_Chat_With');
_userPresence = database.reference().child('User_Presence');
if (database != null && database.databaseURL != null)
database.setPersistenceEnabled(true);
database.setPersistenceCacheSizeBytes(10000000);
animationController =
AnimationController(duration: PsConfig.animation_duration, vsync: this);
WidgetsBinding.instance.addObserver(this);
@override
void didChangeAppLifecycleState(AppLifecycleState state)
if (state == AppLifecycleState.resumed)
// user returned to our app
else if (state == AppLifecycleState.inactive)
_chatRef.child(psValueHolder.loginUserId).remove();
// app is inactive
else if (state == AppLifecycleState.paused)
_chatRef.child(psValueHolder.loginUserId).remove();
// user is about quit our app temporally
else if (state == AppLifecycleState.detached)
// app suspended (not used in iOS)
@override
void dispose()
super.dispose();
if (mounted)
_chatRef.child(psValueHolder.loginUserId).remove();
_userPresence.child(psValueHolder.loginUserId).remove();
WidgetsBinding.instance.removeObserver(this);
Utils.isReachChatView = false;
Future<bool> resetUnreadMessageCount(
ChatHistoryListProvider chatHistoryListProvider,
PsValueHolder valueHolder,
UserUnreadMessageProvider userUnreadMessageProvider) async
final ResetUnreadMessageParameterHolder resetUnreadMessageParameterHolder =
ResetUnreadMessageParameterHolder(
itemId: widget.itemId,
buyerUserId: widget.buyerUserId,
sellerUserId: widget.sellerUserId,
type: widget.chatFlag == PsConst.CHAT_FROM_BUYER
? PsConst.CHAT_TO_SELLER
: PsConst.CHAT_TO_BUYER);
final dynamic _returnData = await chatHistoryListProvider
.resetUnreadMessageCount(resetUnreadMessageParameterHolder.toMap());
if (_returnData == null)
if (valueHolder.loginUserId != null && valueHolder.loginUserId != '')
final UserUnreadMessageParameterHolder userUnreadMessageHolder =
UserUnreadMessageParameterHolder(
userId: valueHolder.loginUserId,
deviceToken: valueHolder.deviceToken);
userUnreadMessageProvider
.userUnreadMessageCount(userUnreadMessageHolder);
return true;
else
return false;
Future<void> _insertDataToFireBase(
String id,
bool isSold,
bool isUserBought,
String itemId,
String message,
int offerStatus,
String sendByUserId,
String sessionId,
int type,
) async
final Message messages = Message();
messages.addedDate = Utils.getTimeStamp();
messages.id = id;
messages.isSold = isSold;
messages.isUserBought = isUserBought;
messages.itemId = itemId;
messages.message = message;
messages.offerStatus = offerStatus;
messages.sendByUserId = sendByUserId;
messages.sessionId = sessionId;
messages.type = type;
final String newkey = _messagesRef.child(sessionId).push().key;
messages.id = newkey;
_messagesRef
.child(sessionId)
.child(newkey)
.set(messages.toInsertMap(messages));
Future<void> _deleteDataToFireBase(
String id,
bool isSold,
String itemId,
String message,
String sendByUserId,
String sessionId,
) async
final Message messages = Message();
messages.addedDate = Utils.getTimeStamp();
messages.id = id;
messages.isSold = isSold;
messages.itemId = itemId;
messages.message = message;
messages.sendByUserId = sendByUserId;
messages.sessionId = sessionId;
final String key =
_messagesRef.child(sessionId).child(id).remove().toString();
messages.id = key;
_messagesRef
.child(sessionId)
.child(key)
.set(messages.toDeleteMap(messages));
Future<void> _updateDataToFireBase(
int addedDate,
String id,
bool isSold,
bool isUserBought,
String itemId,
String message,
int offerStatus,
String sendByUserId,
String sessionId,
int type,
) async
final Message messages = Message();
messages.id = id;
messages.isSold = isSold;
messages.isUserBought = isUserBought;
messages.itemId = itemId;
messages.message = message;
messages.offerStatus = offerStatus;
messages.sendByUserId = sendByUserId;
messages.sessionId = sessionId;
messages.type = type;
messages.addedDateTimeStamp = addedDate;
_messagesRef
.child(sessionId)
.child(messages.id)
.set(messages.toUpdateMap(messages));
Future<void> _insertSenderAndReceiverToFireBase(
String sessionId,
String itemId,
String receiverId,
String senderId,
String userName) async
final Chat chat =
Chat(itemId: itemId, receiverId: receiverId, senderId: senderId);
_chatRef.child(senderId).set(chat.toMap(chat));
final ChatUserPresence chatUserPresence =
ChatUserPresence(userId: senderId, userName: userName);
_userPresence.child(senderId).set(chatUserPresence.toMap(chatUserPresence));
@override
Widget build(BuildContext context)
const Widget _spacingWidget = SizedBox(
width: PsDimens.space10,
);
lastTimeStamp = null;
psValueHolder = Provider.of<PsValueHolder>(context);
chatHistoryRepository = Provider.of<ChatHistoryRepository>(context);
notiRepo = Provider.of<NotificationRepository>(context);
galleryRepo = Provider.of<GalleryRepository>(context);
productRepo = Provider.of<ProductRepository>(context);
userUnreadMessageRepository =
Provider.of<UserUnreadMessageRepository>(context);
if (psValueHolder.loginUserId != null)
if (psValueHolder.loginUserId == widget.buyerUserId)
sessionId =
Utils.sortingUserId(widget.sellerUserId, widget.buyerUserId);
otherUserId = widget.sellerUserId;
else if (psValueHolder.loginUserId == widget.sellerUserId)
sessionId =
Utils.sortingUserId(widget.buyerUserId, widget.sellerUserId);
otherUserId = widget.buyerUserId;
_insertSenderAndReceiverToFireBase(sessionId, widget.itemId, otherUserId,
psValueHolder.loginUserId, psValueHolder.loginUserName);
_chatRef.child(otherUserId).onValue.listen((Event event)
if (event.snapshot.value == null)
if (isActive == null || isActive != ChatUserStatus.offline && mounted)
setState(()
status = Utils.getString(context, 'chat_view__status_offline');
isActive = ChatUserStatus.offline;
);
else
itemId = event.snapshot.value['itemId'];
final String _receiverId = event.snapshot.value['receiver_id'];
if (_receiverId == psValueHolder.loginUserId &&
itemId == widget.itemId)
if (isActive != ChatUserStatus.active && mounted)
setState(()
status = Utils.getString(context, 'chat_view__status_active');
isActive = ChatUserStatus.active;
);
else
if (isActive != ChatUserStatus.in_active && mounted)
setState(()
status = Utils.getString(context, 'chat_view__status_inactive');
isActive = ChatUserStatus.in_active;
);
);
Future<void> checkOfferStatus(ChatHistory chatHistory) async
if (chatHistory != null &&
chatHistory.isOffer == PsConst.ONE &&
chatHistory.isAccept != PsConst.ONE)
await getChatHistoryProvider
.getChatHistory(getChatHistoryParameterHolder);
return Scaffold(
appBar: PreferredSize(
preferredSize: const Size.fromHeight(0), // here the desired height
child: AppBar(
automaticallyImplyLeading: true,
systemOverlayStyle: SystemUiOverlayStyle(
statusBarIconBrightness: Utils.getBrightnessForAppBar(context),
),
iconTheme: Theme.of(context)
.iconTheme
.copyWith(color: PsColors.textPrimaryColor),
title: Text(
status,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.headline6.copyWith(
fontWeight: FontWeight.bold,
color: PsColors.white),
),
backgroundColor: PsColors.mainColor,
bottomOpacity: 0.0,
elevation: 0.0)),
body: PsWidgetWithMultiProvider(
child: MultiProvider(
providers: <SingleChildWidget>[
ChangeNotifierProvider<ItemDetailProvider>(
lazy: false,
create: (BuildContext context)
itemDetailProvider = ItemDetailProvider(
repo: productRepo, psValueHolder: psValueHolder);
final String loginUserId =
Utils.checkUserLoginId(psValueHolder);
itemDetailProvider.loadProduct(widget.itemId, loginUserId);
return itemDetailProvider;
),
ChangeNotifierProvider<UserUnreadMessageProvider>(
lazy: false,
create: (BuildContext context)
userUnreadMessageProvider = UserUnreadMessageProvider(
repo: userUnreadMessageRepository);
return userUnreadMessageProvider;
),
ChangeNotifierProvider<ChatHistoryListProvider>(
lazy: false,
create: (BuildContext context)
chatHistoryListProvider =
ChatHistoryListProvider(repo: chatHistoryRepository);
resetUnreadMessageCount(chatHistoryListProvider,
psValueHolder, userUnreadMessageProvider);
return chatHistoryListProvider;
),
ChangeNotifierProvider<NotificationProvider>(
lazy: false,
create: (BuildContext context)
notiProvider = NotificationProvider(
repo: notiRepo, psValueHolder: psValueHolder);
return notiProvider;
),
ChangeNotifierProvider<GalleryProvider>(
lazy: false,
create: (BuildContext context)
galleryProvider = GalleryProvider(
repo: galleryRepo,
);
return galleryProvider;
),
ChangeNotifierProvider<GetChatHistoryProvider>(
lazy: false,
create: (BuildContext context)
getChatHistoryProvider =
GetChatHistoryProvider(repo: chatHistoryRepository);
getChatHistoryParameterHolder = GetChatHistoryParameterHolder(
itemId: widget.itemId,
buyerUserId: widget.buyerUserId,
sellerUserId: widget.sellerUserId);
getChatHistoryProvider
.getChatHistory(getChatHistoryParameterHolder);
return getChatHistoryProvider;
),
],
child: Consumer<ItemDetailProvider>(builder:
(BuildContext context, ItemDetailProvider itemDetailProvider,
Widget child)
if (itemDetailProvider.itemDetail != null &&
itemDetailProvider.itemDetail.data != null)
return Container(
color: Utils.isLightMode(context)
? Colors.grey[100]
: Colors.grey[900],
child: Column(
children: <Widget>[
Align(
alignment: Alignment.topCenter,
child: Container(
alignment: Alignment.topCenter,
width: double.infinity,
child: ItemInfoWidget(
insertDataToFireBase: _insertDataToFireBase,
sessionId: sessionId,
itemData: itemDetailProvider.itemDetail.data,
sendByUserId: psValueHolder.loginUserId ?? '',
chatFlag: widget.chatFlag,
buyerUserId: widget.buyerUserId,
sellerUserId: widget.sellerUserId,
chatHistoryProvider: getChatHistoryProvider,
isOffer:
(getChatHistoryProvider.chatHistory.data !=
null &&
getChatHistoryProvider
.chatHistory.data.id !=
'')
? getChatHistoryProvider
.chatHistory.data.isOffer
: '0',
isUserOnline: isActive == ChatUserStatus.active
? PsConst.ONE
: PsConst.ZERO,
)
)),
Flexible(
child: Container(
margin:
const EdgeInsets.only(bottom: PsDimens.space12),
child: FirebaseAnimatedList(
key: ValueKey<bool>(_anchorToBottom),
query: _messagesRef
.child(sessionId)
.orderByChild('itemId')
.equalTo(widget.itemId),
reverse: _anchorToBottom,
sort: _anchorToBottom
? (DataSnapshot a, DataSnapshot b)
return b.value['addedDate']
.toString()
.compareTo(
a.value['addedDate'].toString());
: null,
itemBuilder: (BuildContext context,
DataSnapshot snapshot,
Animation<double> animation,
int index)
print('- - - - - - - /nIndex : $index');
bool isSameDate = false;
final Message messages =
Message().fromMap(snapshot.value);
final String chatDateString =
Utils.convertTimeStampToDate(
messages.addedDateTimeStamp);
if (index == 0 || lastTimeStamp == null)
lastTimeStamp = chatDateString;
lastAddedDateTimeStamp =
messages.addedDateTimeStamp;
final DateTime msgDate =
Utils.getDateOnlyFromTimeStamp(
messages.addedDateTimeStamp);
final DateTime lastDate =
Utils.getDateOnlyFromTimeStamp(
lastAddedDateTimeStamp);
if (lastTimeStamp == chatDateString ||
msgDate.compareTo(lastDate) >= 0)
isSameDate = true;
else
isSameDate = false;
final Widget _chatCell =
_ChatPageWidget(
buyerUserId: widget.buyerUserId,
sellerUserId: widget.sellerUserId,
chatFlag: widget.chatFlag,
chatHistoryProvider: getChatHistoryProvider,
chatHistoryParameterHolder:
getChatHistoryParameterHolder,
messageObj: messages,
itemDetail:
itemDetailProvider.itemDetail.data,
psValueHolder: psValueHolder,
updateDataToFireBase: _updateDataToFireBase,
insertDataToFireBase: _insertDataToFireBase,
deleteDataToFireBase: _deleteDataToFireBase,
checkOfferStatus: checkOfferStatus,
index: index,
isUserOnline:
isActive == ChatUserStatus.active
? PsConst.ONE
: PsConst.ZERO,
);
Widget _dateWidget;
if (!isSameDate)
_dateWidget = Container(
margin: const EdgeInsets.only(
top: PsDimens.space8,
bottom: PsDimens.space8),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: <Widget>[
_spacingWidget,
const Expanded(
child: Divider(
height: PsDimens.space1,
color: Colors.black54),
),
_spacingWidget,
Container(
padding: const EdgeInsets.all(
PsDimens.space4),
decoration: BoxDecoration(
color: Colors.black54,
borderRadius:
BorderRadius.circular(
PsDimens.space8)),
child: Text(
lastTimeStamp,
style: Theme.of(context)
.textTheme
.caption
.copyWith(color: Colors.white),
),
),
_spacingWidget,
const Expanded(
child: Divider(
height: PsDimens.space1,
color: Colors.black54),
),
_spacingWidget,
],
),
);
lastTimeStamp = chatDateString;
lastAddedDateTimeStamp =
messages.addedDateTimeStamp;
if (msgDate.compareTo(lastDate) >= 0)
lastTimeStamp = chatDateString;
lastAddedDateTimeStamp =
messages.addedDateTimeStamp;
return isSameDate
? _chatCell
: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
_chatCell,
_dateWidget,
],
);
,
),
),
),
],
),
);
else
return Container();
))),
);
你能建议我一个解决方案吗?
Flutter 2.5.3 版
【问题讨论】:
在处理 Futures 时,使用 FutureBuilder 小部件。 【参考方案1】:像这样实现FutureBuilder
(最好在初始化 Firebase 的 main.dart 中):
return FutureBuilder(
// Initialize FlutterFire:
future: _initialization,
builder: (context, snapshot)
// Check for errors
if (snapshot.hasError)
return SomethingWentWrong();
// Once complete, show your application
if (snapshot.connectionState == ConnectionState.done)
return MyAwesomeApp();
// Otherwise, show something whilst waiting for initialization to complete
return Loading();
,
);
对于Loading();
,您可以制作自己的自定义小部件或仅渲染CircularProgressIndicator()
。 Here你可以了解如何配置它。
【讨论】:
你能用我的代码 sn-p 做例子吗?对不起我的新手问题 您的 sn-p 相当大,我不确定您的整体项目结构如何,我建议您深入了解 this article 并将其应用于您的项目,因为您知道最好的,这将是您成长为开发人员的一种更好的做法;)以上是关于firebase 在聊天中检索数据时的进度指示器 - Flutter的主要内容,如果未能解决你的问题,请参考以下文章