import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_localizations/stream_chat_localizations.dart'; class _NnStreamChatLocalizationsDelegate extends LocalizationsDelegate { const _NnStreamChatLocalizationsDelegate(); @override bool isSupported(Locale locale) => locale.languageCode == 'nn'; @override Future load(Locale locale) => SynchronousFuture(const NnStreamChatLocalizations()); @override bool shouldReload(_NnStreamChatLocalizationsDelegate old) => false; } /// A custom set of localizations for the 'nn' locale. In this example, only /// the value for launchUrlError was modified to use a custom message as /// an example. Everything else uses the American English (en_US) messages /// and formatting. class NnStreamChatLocalizations extends GlobalStreamChatLocalizations { /// Create an instance of the translation bundle for English. const NnStreamChatLocalizations({super.localeName = 'nn'}); /// A [LocalizationsDelegate] for [NnStreamChatLocalizations]. static const delegate = _NnStreamChatLocalizationsDelegate(); @override String get launchUrlError => 'Custom error'; @override String get loadingUsersError => 'Error loading users'; @override String get noUsersLabel => 'There are no users currently'; @override String get noPhotoOrVideoLabel => 'There is no photo or video'; @override String get retryLabel => 'Retry'; @override String get userLastOnlineText => 'Last online'; @override String get userOnlineText => 'Online'; @override String userTypingText(Iterable users) { if (users.isEmpty) return ''; final first = users.first; if (users.length == 1) { return '${first.name} is typing'; } return '${first.name} and ${users.length - 1} more are typing'; } @override String get threadReplyLabel => 'Thread Reply'; @override String get onlyVisibleToYouText => 'Only visible to you'; @override String threadReplyCountText(int count) => '$count Thread Replies'; @override String attachmentsUploadProgressText({ required int remaining, required int total, }) => 'Uploading $remaining/$total ...'; @override String pinnedByUserText({ required User pinnedBy, required User currentUser, }) { final pinnedByCurrentUser = currentUser.id == pinnedBy.id; if (pinnedByCurrentUser) return 'Pinned by You'; return 'Pinned by ${pinnedBy.name}'; } @override String get sendMessagePermissionError => "You don't have permission to send messages"; @override String get emptyMessagesText => 'There are no messages currently'; @override String get genericErrorText => 'Something went wrong'; @override String get loadingMessagesError => 'Error loading messages'; @override String resultCountText(int count) => '$count results'; @override String get messageDeletedText => 'This message is deleted.'; @override String get messageDeletedLabel => 'Message deleted'; @override String get messageReactionsLabel => 'Message Reactions'; @override String get emptyChatMessagesText => 'No chats here yet...'; @override String threadSeparatorText(int replyCount) { if (replyCount == 1) return '1 Reply'; return '$replyCount Replies'; } @override String get connectedLabel => 'Connected'; @override String get disconnectedLabel => 'Disconnected'; @override String get reconnectingLabel => 'Reconnecting...'; @override String get alsoSendAsDirectMessageLabel => 'Also send as direct message'; @override String get addACommentOrSendLabel => 'Add a comment or send'; @override String get searchGifLabel => 'Search GIFs'; @override String get writeAMessageLabel => 'Write a message'; @override String get instantCommandsLabel => 'Instant Commands'; @override String fileTooLargeAfterCompressionError(double limitInMB) => 'The file is too large to upload. ' 'The file size limit is $limitInMB MB. ' 'We tried compressing it, but it was not enough.'; @override String fileTooLargeError(double limitInMB) => 'The file is too large to upload. The file size limit is $limitInMB MB.'; @override String get couldNotReadBytesFromFileError => 'Could not read bytes from file.'; @override String get addAFileLabel => 'Add a file'; @override String get photoFromCameraLabel => 'Photo from camera'; @override String get uploadAFileLabel => 'Upload a file'; @override String get uploadAPhotoLabel => 'Upload a photo'; @override String get uploadAVideoLabel => 'Upload a video'; @override String get videoFromCameraLabel => 'Video from camera'; @override String get okLabel => 'OK'; @override String get somethingWentWrongError => 'Something went wrong'; @override String get addMoreFilesLabel => 'Add more files'; @override String get enablePhotoAndVideoAccessMessage => 'Please enable access to your photos' '\nand videos so you can share them with friends.'; @override String get allowGalleryAccessMessage => 'Allow access to your gallery'; @override String get flagMessageLabel => 'Flag Message'; @override String get flagMessageQuestion => 'Do you want to send a copy of this message to a' '\nmoderator for further investigation?'; @override String get flagLabel => 'FLAG'; @override String get cancelLabel => 'CANCEL'; @override String get flagMessageSuccessfulLabel => 'Message flagged'; @override String get flagMessageSuccessfulText => 'The message has been reported to a moderator.'; @override String get deleteLabel => 'DELETE'; @override String get deleteMessageLabel => 'Delete Message'; @override String get deleteMessageQuestion => 'Are you sure you want to permanently delete this\nmessage?'; @override String get operationCouldNotBeCompletedText => "The operation couldn't be completed."; @override String get replyLabel => 'Reply'; @override String togglePinUnpinText({required bool pinned}) { if (pinned) return 'Unpin from Conversation'; return 'Pin to Conversation'; } @override String toggleDeleteRetryDeleteMessageText({required bool isDeleteFailed}) { if (isDeleteFailed) return 'Retry Deleting Message'; return 'Delete Message'; } @override String get copyMessageLabel => 'Copy Message'; @override String get editMessageLabel => 'Edit Message'; @override String toggleResendOrResendEditedMessage({required bool isUpdateFailed}) { if (isUpdateFailed) return 'Resend Edited Message'; return 'Resend'; } @override String get photosLabel => 'Photos'; String _getDay(DateTime dateTime) { final now = DateTime.now(); final today = DateTime(now.year, now.month, now.day); final yesterday = DateTime(now.year, now.month, now.day - 1); final date = DateTime(dateTime.year, dateTime.month, dateTime.day); if (date == today) { return 'today'; } else if (date == yesterday) { return 'yesterday'; } else { return 'on ${Jiffy.parseFromDateTime(date).MMMd}'; } } @override String sentAtText({required DateTime date, required DateTime time}) { final atTime = Jiffy.parseFromDateTime(time.toLocal()); return 'Sent ${_getDay(date)} at ${atTime.jm}'; } @override String get todayLabel => 'Today'; @override String get yesterdayLabel => 'Yesterday'; @override String get channelIsMutedText => 'Channel is muted'; @override String get noTitleText => 'No title'; @override String get letsStartChattingLabel => 'Let’s start chatting!'; @override String get sendingFirstMessageLabel => 'How about sending your first message to a friend?'; @override String get startAChatLabel => 'Start a chat'; @override String get loadingChannelsError => 'Error loading channels'; @override String get deleteConversationLabel => 'Delete Conversation'; @override String get deleteConversationQuestion => 'Are you sure you want to delete this conversation?'; @override String get streamChatLabel => 'Stream Chat'; @override String get searchingForNetworkText => 'Searching for Network'; @override String get offlineLabel => 'Offline...'; @override String get tryAgainLabel => 'Try Again'; @override String membersCountText(int count) { if (count == 1) return '1 Member'; return '$count Members'; } @override String watchersCountText(int count) { if (count == 1) return '1 Online'; return '$count Online'; } @override String get viewInfoLabel => 'View Info'; @override String get leaveGroupLabel => 'Leave Group'; @override String get leaveLabel => 'LEAVE'; @override String get leaveConversationLabel => 'Leave conversation'; @override String get leaveConversationQuestion => 'Are you sure you want to leave this conversation?'; @override String get showInChatLabel => 'Show in Chat'; @override String get saveImageLabel => 'Save Image'; @override String get saveVideoLabel => 'Save Video'; @override String get uploadErrorLabel => 'UPLOAD ERROR'; @override String get giphyLabel => 'Giphy'; @override String get shuffleLabel => 'Shuffle'; @override String get sendLabel => 'Send'; @override String get withText => 'with'; @override String get inText => 'in'; @override String get youText => 'You'; @override String galleryPaginationText({ required int currentPage, required int totalPages, }) => '$currentPage of $totalPages'; @override String get fileText => 'File'; @override String get replyToMessageLabel => 'Reply to Message'; @override String attachmentLimitExceedError(int limit) => 'Attachment limit exceeded, limit: $limit'; @override String get slowModeOnLabel => 'Slow mode ON'; @override String get downloadLabel => 'Download'; @override String toggleMuteUnmuteUserText({required bool isMuted}) { if (isMuted) { return 'Unmute User'; } else { return 'Mute User'; } } @override String toggleMuteUnmuteGroupQuestion({required bool isMuted}) { if (isMuted) { return 'Are you sure you want to unmute this group?'; } else { return 'Are you sure you want to mute this group?'; } } @override String toggleMuteUnmuteUserQuestion({required bool isMuted}) { if (isMuted) { return 'Are you sure you want to unmute this user?'; } else { return 'Are you sure you want to mute this user?'; } } @override String toggleMuteUnmuteAction({required bool isMuted}) { if (isMuted) { return 'UNMUTE'; } else { return 'MUTE'; } } @override String toggleMuteUnmuteGroupText({required bool isMuted}) { if (isMuted) { return 'Unmute Group'; } else { return 'Mute Group'; } } @override String get linkDisabledDetails => 'Sending links is not allowed in this conversation.'; @override String get linkDisabledError => 'Links are disabled'; @override String get viewLibrary => 'View library'; @override String unreadMessagesSeparatorText(int unreadCount) => 'New messages'; @override String get enableFileAccessMessage => 'Enable file access to continue'; @override String get allowFileAccessMessage => 'Allow access to files'; } void main() async { WidgetsFlutterBinding.ensureInitialized(); /// Create a new instance of [StreamChatClient] passing the apikey obtained /// from your project dashboard. final client = StreamChatClient( 's2dxdhpxd94g', logLevel: Level.INFO, ); /// Set the current user and connect the websocket. In a production /// scenario, this should be done using a backend to generate a user token /// using our server SDK. /// /// Please see the following for more information: /// https://getstream.io/chat/docs/ios_user_setup_and_tokens/ await client.connectUser( User(id: 'super-band-9'), '''eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic3VwZXItYmFuZC05In0.0L6lGoeLwkz0aZRUcpZKsvaXtNEDHBcezVTZ0oPq40A''', ); final channel = client.channel('messaging', id: 'godevs'); await channel.watch(); runApp( MyApp( client: client, channel: channel, ), ); } /// Example application using Stream Chat Flutter widgets. /// /// Stream Chat Flutter is a set of Flutter widgets which provide full chat /// functionalities for building Flutter applications using Stream. If you'd /// prefer using minimal wrapper widgets for your app, please see our other /// package, `stream_chat_flutter_core`. class MyApp extends StatelessWidget { /// Example using Stream's Flutter package. /// /// If you'd prefer using minimal wrapper widgets for your app, please see /// our other package, `stream_chat_flutter_core`. const MyApp({ super.key, required this.client, required this.channel, }); /// Instance of Stream Client. /// /// Stream's [StreamChatClient] can be used to connect to our servers and /// set the default user for the application. Performing these actions /// trigger a websocket connection allowing for real-time updates. final StreamChatClient client; /// Instance of the Channel final Channel channel; @override Widget build(BuildContext context) { return MaterialApp( theme: ThemeData.light(), darkTheme: ThemeData.dark(), // Add all the supported locales supportedLocales: const [ Locale('en'), Locale('hi'), Locale('fr'), Locale('it'), Locale('es'), Locale('ja'), Locale('ko'), // Add support for additional 'nn' locale Locale('nn'), ], // Add overridden "NnStreamChatLocalizations.delegate" along with // "GlobalStreamChatLocalizations.delegates" localizationsDelegates: const [ NnStreamChatLocalizations.delegate, ...GlobalStreamChatLocalizations.delegates, ], builder: (context, widget) => StreamChat( client: client, child: widget, ), home: StreamChannel( channel: channel, child: const ChannelPage(), ), ); } } /// A list of messages sent in the current channel. /// /// This is implemented using [StreamMessageListView], /// a widget that provides query /// functionalities fetching the messages from the api and showing them in a /// listView. class ChannelPage extends StatelessWidget { /// Creates the page that shows the list of messages const ChannelPage({ super.key, }); @override Widget build(BuildContext context) { return const Scaffold( appBar: StreamChannelHeader(), body: Column( children: [ Expanded( child: StreamMessageListView(), ), StreamMessageInput(), ], ), ); } }