How to share an image on iOS and Android using Flutter?

I would like to share an image using the standard iOS and Android share dialogs. The code below is mostly from https://pub.dartlang.org/packages/share which I am using as a starting point (Dart and Objective-C below only). It currently only splits text.

Instead of the image below, which I'm not sure is the best approach, how would I convert the image to a stream of bytes in Dart and process it in iOS and Android.

dart

static const _kShareChannel = const MethodChannel('example.test.com/share');
Future<Null> shareImage(Image image) {
  assert(image != null);
  return _kShareChannel.invokeMethod('shareImage', image);
}

      

Objective-C

static NSString *const PLATFORM_CHANNEL = @"example.test.com/share";

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [GeneratedPluginRegistrant registerWithRegistry:self];

    FlutterViewController* controller = (FlutterViewController*)self.window.rootViewController;

    FlutterMethodChannel *shareChannel = [FlutterMethodChannel methodChannelWithName:PLATFORM_CHANNEL
                            binaryMessenger:controller];

    [shareChannel setMethodCallHandler:^(FlutterMethodCall *call, FlutterResult result) {
        if ([@"shareImage" isEqualToString:call.method]) {
            [self share:call.arguments withController:[UIApplication sharedApplication].keyWindow.rootViewController];
            result(nil);
        } else {
            result([FlutterError errorWithCode:@"UNKNOWN_METHOD"
                                       message:@"Unknown share method called"
                                       details:nil]);
        }
    }];
    return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

- (void)share:(id)sharedItems withController:(UIViewController *)controller {
    UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:@[ sharedItems ]
                                  applicationActivities:nil];
    [controller presentViewController:activityViewController animated:YES completion:nil];
}

      

+16


source to share


6 answers


Below you will be able to send a file (specifically the image in this example) using UIActivityViewController

on iOS and as a UIActivityViewController

resource on Android.

FileProvider overview (Android)

Update pubspec.yaml

to link to your image if it's local (in this example, image.jpg) and use the plugin path_provider

to access the filesystem. https://pub.dartlang.org/packages/path_provider

main.dart

import 'dart:io';
import 'dart:typed_data';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:path_provider/path_provider.dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Share Demo',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new MyHomePage(title: 'Share Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  @override
  Widget build(BuildContext context) {

    return new Scaffold(
      appBar: new AppBar(
        title: new Text(widget.title),
      ),
      body: new Center(
        child: new Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
          ],
        ),
      ),
      floatingActionButton: new FloatingActionButton(
        onPressed: _shareImage,
        tooltip: 'Share',
        child: new Icon(Icons.share),
      ),
    );
  }

  _shareImage() async {
    try {
      final ByteData bytes = await rootBundle.load('assets/image.jpg');
      final Uint8List list = bytes.buffer.asUint8List();

      final tempDir = await getTemporaryDirectory();
      final file = await new File('${tempDir.path}/image.jpg').create();
      file.writeAsBytesSync(list);

      final channel = const MethodChannel('channel:me.albie.share/share');
      channel.invokeMethod('shareFile', 'image.jpg');

    } catch (e) {
      print('Share error: $e');
    }
  }
}

      

AppDelegate.m

#include "AppDelegate.h"
#include "GeneratedPluginRegistrant.h"

@implementation AppDelegate

static NSString *const SHARE_CHANNEL = @"channel:me.albie.share/share";

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [GeneratedPluginRegistrant registerWithRegistry:self];
    FlutterViewController* controller = (FlutterViewController*)self.window.rootViewController;

    FlutterMethodChannel *shareChannel =
    [FlutterMethodChannel methodChannelWithName:SHARE_CHANNEL
                                binaryMessenger:controller];

    [shareChannel setMethodCallHandler:^(FlutterMethodCall *call, FlutterResult result) {
        if ([@"shareFile" isEqualToString:call.method]) {
            [self shareFile:call.arguments
             withController:[UIApplication sharedApplication].keyWindow.rootViewController];
        }
    }];

    return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

- (void)shareFile:(id)sharedItems withController:(UIViewController *)controller {
    NSMutableString *filePath = [NSMutableString stringWithString:sharedItems];
    NSString *docsPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0];
    NSString *imagePath = [docsPath stringByAppendingPathComponent:filePath];
    NSURL *imageUrl = [NSURL fileURLWithPath:imagePath];
    NSData *imageData = [NSData dataWithContentsOfURL:imageUrl];
    UIImage *shareImage = [UIImage imageWithData:imageData];

    UIActivityViewController *activityViewController =
    [[UIActivityViewController alloc] initWithActivityItems:@[ shareImage ]
                                      applicationActivities:nil];
    [controller presentViewController:activityViewController animated:YES completion:nil];
}

@end

      

MainActivity.java



package com.example.share;

import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;

import java.io.File;

import io.flutter.app.FlutterActivity;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugins.GeneratedPluginRegistrant;

import android.support.v4.content.FileProvider;

