Fix share on Android by moving to flutter share lib (#87)
Co-authored-by: Nate Brown <nbrown.us@gmail.com>
This commit is contained in:
parent
c7a53c3905
commit
a5684e1978
|
@ -26,7 +26,7 @@ apply plugin: 'kotlin-android'
|
|||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||
|
||||
android {
|
||||
compileSdkVersion flutter.compileSdkVersion
|
||||
compileSdkVersion 33
|
||||
ndkVersion flutter.ndkVersion
|
||||
|
||||
compileOptions {
|
||||
|
|
|
@ -84,9 +84,6 @@ class MainActivity: FlutterActivity() {
|
|||
"active.setRemoteForTunnel" -> activeSetRemoteForTunnel(call, result)
|
||||
"active.closeTunnel" -> activeCloseTunnel(call, result)
|
||||
|
||||
"share" -> Share.share(call, result)
|
||||
"shareFile" -> Share.shareFile(call, result)
|
||||
|
||||
else -> result.notImplemented()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,134 +0,0 @@
|
|||
package net.defined.mobile_nebula
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.content.*
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.pm.ResolveInfo
|
||||
import android.util.Log
|
||||
import androidx.core.content.FileProvider
|
||||
import io.flutter.plugin.common.MethodCall
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
import java.io.File
|
||||
|
||||
class Share {
|
||||
companion object {
|
||||
fun share(call: MethodCall, result: MethodChannel.Result) {
|
||||
val title = call.argument<String>("title")
|
||||
val text = call.argument<String>("text")
|
||||
val filename = call.argument<String>("filename")
|
||||
|
||||
if (filename == null || filename.isEmpty()) {
|
||||
return result.error("filename was not provided", null, null)
|
||||
}
|
||||
|
||||
try {
|
||||
val context = MainActivity!!.getContext()!!
|
||||
val cacheDir = context.cacheDir.resolve("share")
|
||||
cacheDir.deleteRecursively()
|
||||
cacheDir.mkdir()
|
||||
val newFile = cacheDir.resolve(filename!!)
|
||||
newFile.delete()
|
||||
newFile.writeText(text ?: "")
|
||||
pop(title, newFile, result)
|
||||
|
||||
} catch (err: Exception) {
|
||||
Log.println(Log.ERROR, "", "Share: Error")
|
||||
result.error(err.message ?: "Unknown error", null, null)
|
||||
}
|
||||
}
|
||||
|
||||
fun shareFile(call: MethodCall, result: MethodChannel.Result) {
|
||||
val title = call.argument<String>("title")
|
||||
val filename = call.argument<String>("filename")
|
||||
val filePath = call.argument<String>("filePath")
|
||||
|
||||
if (filename == null || filename.isEmpty()) {
|
||||
result.error("filename was not provided", null, null)
|
||||
return
|
||||
}
|
||||
|
||||
if (filePath == null || filePath.isEmpty()) {
|
||||
result.error("filePath was not provided", null, null)
|
||||
return
|
||||
}
|
||||
|
||||
val file = File(filePath)
|
||||
|
||||
try {
|
||||
val context = MainActivity!!.getContext()!!
|
||||
val cacheDir = context.cacheDir.resolve("share")
|
||||
cacheDir.deleteRecursively()
|
||||
cacheDir.mkdir()
|
||||
val newFile = cacheDir.resolve(filename!!)
|
||||
newFile.delete()
|
||||
file.copyTo(newFile)
|
||||
|
||||
pop(title, newFile, result)
|
||||
|
||||
} catch (err: Exception) {
|
||||
Log.println(Log.ERROR, "", "Share: Error")
|
||||
result.error(err.message ?: "Unknown error", null, null)
|
||||
}
|
||||
}
|
||||
|
||||
private fun pop(title: String?, file: File, result: MethodChannel.Result) {
|
||||
if (title == null || title.isEmpty()) {
|
||||
result.error("title was not provided", null, null)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
val context = MainActivity!!.getContext()!!
|
||||
|
||||
val fileUri = FileProvider.getUriForFile(context, context.applicationContext.packageName + ".provider", file)
|
||||
val intent = Intent()
|
||||
|
||||
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
|
||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
intent.action = Intent.ACTION_SEND
|
||||
intent.type = "text/*"
|
||||
|
||||
intent.putExtra(Intent.EXTRA_SUBJECT, title)
|
||||
intent.putExtra(Intent.EXTRA_STREAM, fileUri)
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
|
||||
val receiver = Intent(context, ShareReceiver::class.java)
|
||||
receiver.putExtra(Intent.EXTRA_TEXT, file)
|
||||
val pendingIntent = PendingIntent.getBroadcast(context, 0, receiver, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
|
||||
val chooserIntent = Intent.createChooser(intent, title, pendingIntent.intentSender)
|
||||
val resInfoList: List<ResolveInfo> = context.packageManager.queryIntentActivities(chooserIntent, PackageManager.MATCH_DEFAULT_ONLY)
|
||||
for (resolveInfo in resInfoList) {
|
||||
val packageName: String = resolveInfo.activityInfo.packageName
|
||||
context.grantUriPermission(packageName, fileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
}
|
||||
|
||||
context.startActivity(chooserIntent)
|
||||
|
||||
} catch (err: Exception) {
|
||||
Log.println(Log.ERROR, "", "Share: Error")
|
||||
return result.error(err.message ?: "Unknown error", null, null)
|
||||
}
|
||||
|
||||
result.success(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ShareReceiver : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
if (intent == null) {
|
||||
return
|
||||
}
|
||||
|
||||
val res = intent.extras!!.get(Intent.EXTRA_CHOSEN_COMPONENT) as? ComponentName ?: return
|
||||
when (res.className) {
|
||||
"org.chromium.arc.intent_helper.SendTextToClipboardActivity" -> {
|
||||
val file = intent.extras!![Intent.EXTRA_TEXT] as? File ?: return
|
||||
val clipboard = context?.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
|
||||
clipboard.setPrimaryClip(ClipData.newPlainText("", file.readText()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -43,6 +43,8 @@ PODS:
|
|||
- SDWebImage (5.13.3):
|
||||
- SDWebImage/Core (= 5.13.3)
|
||||
- SDWebImage/Core (5.13.3)
|
||||
- share_plus (0.0.1):
|
||||
- Flutter
|
||||
- SwiftyGif (5.4.3)
|
||||
- SwiftyJSON (5.0.1)
|
||||
- url_launcher_ios (0.0.1):
|
||||
|
@ -54,6 +56,7 @@ DEPENDENCIES:
|
|||
- flutter_barcode_scanner (from `.symlinks/plugins/flutter_barcode_scanner/ios`)
|
||||
- package_info (from `.symlinks/plugins/package_info/ios`)
|
||||
- path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`)
|
||||
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
||||
- SwiftyJSON (~> 5.0)
|
||||
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
||||
|
||||
|
@ -76,6 +79,8 @@ EXTERNAL SOURCES:
|
|||
:path: ".symlinks/plugins/package_info/ios"
|
||||
path_provider_ios:
|
||||
:path: ".symlinks/plugins/path_provider_ios/ios"
|
||||
share_plus:
|
||||
:path: ".symlinks/plugins/share_plus/ios"
|
||||
url_launcher_ios:
|
||||
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
||||
|
||||
|
@ -88,6 +93,7 @@ SPEC CHECKSUMS:
|
|||
package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62
|
||||
path_provider_ios: 7d7ce634493af4477d156294792024ec3485acd5
|
||||
SDWebImage: af5bbffef2cde09f148d826f9733dcde1a9414cd
|
||||
share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68
|
||||
SwiftyGif: 6c3eafd0ce693cad58bb63d2b2fb9bacb8552780
|
||||
SwiftyJSON: 2f33a42c6fbc52764d96f13368585094bfd8aa5e
|
||||
url_launcher_ios: 02f1989d4e14e998335b02b67a7590fa34f971af
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
43AA89572444DA6500EDC39C /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43AA89562444DA6500EDC39C /* PacketTunnelProvider.swift */; };
|
||||
43AA895C2444DA6500EDC39C /* NebulaNetworkExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 43AA89542444DA6500EDC39C /* NebulaNetworkExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
43AA89622444DAA500EDC39C /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43AA894E2444D8BC00EDC39C /* NetworkExtension.framework */; };
|
||||
43AD63F424EB3802000FB47E /* Share.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43AD63F324EB3802000FB47E /* Share.swift */; };
|
||||
43ED87842912D0DD004DAFC5 /* DNUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43ED87832912D0DD004DAFC5 /* DNUpdate.swift */; };
|
||||
43ED87852912D0DD004DAFC5 /* DNUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43ED87832912D0DD004DAFC5 /* DNUpdate.swift */; };
|
||||
4CF2F06A02A63B862C9F6F03 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 384887B4785D38431E800D3A /* Pods_Runner.framework */; };
|
||||
|
@ -89,7 +88,6 @@
|
|||
43AA89562444DA6500EDC39C /* PacketTunnelProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PacketTunnelProvider.swift; sourceTree = "<group>"; };
|
||||
43AA89582444DA6500EDC39C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
43AA89592444DA6500EDC39C /* NebulaNetworkExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NebulaNetworkExtension.entitlements; sourceTree = "<group>"; };
|
||||
43AD63F324EB3802000FB47E /* Share.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Share.swift; sourceTree = "<group>"; };
|
||||
43B66ECA245A0C8400B18C36 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; };
|
||||
43B66ECC245A146300B18C36 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
|
||||
43ED87832912D0DD004DAFC5 /* DNUpdate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DNUpdate.swift; sourceTree = "<group>"; };
|
||||
|
@ -210,7 +208,6 @@
|
|||
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
|
||||
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
|
||||
43871C9C2444E2EC004F9075 /* Sites.swift */,
|
||||
43AD63F324EB3802000FB47E /* Share.swift */,
|
||||
43ED87832912D0DD004DAFC5 /* DNUpdate.swift */,
|
||||
BE45F625291AEAB300902884 /* PackageInfo.swift */,
|
||||
BE5BC105291C41E600B6FE5B /* APIClient.swift */,
|
||||
|
@ -362,6 +359,7 @@
|
|||
"${BUILT_PRODUCTS_DIR}/flutter_barcode_scanner/flutter_barcode_scanner.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/package_info/package_info.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/path_provider_ios/path_provider_ios.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/share_plus/share_plus.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/url_launcher_ios/url_launcher_ios.framework",
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
|
@ -375,6 +373,7 @@
|
|||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_barcode_scanner.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/package_info.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider_ios.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/share_plus.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/url_launcher_ios.framework",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
@ -476,7 +475,6 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
|
||||
43AD63F424EB3802000FB47E /* Share.swift in Sources */,
|
||||
432D0E3E291C562200752563 /* SiteList.swift in Sources */,
|
||||
43871C9D2444E2EC004F9075 /* Sites.swift in Sources */,
|
||||
BE5BC106291C41E600B6FE5B /* APIClient.swift in Sources */,
|
||||
|
|
|
@ -68,9 +68,6 @@ func MissingArgumentError(message: String, details: Any?) -> FlutterError {
|
|||
case "active.setRemoteForTunnel": self.vpnRequest(command: "setRemoteForTunnel", arguments: call.arguments, result: result)
|
||||
case "active.closeTunnel": self.vpnRequest(command: "closeTunnel", arguments: call.arguments, result: result)
|
||||
|
||||
case "share": Share.share(call: call, result: result)
|
||||
case "shareFile": Share.shareFile(call: call, result: result)
|
||||
|
||||
default:
|
||||
result(FlutterMethodNotImplemented)
|
||||
}
|
||||
|
|
|
@ -1,150 +0,0 @@
|
|||
// Basis of this code comes from https://github.com/lubritto/flutter_share
|
||||
|
||||
import Flutter
|
||||
import UIKit
|
||||
|
||||
public class Share {
|
||||
public static func share(call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
let args = call.arguments as? [String: Any?]
|
||||
|
||||
let title = args!["title"] as? String
|
||||
let text = args!["text"] as? String
|
||||
let filename = args!["filename"] as? String
|
||||
let tmpDirURL = FileManager.default.temporaryDirectory
|
||||
|
||||
if (filename == nil || filename!.isEmpty) {
|
||||
return result(false)
|
||||
}
|
||||
|
||||
let tmpFile = tmpDirURL.appendingPathComponent(filename!)
|
||||
do {
|
||||
try text?.write(to: tmpFile, atomically: true, encoding: .utf8)
|
||||
} catch {
|
||||
//TODO: return error
|
||||
return result(false)
|
||||
}
|
||||
|
||||
pop(title: title, file: tmpFile) { pass in
|
||||
let fm = FileManager()
|
||||
do {
|
||||
try fm.removeItem(at: tmpFile)
|
||||
} catch {}
|
||||
|
||||
return result(pass)
|
||||
}
|
||||
}
|
||||
|
||||
public static func shareFile(call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
let args = call.arguments as? [String: Any?]
|
||||
|
||||
let title = args!["title"] as? String
|
||||
let filePath = args!["filePath"] as? String
|
||||
let filename = args!["filename"] as? String
|
||||
|
||||
if (filePath == nil || filePath!.isEmpty) {
|
||||
return result(false)
|
||||
}
|
||||
|
||||
var tmpFile: URL?
|
||||
let fm = FileManager()
|
||||
var realPath = URL(fileURLWithPath: filePath!)
|
||||
|
||||
if (filename != nil && !filename!.isEmpty) {
|
||||
tmpFile = FileManager.default.temporaryDirectory.appendingPathComponent(filename!)
|
||||
|
||||
do {
|
||||
try fm.linkItem(at: URL(fileURLWithPath: filePath!), to: tmpFile!)
|
||||
} catch {
|
||||
//TODO: return error
|
||||
return result(false)
|
||||
}
|
||||
|
||||
realPath = tmpFile!
|
||||
}
|
||||
|
||||
pop(title: title, file: realPath) { pass in
|
||||
if (tmpFile != nil) {
|
||||
do {
|
||||
try fm.removeItem(at: tmpFile!)
|
||||
} catch {}
|
||||
}
|
||||
result(pass)
|
||||
}
|
||||
}
|
||||
|
||||
private static func pop(title: String?, file: URL, completion: @escaping ((Bool) -> Void)) {
|
||||
if (title == nil || title!.isEmpty) {
|
||||
return completion(false)
|
||||
}
|
||||
|
||||
let activityViewController = UIActivityViewController(activityItems: [ShareCopy(file: file)], applicationActivities: nil)
|
||||
|
||||
activityViewController.completionWithItemsHandler = {(activityType: UIActivity.ActivityType?, completed: Bool, returnedItems: [Any]?, error: Error?) in
|
||||
completion(true)
|
||||
}
|
||||
|
||||
// Subject
|
||||
activityViewController.setValue(title, forKeyPath: "subject")
|
||||
|
||||
// For iPads, fix issue where Exception is thrown by using a popup instead
|
||||
if UIDevice.current.userInterfaceIdiom == .pad {
|
||||
activityViewController.popoverPresentationController?.sourceView = UIApplication.topViewController()?.view
|
||||
if let view = UIApplication.topViewController()?.view {
|
||||
activityViewController.popoverPresentationController?.permittedArrowDirections = []
|
||||
activityViewController.popoverPresentationController?.sourceRect = CGRect(x: view.bounds.midX, y: view.bounds.midY, width: 0, height: 0)
|
||||
}
|
||||
}
|
||||
|
||||
DispatchQueue.main.async {
|
||||
UIApplication.topViewController()?.present(activityViewController, animated: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension UIApplication {
|
||||
class func topViewController(controller: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
|
||||
if let navigationController = controller as? UINavigationController {
|
||||
return topViewController(controller: navigationController.visibleViewController)
|
||||
}
|
||||
if let tabController = controller as? UITabBarController {
|
||||
if let selected = tabController.selectedViewController {
|
||||
return topViewController(controller: selected)
|
||||
}
|
||||
}
|
||||
if let presented = controller?.presentedViewController {
|
||||
return topViewController(controller: presented)
|
||||
}
|
||||
return controller
|
||||
}
|
||||
}
|
||||
|
||||
class ShareCopy: UIActivityItemProvider {
|
||||
private let file: URL
|
||||
private let content: String
|
||||
|
||||
init(file: URL) {
|
||||
self.file = file
|
||||
do {
|
||||
self.content = try String.init(contentsOf: file)
|
||||
} catch {
|
||||
self.content = "Error"
|
||||
}
|
||||
|
||||
// the type of the placeholder item is used to
|
||||
// display correct activity types by UIActivityControler
|
||||
super.init(placeholderItem: self.content)
|
||||
}
|
||||
|
||||
override var item: Any {
|
||||
get {
|
||||
guard let activityType = activityType else {
|
||||
return file
|
||||
}
|
||||
|
||||
switch activityType {
|
||||
case .copyToPasteboard: return content
|
||||
default: return file
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,14 +1,12 @@
|
|||
// This code comes from https://github.com/lubritto/flutter_share with bugfixes for ipad
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:share_plus/share_plus.dart' as sp;
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
class Share {
|
||||
static const _channel = MethodChannel('net.defined.mobileNebula/NebulaVpnService');
|
||||
|
||||
/// Shares a string of text
|
||||
/// Transforms a string of text into a file and shares that file
|
||||
/// - title: Title of message or subject if sending an email
|
||||
/// - text: The text to share
|
||||
/// - filename: The filename to use if sending over airdrop for example
|
||||
|
@ -21,17 +19,19 @@ class Share {
|
|||
assert(text.isNotEmpty);
|
||||
assert(filename.isNotEmpty);
|
||||
|
||||
if (title.isEmpty) {
|
||||
throw FlutterError('Title cannot be empty');
|
||||
final tmpDir = await getTemporaryDirectory();
|
||||
final file = File(p.join(tmpDir.path, filename));
|
||||
var res = false;
|
||||
|
||||
try {
|
||||
file.writeAsStringSync(text, flush: true);
|
||||
res = await Share.shareFile(title: title, filePath: file.path);
|
||||
} catch (err) {
|
||||
// Ignoring file write errors
|
||||
}
|
||||
|
||||
final bool success = await _channel.invokeMethod('share', <String, dynamic>{
|
||||
'title': title,
|
||||
'text': text,
|
||||
'filename': filename,
|
||||
});
|
||||
|
||||
return success;
|
||||
file.delete();
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Shares a local file
|
||||
|
@ -41,23 +41,16 @@ class Share {
|
|||
static Future<bool> shareFile({
|
||||
required String title,
|
||||
required String filePath,
|
||||
String? filename,
|
||||
String? filename
|
||||
}) async {
|
||||
assert(title.isNotEmpty);
|
||||
assert(filePath.isNotEmpty);
|
||||
|
||||
if (title.isEmpty) {
|
||||
throw FlutterError('Title cannot be empty');
|
||||
} else if (filePath.isEmpty) {
|
||||
throw FlutterError('FilePath cannot be empty');
|
||||
}
|
||||
|
||||
final bool success = await _channel.invokeMethod('shareFile', <String, dynamic>{
|
||||
'title': title,
|
||||
'filePath': filePath,
|
||||
'filename': filename,
|
||||
});
|
||||
|
||||
return success;
|
||||
//NOTE: the filename used to specify the name of the file in gmail/slack/etc but no longer works that way
|
||||
// If we want to support that again we will need to save the file to a temporary directory, share that,
|
||||
// and then delete it
|
||||
final xFile = sp.XFile(filePath, name: filename);
|
||||
final result = await sp.Share.shareXFiles([xFile], subject: title);
|
||||
return result.status == sp.ShareResultStatus.success;
|
||||
}
|
||||
}
|
||||
|
|
40
pubspec.lock
40
pubspec.lock
|
@ -36,6 +36,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.16.0"
|
||||
cross_file:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cross_file
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.3.3+2"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -77,7 +84,7 @@ packages:
|
|||
name: file_picker
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "5.0.1"
|
||||
version: "5.2.2"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
|
@ -156,6 +163,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.8.0"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: mime
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
package_info:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -268,6 +282,20 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
share_plus:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: share_plus
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "6.3.0"
|
||||
share_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: share_plus_platform_interface
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.2.0"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
|
@ -349,7 +377,7 @@ packages:
|
|||
name: url_launcher_linux
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.2"
|
||||
version: "3.0.1"
|
||||
url_launcher_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -370,21 +398,21 @@ packages:
|
|||
name: url_launcher_web
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.5"
|
||||
version: "2.0.13"
|
||||
url_launcher_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_windows
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.2"
|
||||
version: "3.0.1"
|
||||
uuid:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: uuid
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.0.5"
|
||||
version: "3.0.7"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -398,7 +426,7 @@ packages:
|
|||
name: win32
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.7.0"
|
||||
version: "3.1.1"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -33,6 +33,7 @@ dependencies:
|
|||
flutter_barcode_scanner: ^2.0.0
|
||||
flutter_svg: ^1.1.5
|
||||
intl: ^0.17.0
|
||||
share_plus: ^6.3.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
|
Loading…
Reference in New Issue