Live Chat With Pusher Using Provider

Learn how to setup a real-time messaging Flutter App using Pusher API and a Go backend deployed as a containerised web service to GCP Cloud Run. By Wilberforce Uwadiegwu.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 4 of 4 of this article. Click here to view the first page.

Displaying Images

You have to show the user the images they chose and also allow them to remove them. So, head over to messages_screen.dart and import 'dart:io' and 'package:image_picker/image_picker.dart'. Afterward, create a stateless widget below _InputWidget. This widget will render a single image.

class _ImageWidget extends StatelessWidget {
  final XFile file;
  final VoidCallback onRemove;
  final double size;

  const _ImageWidget({
    Key? key,
    required this.onRemove,
    required this.file,
    required this.size,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

Since the images it'll display are local files from the image picker, you don't need to handle image URLs like you did for MessageWidget. Replace the build() of _ImageWidget with:

Widget build(BuildContext context) {
  final imageSize = size - 15;
  return Padding(
    padding: const EdgeInsets.only(left: 5, right: 10),
    child: SizedBox(
      height: size,
      width: size,
      child: Stack(
        clipBehavior: Clip.none,
        children: [
          Positioned(
            top: 15,
            child: ClipRRect(
              borderRadius: BorderRadius.circular(8),
              child: Image.file(
                File(file.path),
                width: imageSize,
                height: imageSize,
                fit: BoxFit.cover,
              ),
            ),
          ),
          Positioned(
            top: -10,
            right: -10,
            child: IconButton(
              onPressed: onRemove,
              icon: const Icon(Icons.cancel),
            ),
          )
        ],
      ),
    ),
  );
}

This will display an image with round edges, with an "x" icon at the top-right.

Next, declare a variable inside build() of _InputWidget, above the return statement.

Widget build(BuildContext context) {
   final imageSize = MediaQuery.of(context).size.width * 0.21;
   ...
}

Still, in _InputWidget, wrap the TextField in a Column. You'll display a horizontal list of images above the text field like so:

Widget build(BuildContext context) {
  ...
  return Transform.translate(
   ...
    child: SafeArea(
      ...
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          SizedBox(
            height: vm.images.isEmpty ? 0 : imageSize,
            child: ListView.builder(
              itemCount: vm.images.length,
              scrollDirection: Axis.horizontal,
              itemBuilder: (ctx, i) {
                final file = vm.images[i];
                return _ImageWidget(
                  onRemove: () => vm.removeImage(i),
                  file: file,
                  size: imageSize,
                );
              },
            ),
          ),
          TextField(
           ...
          ),
        ],
      ),
    ),
  );
}

Add a suffix icon that'll trigger the image picker:

TextField(
    ...
    prefixIcon: IconButton(
    onPressed: vm.pickImages,
    icon: const Icon(Icons.add),
   ),
)

Run the app on both devices and send an image from any of them. You will see something like this:

Screenshot after supporting images

That's all. Great job on completing this tutorial!

Where to Go From Here

The final directory inside the mobile directory contains the full code used in this tutorial, and you can find it in the zipped file you downloaded earlier. You can still download it by clicking Download Materials at the top or bottom of this tutorial.

In this tutorial, you deployed a Golang service on Cloud Run and learned how to use Pusher to implement real-time chat. To make improvements to the memory footprint and performance of the app, one suggestion is to paginate the chat, letting messages load in pages rather than loading all at once. You can improve the app's functionality by adding support for resending messages that failed to send. You could also use AnimatedList instead of ListView to improve the granularity of the entrance of the message widgets. After playing around, remember to delete the project from GCP, so it doesn't incur any charges.

We hope you enjoyed this tutorial. If you have any questions or comments, please join the forum discussion below!