feat: add imagePickerBuilder to builders to override the default imagepicker for selecting an image

This commit is contained in:
Freek van de Ven 2025-02-18 22:12:52 +01:00 committed by Bart Ribbers
parent 0fc0153463
commit 4e9feeadb2
6 changed files with 122 additions and 85 deletions

View file

@ -16,6 +16,7 @@
- Added ChatPaginationControls to the ChatOptions to allow for more control over the pagination
- Fixed that chat message is automatically sent when the user presses enter on the keyboard in the chat input
- Added sender and chatId to uploadImage in the ChatRepositoryInterface
- Added imagePickerBuilder to the builders in the ChatOptions to override the image picker with a custom implementation that needs to return a Future<Uint8List?>
## 4.0.0
- Move to the new user story architecture

View file

@ -1,9 +1,12 @@
import "dart:typed_data";
import "package:chat_repository_interface/chat_repository_interface.dart";
import "package:flutter/material.dart";
import "package:flutter_chat/src/config/chat_translations.dart";
import "package:flutter_chat/src/config/screen_types.dart";
import "package:flutter_chat/src/screens/chat_detail/widgets/default_loader.dart";
import "package:flutter_chat/src/screens/chat_detail/widgets/default_message_builder.dart";
import "package:flutter_chat/src/screens/creation/widgets/default_image_picker.dart";
/// The chat builders
class ChatBuilders {
@ -20,6 +23,7 @@ class ChatBuilders {
this.noUsersPlaceholderBuilder,
this.chatTitleBuilder,
this.chatMessageBuilder = DefaultChatMessageBuilder.builder,
this.imagePickerBuilder = DefaultImagePickerDialog.builder,
this.usernameBuilder,
this.loadingWidgetBuilder = DefaultChatLoadingOverlay.builder,
this.loadingChatMessageBuilder = DefaultChatMessageLoader.builder,
@ -75,6 +79,11 @@ class ChatBuilders {
/// The image picker container builder
final ImagePickerContainerBuilder? imagePickerContainerBuilder;
/// A way to provide your own image picker implementation
/// If not provided the [DefaultImagePicker.builder] will be used which
/// shows a modal buttom sheet with the option for a camera or gallery image
final ImagePickerBuilder imagePickerBuilder;
/// The loading widget builder
/// This is used to build the loading widget that is displayed on the chat
/// screen when loading the chat
@ -100,6 +109,11 @@ typedef ImagePickerContainerBuilder = Widget Function(
ChatTranslations translations,
);
/// Builder definition for providing an image picker implementation
typedef ImagePickerBuilder = Future<Uint8List?> Function(
BuildContext context,
);
/// The text input builder
typedef TextInputBuilder = Widget Function(
BuildContext context,

View file

@ -6,7 +6,7 @@ import "package:flutter/material.dart";
import "package:flutter_chat/src/config/screen_types.dart";
import "package:flutter_chat/src/screens/chat_detail/widgets/chat_bottom.dart";
import "package:flutter_chat/src/screens/chat_detail/widgets/chat_widgets.dart";
import "package:flutter_chat/src/screens/creation/widgets/image_picker.dart";
import "package:flutter_chat/src/screens/creation/widgets/default_image_picker.dart";
import "package:flutter_chat/src/util/scope.dart";
import "package:flutter_hooks/flutter_hooks.dart";

View file

@ -3,7 +3,7 @@ import "dart:typed_data";
import "package:chat_repository_interface/chat_repository_interface.dart";
import "package:flutter/material.dart";
import "package:flutter_chat/src/config/screen_types.dart";
import "package:flutter_chat/src/screens/creation/widgets/image_picker.dart";
import "package:flutter_chat/src/screens/creation/widgets/default_image_picker.dart";
import "package:flutter_chat/src/util/scope.dart";
import "package:flutter_hooks/flutter_hooks.dart";
import "package:flutter_profile/flutter_profile.dart";

View file

@ -0,0 +1,105 @@
import "dart:typed_data";
import "package:flutter/material.dart";
import "package:flutter_chat/src/config/chat_options.dart";
import "package:flutter_chat/src/config/chat_translations.dart";
import "package:flutter_chat/src/util/scope.dart";
import "package:flutter_image_picker/flutter_image_picker.dart";
/// The function to call when the user selects an image
Future<void> onPressSelectImage(
BuildContext context,
ChatOptions options,
Function(Uint8List image) onUploadImage,
) async {
var image = await options.builders.imagePickerBuilder.call(context);
if (image == null) return;
if (!context.mounted) return;
var messenger = ScaffoldMessenger.of(context)
..showSnackBar(
_getImageLoadingSnackbar(options.translations),
)
..activate();
await onUploadImage(image);
await Future.delayed(const Duration(seconds: 1));
messenger.hideCurrentSnackBar();
}
/// Default image picker dialog for selecting an image from the gallery or
/// taking a photo.
class DefaultImagePickerDialog extends StatelessWidget {
/// Creates a new default image picker dialog.
const DefaultImagePickerDialog({
super.key,
});
/// Builds the default image picker dialog.
static Future<Uint8List?> builder(BuildContext context) async =>
showModalBottomSheet<Uint8List?>(
context: context,
builder: (context) => const DefaultImagePickerDialog(),
);
@override
Widget build(BuildContext context) {
var chatScope = ChatScope.of(context);
var options = chatScope.options;
var translations = options.translations;
var theme = Theme.of(context);
var textTheme = theme.textTheme;
return options.builders.imagePickerContainerBuilder?.call(
context,
() => Navigator.of(context).pop(),
translations,
) ??
Container(
padding: const EdgeInsets.all(8.0),
color: Colors.white,
child: ImagePicker(
theme: ImagePickerTheme(
spaceBetweenIcons: 32.0,
iconColor: theme.primaryColor,
title: translations.imagePickerTitle,
titleStyle: textTheme.titleMedium,
iconSize: 60.0,
makePhotoText: translations.takePicture,
selectImageText: translations.uploadFile,
selectImageIcon: Icon(
color: theme.primaryColor,
Icons.insert_drive_file_rounded,
size: 60,
),
closeButtonBuilder: (ontap) => TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text(
translations.cancelImagePickerBtn,
style: textTheme.bodyMedium!.copyWith(
fontSize: 18,
decoration: TextDecoration.underline,
),
),
),
),
),
);
}
}
SnackBar _getImageLoadingSnackbar(ChatTranslations translations) => SnackBar(
duration: const Duration(minutes: 1),
content: Row(
children: [
const SizedBox(
width: 25,
height: 25,
child: CircularProgressIndicator(color: Colors.grey),
),
Padding(
padding: const EdgeInsets.only(left: 16.0),
child: Text(translations.imageUploading),
),
],
),
);

View file

@ -1,83 +0,0 @@
import "dart:typed_data";
import "package:flutter/material.dart";
import "package:flutter_chat/src/config/chat_options.dart";
import "package:flutter_chat/src/config/chat_translations.dart";
import "package:flutter_image_picker/flutter_image_picker.dart";
/// The function to call when the user selects an image
Future<void> onPressSelectImage(
BuildContext context,
ChatOptions options,
Function(Uint8List image) onUploadImage,
) async =>
showModalBottomSheet<Uint8List?>(
context: context,
builder: (BuildContext context) =>
options.builders.imagePickerContainerBuilder?.call(
context,
() => Navigator.of(context).pop(),
options.translations,
) ??
Container(
padding: const EdgeInsets.all(8.0),
color: Colors.white,
child: ImagePicker(
theme: ImagePickerTheme(
spaceBetweenIcons: 32.0,
iconColor: Theme.of(context).primaryColor,
title: options.translations.imagePickerTitle,
titleStyle: Theme.of(context).textTheme.titleMedium,
iconSize: 60.0,
makePhotoText: options.translations.takePicture,
selectImageText: options.translations.uploadFile,
selectImageIcon: Icon(
color: Theme.of(context).primaryColor,
Icons.insert_drive_file_rounded,
size: 60,
),
closeButtonBuilder: (ontap) => TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text(
options.translations.cancelImagePickerBtn,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
fontSize: 18,
decoration: TextDecoration.underline,
),
),
),
),
),
),
).then(
(image) async {
if (image == null) return;
if (!context.mounted) return;
var messenger = ScaffoldMessenger.of(context)
..showSnackBar(
_getImageLoadingSnackbar(options.translations),
)
..activate();
await onUploadImage(image);
Future.delayed(const Duration(seconds: 1), () {
messenger.hideCurrentSnackBar();
});
},
);
SnackBar _getImageLoadingSnackbar(ChatTranslations translations) => SnackBar(
duration: const Duration(minutes: 1),
content: Row(
children: [
const SizedBox(
width: 25,
height: 25,
child: CircularProgressIndicator(color: Colors.grey),
),
Padding(
padding: const EdgeInsets.only(left: 16.0),
child: Text(translations.imageUploading),
),
],
),
);