public class MainActivity extends FlutterActivity {

    private static final String SHARE_CHANNEL = "channel:me.albie.share/share";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        GeneratedPluginRegistrant.registerWith(this);

        new MethodChannel(this.getFlutterView(), SHARE_CHANNEL).setMethodCallHandler(new MethodChannel.MethodCallHandler() {
            public final void onMethodCall(MethodCall methodCall, MethodChannel.Result result) {
                if (methodCall.method.equals("shareFile")) {
                    shareFile((String) methodCall.arguments);
                }
            }
        });
    }

    private void shareFile(String path) {
        File imageFile = new File(this.getApplicationContext().getCacheDir(), path);
        Uri contentUri = FileProvider.getUriForFile(this, "me.albie.share", imageFile);
        Intent shareIntent = new Intent(Intent.ACTION_SEND);
        shareIntent.setType("image/jpg");
        shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri);
        this.startActivity(Intent.createChooser(shareIntent, "Share image using"));
    }
}

      

AndroidManifest.xml

<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="me.albie.share"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths" />
</provider>

      

XML / file_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <cache-path name="images" path="/"/>
</paths>

      

build.gradle (app)

dependencies {
    ...
    implementation 'com.android.support:support-v4:27.1.1'
}

      

+26


source


We have added this functionality to the plugin: https://pub.dartlang.org/packages/esys_flutter_share .

Dart:



final ByteData bytes = await rootBundle.load('assets/image1.png');
await Share.file('esys image', 'esys.png', bytes.buffer.asUint8List(), 'image/png');

      

+4


source


I would suggest using the following flutter plugin:

https://pub.dartlang.org/packages/share

The exchange of texts is quite simple:

Share.share('Text I wish to share');

      

For Image: Better to convert the image to Base64 string and send as string.

+3


source


If the image file is loaded, I would recommend saving it to a temporary file in Dart.

await new File('${systemTempDir.path}/foo.jpg').create();

      

Then you can call UIActivityViewController

with a URL representing the name of the image file. Here is a sample code example on how to do this in a non-flutter application that should get you started.

If your image file is built dynamically (for example using an API Canvas

), you may be wondering how to encode ui.Image

into an image file. The Flutter engine currently does not provide the ability to do this, it is possible that the engine will be changed to add this support. You can see how screenshot support is implemented for inspiration, but it won't be trivial.

+1


source


Thanks @ albert-lardizabal for the above code, it works just fine! I had to translate it to Swift and Kotlin, so here's the code in case you need it:

Swift:

override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?
    ) -> Bool {
    GeneratedPluginRegistrant.register(with: self)


    let shareChannelName = "channel:me.albie.share/share";
    let controller:FlutterViewController = self.window?.rootViewController as! FlutterViewController;
    let shareChannel:FlutterMethodChannel = FlutterMethodChannel.init(name: shareChannelName, binaryMessenger: controller);

    shareChannel.setMethodCallHandler({
        (call: FlutterMethodCall, result: FlutterResult) -> Void in
        if (call.method == "shareFile") {
            self.shareFile(sharedItems: call.arguments!,controller: controller);
        }
    });


    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}

func shareFile(sharedItems:Any, controller:UIViewController) {
    let filePath:NSMutableString = NSMutableString.init(string: sharedItems as! String);
    let docsPath:NSString = (NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.cachesDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)[0]) as NSString;
    let imagePath = docsPath.appendingPathComponent(filePath as String);
    let imageUrl = URL.init(fileURLWithPath: imagePath, relativeTo: nil);
    do {
        let imageData = try Data.init(contentsOf: imageUrl);
        let shareImage = UIImage.init(data: imageData);
        let activityViewController:UIActivityViewController = UIActivityViewController.init(activityItems: [shareImage!], applicationActivities: nil);
        controller.present(activityViewController, animated: true, completion: nil);
    } catch let error {
        print(error.localizedDescription);
    }
}

      

Kotlin:

    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    GeneratedPluginRegistrant.registerWith(this)

    MethodChannel(flutterView,"channel:me.albie.share/share").setMethodCallHandler { methodCall, _ ->
        if (methodCall.method == "shareFile") {
            shareFile(methodCall.arguments as String)
        }
    }
}

private fun shareFile(path:String) {
    val imageFile = File(this.applicationContext.cacheDir,path)
    val contentUri = FileProvider.getUriForFile(this,"me.albie.share",imageFile)

    val shareIntent = Intent()
    shareIntent.action = Intent.ACTION_SEND
    shareIntent.type="image/jpg"
    shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri)
    startActivity(Intent.createChooser(shareIntent,"Compartir usando"))
}

      

+1


source


Try using wc_flutter_share .

https://pub.dev/packages/wc_flutter_share

This plugin supports image, text and theme sharing. A unique feature of this plugin is that it also supports sharing image and text at the same time, which other plugins do not support at the time I write this answer.

0


source







All Articles