Skip to content

Commit

Permalink
feat: ✨add default avatar profile image, cached network image for net…
Browse files Browse the repository at this point in the history
…work images and image type to support profile image as asset, network or base64 data (#172)

* feat: ✨add default avatar profile image, cached network image for network images and image type to support profile image as asset, network or base64 data
  • Loading branch information
apurva010 authored Jun 4, 2024
1 parent afc872c commit 585e943
Show file tree
Hide file tree
Showing 18 changed files with 389 additions and 69 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
## [2.0.0] (Unreleased)

* **Feat**: [156](https://github.com/SimformSolutionsPvtLtd/flutter_chatview/pull/156) Added
default avatar, error builder for asset, network and base64 profile image and
cached_network_image for network images.
* **Breaking**: [173](https://github.com/SimformSolutionsPvtLtd/flutter_chatview/pull/173) Added
callback to sort message in chat.
* **Fix**: [181](https://github.com/SimformSolutionsPvtLtd/flutter_chatview/pull/181) Removed
Expand Down
51 changes: 49 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -702,7 +702,54 @@ ChatView(
)
```

30. Added a `replyMessageBuilder` to customize view for the reply.
30. Add default avatar for profile image `defaultAvatarImage`,
error builder for asset and network profile image `assetImageErrorBuilder` `networkImageErrorBuilder`,
Enum `ImageType` to define image as asset, network or base64 data.
```dart
ChatView(
...
appBar: ChatViewAppBar(
defaultAvatarImage: defaultAvatar,
imageType: ImageType.network,
networkImageErrorBuilder: (context, url, error) {
return Center(
child: Text('Error $error'),
);
},
assetImageErrorBuilder: (context, error, stackTrace) {
return Center(
child: Text('Error $error'),
);
},
),
...
),
```


31. Added a `customMessageReplyViewBuilder` to customize reply message view for custom type message.

```dart
ChatView(
...
messageConfig: MessageConfiguration(
customMessageBuilder: (ReplyMessage state) {
return Text(
state.message,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontSize: 12,
color: Colors.black,
),
);
},
),
...
)
```

32. Added a `replyMessageBuilder` to customize view for the reply.

```dart
ChatView(
Expand Down Expand Up @@ -766,7 +813,7 @@ ChatView(
```


30. Added callback `messageSorter` to sort message in `ChatBackgroundConfiguration`.
33. Added callback `messageSorter` to sort message in `ChatBackgroundConfiguration`.
```dart
ChatView(
Expand Down
15 changes: 11 additions & 4 deletions lib/src/extensions/extensions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
*/
import 'package:chatview/chatview.dart';
import 'package:chatview/src/widgets/chat_view_inherited_widget.dart';
import 'package:chatview/src/widgets/profile_image_widget.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import '../utils/constants/constants.dart';
Expand Down Expand Up @@ -74,12 +75,18 @@ extension ValidateString on String {
double? profileCircleRadius,
EdgeInsets? profileCirclePadding,
}) {
final user = getChatUser(this);
return Padding(
padding: profileCirclePadding ?? const EdgeInsets.only(left: 4),
child: CircleAvatar(
radius: profileCircleRadius ?? 8,
backgroundImage:
NetworkImage(getChatUser(this)?.profilePhoto ?? profileImage),
child: ProfileImageWidget(
imageUrl: user?.profilePhoto,
imageType: user?.imageType,
defaultAvatarImage: user?.defaultAvatarImage ?? profileImage,
circleRadius: profileCircleRadius ?? 8,
assetImageErrorBuilder: user?.assetImageErrorBuilder,
networkImageErrorBuilder: user?.networkImageErrorBuilder,
networkImageProgressIndicatorBuilder:
user?.networkImageProgressIndicatorBuilder,
),
);
}
Expand Down
37 changes: 30 additions & 7 deletions lib/src/models/chat_user.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,40 +19,63 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

import '../utils/constants/constants.dart';
import '../values/enumeration.dart';
import '../values/typedefs.dart';

class ChatUser {
/// Provides id of user.
final String id;

/// Provides name of user.
final String name;

/// Provides profile picture URL of user.
/// Provides profile picture as network URL or asset of user.
/// Or
/// Provides profile picture's data in base64 string.
/// This will be determined by [isProfilePhotoInBase64].
final String? profilePhoto;

/// To check whether profile photo is in base64 or network url.
final bool? isProfilePhotoInBase64;
/// Field to set default image if network url for profile image not provided
final String defaultAvatarImage;

/// Field to define image type [network, asset or base64]
final ImageType imageType;

/// Error builder to build error widget for asset image
final AssetImageErrorBuilder? assetImageErrorBuilder;

/// Error builder to build error widget for network image
final NetworkImageErrorBuilder? networkImageErrorBuilder;

/// Progress indicator builder for network image
final NetworkImageProgressIndicatorBuilder?
networkImageProgressIndicatorBuilder;

ChatUser({
required this.id,
required this.name,
this.profilePhoto,
this.isProfilePhotoInBase64,
this.defaultAvatarImage = profileImage,
this.imageType = ImageType.network,
this.assetImageErrorBuilder,
this.networkImageErrorBuilder,
this.networkImageProgressIndicatorBuilder,
});

factory ChatUser.fromJson(Map<String, dynamic> json) => ChatUser(
id: json["id"],
name: json["name"],
profilePhoto: json["profilePhoto"],
isProfilePhotoInBase64: json["isProfilePhotoInBase64"],
imageType: json["imageType"],
defaultAvatarImage: json["defaultAvatarImage"],
);

Map<String, dynamic> toJson() => {
'id': id,
'name': name,
'profilePhoto': profilePhoto,
'isProfilePhotoInBase64': isProfilePhotoInBase64,
'imageType': imageType,
'defaultAvatarImage': defaultAvatarImage,
};
}
27 changes: 22 additions & 5 deletions lib/src/models/profile_circle.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@
import 'package:flutter/material.dart';

import '../../chatview.dart';
import '../utils/constants/constants.dart';

class ProfileCircleConfiguration {
/// Used to give padding to profile circle.
final EdgeInsetsGeometry? padding;

/// Provides image url of user.
/// Provides image url as network or asset of user.
/// Or
/// Provides image data of user in base64
/// This will be determined by [isProfilePhotoInBase64].
final String? profileImageUrl;

/// Used for give bottom padding to profile circle
Expand All @@ -45,8 +45,21 @@ class ProfileCircleConfiguration {
/// Provides callback when user long press on profile circle.
final void Function(ChatUser)? onAvatarLongPress;

/// To check whether profile photo is in base64 or network url.
final bool? isProfilePhotoInBase64;
/// Field to define image type [network, asset or base64]
final ImageType imageType;

/// Field to set default avatar image if profile image link not provided
final String defaultAvatarImage;

/// Error builder to build error widget for asset image
final AssetImageErrorBuilder? assetImageErrorBuilder;

/// Error builder to build error widget for network image
final NetworkImageErrorBuilder? networkImageErrorBuilder;

/// Progress indicator builder for network image
final NetworkImageProgressIndicatorBuilder?
networkImageProgressIndicatorBuilder;

const ProfileCircleConfiguration({
this.onAvatarTap,
Expand All @@ -55,6 +68,10 @@ class ProfileCircleConfiguration {
this.bottomPadding,
this.circleRadius,
this.onAvatarLongPress,
this.isProfilePhotoInBase64,
this.imageType = ImageType.network,
this.defaultAvatarImage = profileImage,
this.networkImageErrorBuilder,
this.assetImageErrorBuilder,
this.networkImageProgressIndicatorBuilder,
});
}
12 changes: 12 additions & 0 deletions lib/src/values/enumeration.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,18 @@ enum ChatViewState { hasMessages, noData, loading, error }

enum ShowReceiptsIn { all, lastMessage }

enum ImageType {
asset,
network,
base64;

bool get isNetwork => this == ImageType.network;

bool get isAsset => this == ImageType.asset;

bool get isBase64 => this == ImageType.base64;
}

extension ChatViewStateExtension on ChatViewState {
bool get hasMessages => this == ChatViewState.hasMessages;

Expand Down
16 changes: 16 additions & 0 deletions lib/src/values/typedefs.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import 'package:cached_network_image/cached_network_image.dart';
import 'package:chatview/chatview.dart';
import 'package:flutter/material.dart';

Expand Down Expand Up @@ -67,3 +68,18 @@ typedef CustomViewForReplyMessage = Widget Function(
ReplyMessage state,
);
typedef GetMessageSeparator = (Map<int, DateTime>, DateTime);
typedef AssetImageErrorBuilder = Widget Function(
BuildContext context,
Object error,
StackTrace? stackTrace,
);
typedef NetworkImageErrorBuilder = Widget Function(
BuildContext context,
String url,
Object error,
);
typedef NetworkImageProgressIndicatorBuilder = Widget Function(
BuildContext context,
String url,
DownloadProgress progress,
);
16 changes: 14 additions & 2 deletions lib/src/widgets/chat_bubble_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,13 @@ class _ChatBubbleWidgetState extends State<ChatBubbleWidget> {
: profileCircleConfig?.bottomPadding ?? 2,
profileCirclePadding: profileCircleConfig?.padding,
imageUrl: messagedUser?.profilePhoto,
isProfilePhotoInBase64: messagedUser?.isProfilePhotoInBase64,
imageType: messagedUser?.imageType,
defaultAvatarImage:
messagedUser?.defaultAvatarImage ?? profileImage,
networkImageProgressIndicatorBuilder:
messagedUser?.networkImageProgressIndicatorBuilder,
assetImageErrorBuilder: messagedUser?.assetImageErrorBuilder,
networkImageErrorBuilder: messagedUser?.networkImageErrorBuilder,
circleRadius: profileCircleConfig?.circleRadius,
onTap: () => _onAvatarTap(messagedUser),
onLongPress: () => _onAvatarLongPress(messagedUser),
Expand Down Expand Up @@ -232,7 +238,13 @@ class _ChatBubbleWidgetState extends State<ChatBubbleWidget> {
: profileCircleConfig?.bottomPadding ?? 2,
profileCirclePadding: profileCircleConfig?.padding,
imageUrl: currentUser?.profilePhoto,
isProfilePhotoInBase64: currentUser?.isProfilePhotoInBase64,
imageType: currentUser?.imageType,
defaultAvatarImage:
currentUser?.defaultAvatarImage ?? profileImage,
networkImageProgressIndicatorBuilder:
currentUser?.networkImageProgressIndicatorBuilder,
networkImageErrorBuilder: currentUser?.networkImageErrorBuilder,
assetImageErrorBuilder: currentUser?.assetImageErrorBuilder,
circleRadius: profileCircleConfig?.circleRadius,
onTap: () => _onAvatarTap(messagedUser),
onLongPress: () => _onAvatarLongPress(messagedUser),
Expand Down
6 changes: 0 additions & 6 deletions lib/src/widgets/chat_groupedlist_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -196,9 +196,6 @@ class _ChatGroupedListWidgetState extends State<ChatGroupedListWidget>
typeIndicatorConfig: widget.typeIndicatorConfig,
chatBubbleConfig: chatBubbleConfig?.inComingChatBubbleConfig,
showIndicator: widget.showTypingIndicator,
profilePic: profileCircleConfig?.profileImageUrl,
isProfilePhotoInBase64:
profileCircleConfig?.isProfilePhotoInBase64,
)
: ValueListenableBuilder(
valueListenable: ChatViewInheritedWidget.of(context)!
Expand All @@ -209,9 +206,6 @@ class _ChatGroupedListWidgetState extends State<ChatGroupedListWidget>
chatBubbleConfig:
chatBubbleConfig?.inComingChatBubbleConfig,
showIndicator: value,
profilePic: profileCircleConfig?.profileImageUrl,
isProfilePhotoInBase64:
profileCircleConfig?.isProfilePhotoInBase64,
)),
SizedBox(
height: (MediaQuery.of(context).size.width *
Expand Down
1 change: 1 addition & 0 deletions lib/src/widgets/chat_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ class _ChatViewState extends State<ChatView>
chatController: chatController,
featureActiveConfig: featureActiveConfig,
currentUser: widget.currentUser,
profileCircleConfiguration: widget.profileCircleConfig,
child: Container(
height:
chatBackgroundConfig.height ?? MediaQuery.of(context).size.height,
Expand Down
35 changes: 32 additions & 3 deletions lib/src/widgets/chat_view_appbar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ import 'package:flutter/foundation.dart' show kIsWeb;

import 'package:flutter/material.dart';

import '../values/typedefs.dart';
import '../../chatview.dart';
import '../utils/constants/constants.dart';
import 'profile_image_widget.dart';

class ChatViewAppBar extends StatelessWidget {
const ChatViewAppBar({
Expand All @@ -42,6 +44,11 @@ class ChatViewAppBar extends StatelessWidget {
this.padding,
this.leading,
this.showLeading = true,
this.defaultAvatarImage = profileImage,
this.assetImageErrorBuilder,
this.networkImageErrorBuilder,
this.imageType = ImageType.network,
this.networkImageProgressIndicatorBuilder,
}) : super(key: key);

/// Allow user to change colour of appbar.
Expand Down Expand Up @@ -83,6 +90,22 @@ class ChatViewAppBar extends StatelessWidget {
/// Allow user to turn on/off leading icon.
final bool showLeading;

/// Field to set default image if network url for profile image not provided
final String defaultAvatarImage;

/// Error builder to build error widget for asset image
final AssetImageErrorBuilder? assetImageErrorBuilder;

/// Error builder to build error widget for network image
final NetworkImageErrorBuilder? networkImageErrorBuilder;

/// Field to define image type [network, asset or base64]
final ImageType imageType;

/// Progress indicator builder for network image
final NetworkImageProgressIndicatorBuilder?
networkImageProgressIndicatorBuilder;

@override
Widget build(BuildContext context) {
return Material(
Expand Down Expand Up @@ -113,8 +136,14 @@ class ChatViewAppBar extends StatelessWidget {
if (profilePicture != null)
Padding(
padding: const EdgeInsets.only(right: 8.0),
child: CircleAvatar(
backgroundImage: NetworkImage(profilePicture!)),
child: ProfileImageWidget(
imageUrl: profilePicture,
defaultAvatarImage: defaultAvatarImage,
assetImageErrorBuilder: assetImageErrorBuilder,
networkImageErrorBuilder: networkImageErrorBuilder,
imageType: imageType,
networkImageProgressIndicatorBuilder: networkImageProgressIndicatorBuilder,
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
Expand Down
Loading

0 comments on commit 585e943

Please sign in to comment.