美文网首页iOS开发
Flutter 微信Excel分享到App

Flutter 微信Excel分享到App

作者: 下线的时候 | 来源:发表于2022-08-22 15:20 被阅读0次

1.Flutter部分

构建一个类 用于调用原生方法 代码如下

class FileShare {
  static const MethodChannel _channel = MethodChannel('YOUR_CHANNEL_NAME');   //channel名字

  /// 获取分享的文件地址
  static Future<String> getOpenFileUrl() async {
    var path = "";
    path = await _channel.invokeMethod('getOpenFileUrl');  //方法名
    return path;
  }
}

2.iOS原生

2.1配置,在info.plist增加需要打开的文件类型配置,iOS文件配置参考链接:链接

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
     ...
    <key>CFBundleDocumentTypes</key>
    <array>
        <dict>
            <key>CFBundleTypeName</key>
            <string>Microsoft Excel</string>
            <key>LSHandlerRank</key>
            <string>Alternate</string>
            <key>LSItemContentTypes</key>
            <array>
                <string>com.microsoft.excel.xls</string>
                <string>org.openxmlformats.spreadsheetml.sheet</string>
            </array>
        </dict>
    </array>
</dict>
</plist>

2.2 原生方法获取文件地址(此处注意Flutter框架会报错,不过运行是没有问题的)

import UIKit
import Flutter

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
    /// 文件路径 (iOS获取的是文件在Documents目录下的路径存放在Inbox下面)
    var filePath = ""
    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        GeneratedPluginRegistrant.register(with: self)
        let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
        ///调用的方法 (channel名字与flutter统一)
        let methodChannel = FlutterMethodChannel(name: "YOUR_CHANNEL_NAME", binaryMessenger: controller.binaryMessenger)
        
        methodChannel.setMethodCallHandler { [weak self] (call, result) in
            if "getOpenFileUrl" == call.method {
                result(self?.filePath)
            }
        }
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }
    
    override func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
        print("完整文件地址为====\(url.path)")
        let completePath = url.path
        var path = ""
        if let range = completePath.range(of: "Documents/") {
            path = String(completePath.suffix(from: range.upperBound))
        }
        self.filePath = path
        print("Documents后面路径为====\(self.filePath)")
        return true
    }
}

3. 安卓原生(安卓新手,有误勿怪)

3.1配置,在AndroidManifest.xml增加需要打开的文件类型配置,文件配置参考链接:链接

...
   <activity
            android:name=".MainActivity"
            android:launchMode="singleTop"
            android:theme="@style/LaunchTheme"
            android:exported="true"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize">
            ...
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:mimeType="application/vnd.ms-excel" />
                <data android:mimeType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" />
            </intent-filter>

        </activity>
...

3.2 原生方法获取文件地址

package com.example.device_repair

import android.content.Intent
import android.net.Uri
import android.os.Bundle
import androidx.annotation.NonNull
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel

class MainActivity: FlutterActivity() {
    // TODO: CHANGE METHOD CHANNEL NAME
    private val CHANNEL = "YOUR_CHANNEL_NAME"

    var openPath: String? = null
    override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        val channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
        channel.setMethodCallHandler { call, result ->
            if (call.method == "getOpenFileUrl") {
                result.success(openPath)
                openPath = null
            } else {
                result.notImplemented()
            }
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        handleOpenFileUrl(intent)
    }

    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)
        handleOpenFileUrl(intent)
    }

    private fun handleOpenFileUrl(intent: Intent?) {
        var uri: Uri? = intent?.data
        if (uri == null) {
            uri = intent?.getParcelableExtra(Intent.EXTRA_STREAM)
        }

        //获取文件真实地址
        val filePath: String? = UriUtils.getFileFromUri(activity, uri)
//        val path = intent?.data?.path
        if (filePath != null) {
            openPath = filePath
        }
    }
}


3.2.1 andriod获取真实路径 链接

package com.example.device_repair

import android.content.ContentResolver
import android.content.Context
import android.database.Cursor
import android.net.Uri
import android.provider.MediaStore
import java.io.*

object UriUtils {
    /**
     * 获取真实路径
     *
     * @param context
     */
     fun getFileFromUri(context: Context, uri: Uri?): String? {
        return if (uri == null) {
            null
        } else when (uri.getScheme()) {
            ContentResolver.SCHEME_CONTENT ->                 //Android7.0之后的uri content:// URI
                getFilePathFromContentUri(context, uri)
            ContentResolver.SCHEME_FILE ->                 //Android7.0之前的uri file://
                File(uri.getPath()).getAbsolutePath()
            else -> File(uri.getPath()).getAbsolutePath()
        }
    }

