Flutter调用原生图片资源

Posted Ever69

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flutter调用原生图片资源相关的知识,希望对你有一定的参考价值。

在Flutter与原生的混合开发中,经常遇到Flutter与原生都需要使用同一份图片的情况,并且由于原生是主导,图片资源都在原生这侧。
那么,Flutter可不可以直接使用原生的图片资源呢?——答案是不可以的,Flutter并不能直接访问原生侧的图片资源,官方没有提供这样的API供我们使用。
那么,在Flutter侧再放一份图片资源呢?虽然可以这么做,但这样显然是不合理的,除了要维护两套资源文件外,安装包的大小也会增加,尤其是那种有很多图片资源的App。
所以,为了避免两份同样图片资源的存在,我们只能想办法让Flutter使用原生侧的图片资源,那么,到底怎么做呢?

虽然Flutter不能直接使用原生的图片资源,但是我们可以曲线救国嘛,直接不可以,那就间接咯~

Flutter中图片的加载方式

曲线救国前,先了解一下,在Flutter中,负责图片展示的小部Image有四种图片加载方式

  • Image.assest,用于从本地资源集合获取图像。
  • Image.network ,用于从 URL 获取图像。
  • Image.memory,用于从字节数组获取图像。
  • Image.file,用于从文件获取图像。

通过流的形式加载原生的图片资源


既然Flutter中的Image可以通过字节数组的形式加载图片,那么,我们是不是可以将原生中的图片转成字节数组,返回给Flutter进行图片加载呢?我觉得没毛病。
使用MethodChannel定义一个通信方法,向原生获取图片的字节数组。

Flutter侧代码

//字节数组
Uint8List _imageBytes = Uint8List.fromList([]);
//用于展示的小部件
RaisedButton(
  child: Text("通过流的形式调用原生图片"),
  onPressed: () {
    _getImageAsBytes("ic_launcher");
  },
),
Image.memory(_imageBytes)
//获取图片字节数组的方法
Future<void> _getImageAsBytes(String imageName) async {
    Uint8List bytes =
        await platform.invokeMethod("imageAsBytes", {'name': '$imageName'});
    setState(() {
      _imageBytes = bytes;
    });
  }

原生侧代码
android

override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
        GeneratedPluginRegistrant.registerWith(flutterEngine)
        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "samples.flutter.dev/battery")
                .setMethodCallHandler { call, result ->
                    when (call.method) {
                        "imageAsBytes" -> {
                            if (call.hasArgument("name")) {
                                val path = call.argument<String>("name")?.let { getImageAsBytes(it) }
                                if (path == null) {
                                    result.error("-1", "调用原生图片失败", null)
                                } else {
                                    result.success(path)
                                }
                            }
                        }
                    }
                }
    }
private fun getImageAsBytes(imageName: String): ByteArray? {
        var imageId = resources.getIdentifier(imageName, "mipmap", packageName)
        if (imageId == 0) {
            imageId = resources.getIdentifier(imageName, "drawable", packageName)
        }
        if (imageId == 0) {
            return null
        }
        val imageBitmap = BitmapFactory.decodeResource(resources, imageId)
        val  bos = ByteArrayOutputStream()
        imageBitmap.compress(Bitmap.CompressFormat.PNG,100,bos)
        return bos.toByteArray()
    }

ios
略。。

通过文件的形式加载原生的图片资源


Flutter中的Image小部件可以通过文件的形式加载图片,我们大可以将原生中的图片以文件的形式保存在本地,再将文件的路径告知Flutter去加载,这样就可以间接的使用原生中的图片资源了。
ok,上代码。还是使用MethodChannel进行通信,对Flutter与原生通信不熟悉的朋友可以看这篇博客-Flutte与原生通信

Flutter侧代码

 //全局变量,图片文件地址
 String _imageFilePath = "";
 //小部件
 RaisedButton(
   child: Text("调用展示原生图片资源"),
   onPressed: () {
     _getImageAsFile("ic_launcher");
     },
   ),
 Image.file(File(_imageFilePath))
 //获取图片文件路径的方法
 Future<void> _getImageAsFile(String imageName) async {
    String path = await platform.invokeMethod("image", {'name': '$imageName'});
    setState(() {
      _imageFilePath = path;
    });
  }

原生侧代码
Android

override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
        GeneratedPluginRegistrant.registerWith(flutterEngine)
        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "samples.flutter.dev/battery")
                .setMethodCallHandler { call, result ->
                    when (call.method) {
                        "imageAsFile" -> {
                            if (call.hasArgument("name")) {
                                val path = call.argument<String>("name")?.let { getImageAsFile(it) }
                                if (path.isNullOrEmpty()) {
                                    result.error("-1", "调用原生图片失败", null)
                                } else {
                                    result.success(path)
                                }
                            }
                        }
                    }
                }
    }

    private fun getImageAsFile(imageName: String): String {
        var imageFilePath = "$externalCacheDir${File.separator}$imageName"
        val imageFile = File(imageFilePath)
        if (!imageFile.exists()) {
            var imageId = resources.getIdentifier(imageName, "mipmap", packageName)
            if (imageId == 0) {
                imageId = resources.getIdentifier(imageName, "drawable", packageName)
            }
            if (imageId == 0) {
                return ""
            }
            val imageBitmap = BitmapFactory.decodeResource(resources, imageId)
            val fos = FileOutputStream(imageFile)
            val success = imageBitmap.compress(Bitmap.CompressFormat.PNG, 100, fos)
            if (!success) {
                imageFile.delete()
                return ""
            }
        }
        return imageFilePath
    }

Ios
略(滑稽.jpg)

效果如何?


代码写完,读取个原生图片看看效果如何,以Android为例,读取mipmap文件夹下的ic_launcher图片(因为我这是一个Flutter工程,所以ic_launcher是Flutter的logo)。
在这里插入图片描述
运行App。
在这里插入图片描述

dangdan!Flutter读取原生图片,搞定~

以上是关于Flutter调用原生图片资源的主要内容,如果未能解决你的问题,请参考以下文章

Flutter 调用原生

Flutter自定义Widget—可加载原生图片资源的Image

Flutter自定义Widget—可加载原生图片资源的Image

从原生 Android 主屏幕小部件调用 Flutter (Dart) 代码

FlutterWeb 和 WebView 原生交互调用

如何从 Flutter web 调用原生 Android 代码。?