Flutter 微信Excel分享到App

1.Flutter部分

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

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

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

2.iOS原生

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





     ...
    CFBundleDocumentTypes
    
        
            CFBundleTypeName
            Microsoft Excel
            LSHandlerRank
            Alternate
            LSItemContentTypes
            
                com.microsoft.excel.xls
                org.openxmlformats.spreadsheetml.sheet
            
        
    



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增加需要打开的文件类型配置,文件配置参考链接:链接

...
   
            ...
            
                
                
                
                
            

        
...

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(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 createState() => _MyAppState();
}

class _MyAppState extends State 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)