    /**
     * 从uri获取path
     *
     * @param uri content://media/external/file/109009
     *
     *
     * FileProvider适配
     * content://com.tencent.mobileqq.fileprovider/external_files/storage/emulated/0/Tencent/QQfile_recv/
     * content://com.tencent.mm.external.fileprovider/external/tencent/MicroMsg/Download/
     */
    private fun getFilePathFromContentUri(context: Context, uri: Uri?): String? {
        if (null == uri) return null
        var data: String? = null
        val filePathColumn =
            arrayOf<String>(MediaStore.MediaColumns.DATA, MediaStore.MediaColumns.DISPLAY_NAME)
        val cursor: Cursor? = context.contentResolver.query(uri, filePathColumn, null, null, null)
        if (null != cursor) {
            if (cursor.moveToFirst()) {
                val index: Int = cursor.getColumnIndex(MediaStore.MediaColumns.DATA)
                data = if (index > -1) {
                    cursor.getString(index)
                } else {
                    val nameIndex: Int = cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME)
                    val fileName: String = cursor.getString(nameIndex)
                    getPathFromInputStreamUri(context, uri, fileName)
                }
            }
            cursor.close()
        }
        return data
    }

    /**
     * 用流拷贝文件一份到自己APP私有目录下
     *
     * @param context
     * @param uri
     * @param fileName
     */
    private fun getPathFromInputStreamUri(context: Context, uri: Uri, fileName: String): String? {
        var inputStream: InputStream? = null
        var filePath: String? = null
        if (uri.authority != null) {
            try {
                inputStream = context.contentResolver.openInputStream(uri)
                val file = createTemporalFileFrom(context, inputStream, fileName)
                filePath = file!!.path
            } catch (e: Exception) {
            } finally {
                try {
                    if (inputStream != null) {
                        inputStream.close()
                    }
                } catch (e: Exception) {
                }
            }
        }
        return filePath
    }

    @Throws(IOException::class)
    private fun createTemporalFileFrom(
        context: Context,
        inputStream: InputStream?,
        fileName: String
    ): File? {
        var targetFile: File? = null
        if (inputStream != null) {
            var read: Int
            val buffer = ByteArray(8 * 1024)
            //自己定义拷贝文件路径
            targetFile = File(context.externalCacheDir, fileName)
            if (targetFile.exists()) {
                targetFile.delete()
            }
            val outputStream: OutputStream = FileOutputStream(targetFile)
            while (inputStream.read(buffer).also { read = it } != -1) {
                outputStream.write(buffer, 0, read)
            }
            outputStream.flush()
            try {
                outputStream.close()
            } catch (e: IOException) {
                e.printStackTrace()
            }
        }
        return targetFile
    }
}

4.如何调用 (可以根据path使用path_provider创建file,然后做上传服务器后台之类的操作)

import 'package:flutter/material.dart';
import 'package:file_share/file_share.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({Key key}) : super(key: key);

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
  /// 文件路径 (安卓为全路径,iOS为沙盒Documents下面的路径)
  var _filePath = "";

  @override
  void initState() {
    super.initState();
    getOpenFileUrl();

  }

  /// 获取分享到的excel文件
  getOpenFileUrl() async {
    String url = "";

    url = await FileShare.getOpenFileUrl();
    setState(() {
      _filePath = url;
    });
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    switch (state) {
      case AppLifecycleState.inactive: // 处于这种状态的应用程序应该假设它们可能在任何时候暂停。
        print("处于这种状态的应用程序应该假设它们可能在任何时候暂停。");
        break;
      case AppLifecycleState.resumed: // 应用程序可见,前台
        print("应用程序可见,前台");
        getOpenFileUrl();
        break;
      case AppLifecycleState.paused: // 应用程序不可见,后台
        print("应用程序不可见,后台");
        break;
      case AppLifecycleState.detached: // 申请将暂时暂停
        print("申请将暂时暂停");
        break;
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Plugin example app'),
        ),
        body: Center(
          child: Text('文件地址为: $_filePath'),
        ),
      ),
    );
  }
}

相关文章

  • Flutter 微信Excel分享到App

    1.Flutter部分 构建一个类 用于调用原生方法 代码如下 2.iOS原生 2.1配置,在info.plis...

  • Android debug和release签名

    最近使用flutter开发一款APP 里面使用到了微信分享 不知道为什么 只要是添加了微信分享得插件 就运行...

  • 2020-10-29

    ios从app分享到微信然后微信再分享到微信出现分享标题、图片、描述设置了但是没有生效的问题 通过观察发现ios中...

  • 微信分享与设置

    这几天捣鼓微信分享,先定义一下:从你的APP分享到微信叫一次分享,从微信内再次分享到微信叫二次分享。 一次分享 需...

  • 完美Excel文章合集2

    自从2014年开通[完美Excel]微信公众号以来,坚持分享已经学习到的Excel和VBA知识和心得,目前已分享文...

  • 完美Excel文章合集1

    自从2014年开通[完美Excel]微信公众号以来,坚持分享已经学习到的Excel和VBA知识和心得,目前已分享文...

  • 17. 小程序流量入口之分享转发

    1,分享和转发在技术上是一回事,先看APP分享小程序到微信。以前通过App分享链接到微信好友,打开后是普通的网页,...

  • Flutter 微信分享

    https://github.com/pj0579/flutter_wechat

  • flutter 微信分享

    使用jshare_flutter_plugin,微信分享带图片的时候提示图片最大32KB

  • 【iOS】微信分享描述变链接

    从去年开始,微信分享功能改版升级(跟随微信版本升级),APP分享到微信的描述如果是空的,则显示的是对应的网址链接(...

网友评论

    本文标题:Flutter 微信Excel分享到App

    本文链接:https://www.haomeiwen.com/subject/bfrigrtx.html