Flutter 微信Excel分享到App

1.Flutter部分

構(gòu)建一個(gè)類 用于調(diào)用原生方法 代碼如下

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框架會(huì)報(bào)錯(cuò)状婶,不過運(yùn)行是沒有問題的)

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
        ///調(diào)用的方法 (channel名字與flutter統(tǒng)一)
        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)
        }

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


3.2.1 andriod獲取真實(shí)路徑 鏈接

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 {
    /**
     * 獲取真實(shí)路徑
     *
     * @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.如何調(diào)用 (可以根據(jù)path使用path_provider創(chuàng)建file拦盹,然后做上傳服務(wù)器后臺(tái)之類的操作)

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: // 處于這種狀態(tài)的應(yīng)用程序應(yīng)該假設(shè)它們可能在任何時(shí)候暫停。
        print("處于這種狀態(tài)的應(yīng)用程序應(yīng)該假設(shè)它們可能在任何時(shí)候暫停普舆。");
        break;
      case AppLifecycleState.resumed: // 應(yīng)用程序可見恬口,前臺(tái)
        print("應(yīng)用程序可見,前臺(tái)");
        getOpenFileUrl();
        break;
      case AppLifecycleState.paused: // 應(yīng)用程序不可見沼侣,后臺(tái)
        print("應(yīng)用程序不可見祖能,后臺(tái)");
        break;
      case AppLifecycleState.detached: // 申請(qǐng)將暫時(shí)暫停
        print("申請(qǐng)將暫時(shí)暫停");
        break;
    }
  }

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市蛾洛,隨后出現(xiàn)的幾起案子养铸,更是在濱河造成了極大的恐慌,老刑警劉巖轧膘,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件钞螟,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡扶供,警方通過查閱死者的電腦和手機(jī)筛圆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門裂明,熙熙樓的掌柜王于貴愁眉苦臉地迎上來椿浓,“玉大人,你說我怎么就攤上這事闽晦“獍” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵仙蛉,是天一觀的道長笋敞。 經(jīng)常有香客問我,道長荠瘪,這世上最難降的妖魔是什么夯巷? 我笑而不...
    開封第一講書人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任赛惩,我火速辦了婚禮,結(jié)果婚禮上趁餐,老公的妹妹穿的比我還像新娘喷兼。我一直安慰自己,他們只是感情好后雷,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開白布季惯。 她就那樣靜靜地躺著,像睡著了一般臀突。 火紅的嫁衣襯著肌膚如雪勉抓。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評(píng)論 1 285
  • 那天候学,我揣著相機(jī)與錄音藕筋,去河邊找鬼。 笑死盒齿,一個(gè)胖子當(dāng)著我的面吹牛念逞,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播边翁,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼翎承,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了符匾?” 一聲冷哼從身側(cè)響起叨咖,我...
    開封第一講書人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎啊胶,沒想到半個(gè)月后甸各,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡焰坪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年趣倾,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片某饰。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡儒恋,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出黔漂,到底是詐尸還是另有隱情诫尽,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布炬守,位于F島的核電站牧嫉,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜酣藻,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一曹洽、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧辽剧,春花似錦衣洁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至撤卢,卻和暖如春环凿,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背放吩。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國打工智听, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人渡紫。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓到推,卻偏偏與公主長得像,于是被迫代替她去往敵國和親惕澎。 傳聞我的和親對(duì)象是個(gè)殘疾皇子莉测,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容