Code With Bisky

Send Images in Real-Time: Building a Powerful Chat Application with Flutter and Appwrite Episode [14]

Key Topics covered:

  • Crop Image Before Sending It
  • Send Images in Real-time
  • Display Images

Description:

In this tutorial, we'll explore how to send images using Flutter and Appwrite. If you're a beginner looking to enhance your app development skills, this step-by-step guide is perfect for you. You'll learn how to handle image selection, encode the images, and send them securely to your Appwrite backend. We'll cover the necessary code snippets and explain the underlying concepts, ensuring that you grasp the core concepts behind the implementation.

Add the following image_cropper dependency in your pubspec.yaml


dependencies:
  .........
  image_cropper: ^5.0.0
        
        

Create a bucket and copy an id, add it to your strings

  • lib/constant/strings.dart

static var messagesBucketId = "647fb444a06408602d48";
        
        

Create an extensions file.

  • lib/core/extensions/extensions.dart

Add OnBuildContext extension that extends BuildContext. Implement the logic to pick and crop an image


import 'dart:io';

import 'package:flutter/material.dart';
import 'package:image_cropper/image_cropper.dart';
import 'package:image_picker/image_picker.dart';

extension OnBuildContext on BuildContext{

  Future<File?> pickAndCropImage([double? aspectRatioX, ImageSource? source]) async {

    try{
      final file = await ImagePicker().pickImage(source: source ?? ImageSource.gallery);
      if(file != null){
        CroppedFile? croppedFile = await ImageCropper().cropImage(
          sourcePath: file.path,
          aspectRatio: CropAspectRatio(ratioX: (aspectRatioX ?? 1), ratioY: 1),
          uiSettings: [],
        );
        return croppedFile != null ? File(croppedFile.path) :null;
      }
    }catch(e){
      print(e);
    }
    return null;
  }
}
        
        

Code Snippet(StorageProvider.dart):

Create an StorageProvider.dart. We are using riverpod Provider to manage StorageProvider. We will use it to save media into Appwrite storage.

  • lib/core/providers/StorageProvider.dart

import 'package:appwrite/appwrite.dart';
import 'package:chat_with_bisky/core/providers/AppwriteClientProvider.dart';
import 'package:chat_with_bisky/model/db/ChatRealm.dart';
import 'package:chat_with_bisky/model/db/FriendContactRealm.dart';
import 'package:chat_with_bisky/model/db/MessageRealm.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:realm/realm.dart';

final storageProvider=  Provider((ref) => Storage(ref.read(appwriteClientProvider)));

        
        

Code Snippet(MessageViewModel.dart) modification:

Add method to get file preview and upload media using Appwrite

  • lib/pages/dashboard/chat/MessageViewModel.dart


            // global declaration
Storage get _storage => ref.read(storageProvider);

Future<<Uint8List?> getFilePreview(String fileId) async {
     try{
       return await _storage.getFilePreview(bucketId: Strings.messagesBucketId, fileId: fileId);
    }catch(e){
      print(e);
      return null;
    }
  }
  Future<File?> uploadMedia(String imageId,String filePath) async {
    try{
      state = state.copyWith(loading: true);
      File file = await _storage.createFile(bucketId: Strings.messagesBucketId,
          fileId: imageId,
          file: InputFile(path: filePath,filename: '$imageId.${getFileExtension(filePath)}'));
      state = state.copyWith(loading: false);
      return file;
    }catch(e){
      state = state.copyWith(loading: false);
      print(e);
    }
    return null;
  }
  String getFileExtension(String filePath){
    try{
      return '.${filePath.split('.').last}';
    }catch(e){
      return "";
    }
  }
        
        

Code Snippet(LoadingPageOverlay.dart):

Creating a loading layout to show a loading progress bar

  • lib/widget/LoadingPageOverlay.dart

import 'package:flutter/material.dart';

class LoadingPageOverlay extends StatelessWidget {
  const LoadingPageOverlay({
    Key? key,
    required this.child,
    this.loading = false,
  }) : super(key: key);
  final Widget child;
  final bool loading;
  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final scheme = theme.colorScheme;
    return Stack(
      children: [
        child,
        if (loading)
          Material(
            color: scheme.surfaceVariant.withOpacity(0.5),
            child: const Center(
              child: CircularProgressIndicator(),
            ),
          ),
      ],
    );
  }
}
        

Code Snippet(MessageScreen.dart) modification:

Add code to view and send an image. Let's wrap the Scaffold with LoadingPageOverlay

  • lib/pages/dashboard/chat/MessageScreen.dart


return LoadingPageOverlay(
      loading: messageState?.loading ?? false,
      child: Scaffold(
            ........
            )

// add below logic in pickImage() method and make it async

 void pickImage() async{
    final file = await context.pickAndCropImage(3/4,ImageSource.gallery);
    if(file != null){
      print(file.path);
      File? fileUploaded = await messageNotifier?.uploadMedia(realm.ObjectId().hexString,file.path);
      if(fileUploaded !=null){
        sendMessage("IMAGE", fileUploaded.$id);
      }
    }
  }

// to display image add this logic if message type is IMAGE

if (messageAppwrite.type == 'IMAGE') {

      return FutureBuilder(builder: (context, snapshot) {
        if(snapshot.connectionState == ConnectionState.done){

          if(snapshot.hasData){
            final data = snapshot.data as Uint8List ;
            return InkWell(
              onTap: (){
                print("Image clicked");
              },
              child: Image.memory(data, width: 200,height: 200,),
            );
          }
        }else if(snapshot.connectionState == ConnectionState.waiting){
          return const Text("Loading image....");
        }
        return Container();
      },future: messageNotifier?.getFilePreview(messageAppwrite.message ??''),);

        
        

Conclusion:

We managed to pick an image from gallery and send it. We added a code to upload images in the MessageViewModel.dart. In the next tutorial, we are going to refactor our application and polish up real-time functionality. Don't forget to share and join our Discord Channel. May you please subscribe to our YouTube Channel.