Flutter混编工程之Font桥接
Posted eclipse_xu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flutter混编工程之Font桥接相关的知识,希望对你有一定的参考价值。
点击上方蓝字关注我,知识会给你力量
在混编开发中,我们经常遇到要全局替换当前字体的需求,在Native开发中,我们通常会加载Asset或者下载的字体文件,那么在Flutter中,如何直接使用Native的字体文件呢?
毕竟大部分的字体文件都毕竟大,特别是一些字体还有加密策略,如果在Flutter中再创建一份字体文件,既浪费空间,而且也是一种重复代码,所以,我们需要在Flutter端,获取Native的字体文件。
在Flutter中,系统给我们提供了FontLoader,来动态加载字体,与前面的做法一样,我们创建一个Native接口,来获取Native传来的Byte数据流,并借助FontLoader来加载字体。
FontLoader加载字体数据
为了提高传输的效率,我们使用BasicMessageChannel来作为Channel的实现,这些在我们讲解Flutter与Native的通信机制中,都已经演示过了,我们直接拿来Google的Demo代码,修改下需要的内容,将FontLoader引入,代码如下所示。
import 'dart:async';
import 'dart:convert';
import 'package:flutter/services.dart';
class NativeFontApi
NativeFontApi(BinaryMessenger? binaryMessenger) : _binaryMessenger = binaryMessenger;
final BinaryMessenger? _binaryMessenger;
Future<void> loadNativeFont(String fontFamily) async
final BasicMessageChannel<ByteData> channel = BasicMessageChannel<ByteData>(
'dev.flutter.pigeon.NativeFontApi.loadNativeFont',
const BinaryCodec(),
binaryMessenger: _binaryMessenger,
);
try
final uInt8List = utf8.encoder.convert(fontFamily);
final Future<ByteData> fontData = _loadFontFileByteData(uInt8List.buffer.asByteData(), channel);
if (await fontData != ByteData(0))
final FontLoader fontLoader = FontLoader(fontFamily);
fontLoader.addFont(fontData);
fontLoader.load();
catch (e)
return;
Future<ByteData> _loadFontFileByteData(ByteData data, BasicMessageChannel channel) async
final ByteData? fontData = await channel.send(data);
if (fontData != null)
return fontData;
else
return ByteData(0);
在加载好字体数据之后,我们在代码中就可以直接使用看,这和在配置文件中新增字体后的使用方式一样,直接指定fontFamily即可,代码如下所示。
Text(
model[index].bookName ?? "",
style: const TextStyle(
fontSize: 16,
fontFamily: 'xxx_Medium_60',
),
)
唯一需要注意的是,我们需要在程序启动时,初始化我们的字体文件,代码如下所示,通过loadNativeFont调用Channel来加载字体文件。
NativeFontApi().loadNativeFont('xxx_Medium_60');
Native实现
我们仿照pigeon的实现方式,来创建自己的FontBridgeApi,之所以没通过pigeon直接生成,那是因为pigeon还不支持生成Byte数组的方式,所以我们只能自己来写,代码如下所示。
// Autogenerated from Pigeon (v1.0.15), do not edit directly.
// See also: https://pub.dev/packages/pigeon
import java.nio.ByteBuffer;
import io.flutter.plugin.common.BasicMessageChannel;
import io.flutter.plugin.common.BinaryCodec;
import io.flutter.plugin.common.BinaryMessenger;
@SuppressWarnings("unused", "unchecked", "CodeBlock2Expr", "RedundantSuppression")
public class FontBridgeApi
public interface NativeFontApi
ByteBuffer loadNativeFont(String familyName);
static void setup(BinaryMessenger binaryMessenger, NativeFontApi api)
BasicMessageChannel<ByteBuffer> channel =
new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.NativeFontApi.loadNativeFont", BinaryCodec.INSTANCE);
if (api != null)
channel.setMessageHandler((message, reply) ->
try
String param = new String(message != null ? message.array() : new byte[0]);
ByteBuffer output = api.loadNativeFont(param);
reply.reply(output);
catch (Error | RuntimeException ignored)
);
else
channel.setMessageHandler(null);
下面就是实现接口,代码如下所示。
private class NativeFontApiImp : FontBridgeApi.NativeFontApi
private val externalAppFontIndexes = intArrayOf(
ETConverter.FONT_TYPE_INDEX_XXX_LIGHT,
ETConverter.FONT_TYPE_INDEX_XXX_MEDIUM,
ETConverter.FONT_TYPE_INDEX_XXXXX
)
override fun loadNativeFont(familyName: String?): ByteBuffer
val trueTypeFolder = File(ApplicationContext.getInstance().filesDir, ETConverter.FOLDER_TRUE_TYPE_FONTS)
if (!trueTypeFolder.exists()) trueTypeFolder.mkdirs()
val familyNameIndex = when (familyName)
"XXX_Light" -> 0
"XXX_Medium_60" -> 1
"XXXXSerif_Bold" -> 2
else -> 0
val ttf = File(trueTypeFolder, ETConverter.getFontTypeName(externalAppFontIndexes[familyNameIndex]) + ETConverter.POSTFIX_NEW_TTF)
val inputStream: InputStream = FileInputStream(ttf)
val output = ByteArrayOutputStream()
val buffer = ByteArray(4096)
var n = 0
while (true)
try
if (-1 == inputStream.read(buffer).also n = it )
inputStream.close()
output.close()
break
catch (e: IOException)
e.printStackTrace()
output.write(buffer, 0, n)
return ByteBuffer.allocateDirect(output.toByteArray().size).put(output.toByteArray())
我们放到前面pigeon统一实现的地方进行初始化,代码如下。
FontBridgeApi.NativeFontApi.setup(flutterEngine.dartExecutor, NativeFontApiImp())
优化
通过上面的方式,我们很轻松就实现了Flutter端加载Native的字体文件,但是在代码实现过程中,实际上有些地方是可以进行优化的,例如在Flutter中加载字体的异步方法中,我们可以构建一个枚举,根据不同的状态值,来修改代码的执行逻辑,例如增加:「加载中」、「加载失败」等状态,这样在程序异常的时候,可以判断是否需要跳过后面的加载流程、或者是重新执行加载流程,可以增加代码的鲁棒性。
// 状态
enum NativeFontLoadState loading, failed, complete, notFound
// 返回枚举状态
Future<NativeFontLoadState> loadFontIfNeeded(String fontFamily)
全局字体
在Flutter中,我们通常会根据自己项目的特点,封装一些Text组件,那么在这些组件中,就可以直接指定fontFamily,这样在业务开发时,就不需要重复指定fontFamily了,直接使用XXXText即可。
除了这种方式以外,还可以在APP的themeData中,直接指定fontFamily,代码如下:
theme: ThemeData(
fontFamily: xxxx,
),
这样可以为子组件提供默认的字体支持。如果在某些场景下需要修改默认字体,那么重新给Text设置不同的fontFamily覆盖即可。
向大家推荐下我的网站 https://xuyisheng.top/ 点击原文一键直达
专注 android-Kotlin-Flutter 欢迎大家访问
往期推荐
本文原创公众号:群英传,授权转载请联系微信(Tomcat_xu),授权后,请在原创发表24小时后转载。
< END >
作者:徐宜生
更文不易,点个“三连”支持一下👇
以上是关于Flutter混编工程之Font桥接的主要内容,如果未能解决你的问题,请参考以下文章