Flutter实战

Flutter实战-自定义键盘(三)

2022-07-20  本文已影响0人  蓝面书生IT

用了两年的flutter,有了一些心得,从今天开始陆续更新一些案例,不虚头巴脑,只求实战有用,以供学习或使用flutter的小伙伴参考,学习尚浅,如有不正确的地方还望各路大神指正,以免误人子弟,在此拜谢~(原创不易,转发请标注来源和作者)

注意:无特殊说明,flutter版本为3.0+


当初写该组件的时候flutter的版本是2.1,直到有一天flutter 升级到2.5版本后,发现键盘突然不能用了,发现是官方废弃了TestDefaultBinaryMessenger,

/// [WidgetTester], [TestWidgetsFlutterBinding], [TestDefaultBinaryMessenger],
/// and [TestDefaultBinaryMessenger.handlePlatformMessage] respectively).
///
/// To register a handler for a given message channel, see [setMessageHandler].
///
/// To send a message _to_ a plugin on the platform thread, see [send].
// TODO(ianh): deprecate this method once cocoon and other customer_tests are migrated:
// @NotYetDeprecated(
// 'Instead of calling this method, use ServicesBinding.instance.channelBuffers.push. '
// 'In tests, consider using tester.binding.defaultBinaryMessenger.handlePlatformMessage '
/
/ 'or TestDefaultBinaryMessenger.instance.defaultBinaryMessenger.handlePlatformMessage. '
// 'This feature was deprecated after v2.1.0-10.0.pre.'
// )
Future<void> handlePlatformMessage(String channel, ByteData? data, ui.PlatformMessageResponseCallback? callback);

当时cool_ui的作者也没有发布新版本,那么如何解决这个问题呢,那就是重新写一个自己的BinaryMessenger。

一.定义自己的WidgetsFlutterBinding

不了解WidgetsFlutterBinding 的读者可以看我之前的文章,那首先我们要复写ServicesBingding,这个bingding是 用来处理flutter和原生交互用的

class MyWidgetsFlutterBinding extends WidgetsFlutterBinding with MyServicesBinding {
static WidgetsBinding? ensureInitialized() {
MyWidgetsFlutterBinding();
return WidgetsBinding.instance;
}
}

mixin MyServicesBinding on BindingBase, ServicesBinding {
@override
BinaryMessenger createBinaryMessenger() {
return TestDefaultBinaryMessenger(super.createBinaryMessenger());
}
}

看源码中我们知道默认走的是系统的DefaultBinaryMessenger。

我们先看源码

class _DefaultBinaryMessenger extends BinaryMessenger {
const _DefaultBinaryMessenger._();

@override
Future<void> handlePlatformMessage(
String channel,
ByteData? message,
ui.PlatformMessageResponseCallback? callback,
) async {
ui.channelBuffers.push(channel, message, (ByteData? data) {
if (callback != null)
callback(data);
});
}

@override
Future<ByteData?> send(String channel, ByteData? message) {
final Completer<ByteData?> completer = Completer<ByteData?>();
// ui.PlatformDispatcher.instance is accessed directly instead of using
// ServicesBinding.instance.platformDispatcher because this method might be
// invoked before any binding is initialized. This issue was reported in
// #27541. It is not ideal to statically access
// ui.PlatformDispatcher.instance because the PlatformDispatcher may be
// dependency injected elsewhere with a different instance. However, static
// access at this location seems to be the least bad option.
// TODO(ianh): Use ServicesBinding.instance once we have better diagnostics
// on that getter.
ui.PlatformDispatcher.instance.sendPlatformMessage(channel, message, (ByteData? reply) {
try {
completer.complete(reply);
} catch (exception, stack) {
FlutterError.reportError(FlutterErrorDetails(
exception: exception,
stack: stack,
library: 'services library',
context: ErrorDescription('during a platform message response callback'),
));
}
});
return completer.future;
}

@override
void setMessageHandler(String channel, MessageHandler? handler) {
if (handler == null) {
ui.channelBuffers.clearListener(channel);
} else {
ui.channelBuffers.setListener(channel, (ByteData? data, ui.PlatformMessageResponseCallback callback) async {
ByteData? response;
try {
response = await handler(data);
} catch (exception, stack) {
FlutterError.reportError(FlutterErrorDetails(
exception: exception,
stack: stack,
library: 'services library',
context: ErrorDescription('during a platform message callback'),
));
} finally {
callback(response);
}
});
}
}
}

这个也是实现了BinaryMessenger的方法,那我们的思路就是在默认方法中拦截并调用自己的方法

final Map<String, MessageHandler> _outboundHandlers = <String, MessageHandler>{};


@override
void setMessageHandler(String channel, MessageHandler? handler) {
if (handler != null && handler.toString().contains("_textInputHanlde")) {
_outboundHandlers[channel] = handler;
return;
}

...

当含有_textInputHanlde的时候,将处理方法改成自己的,然后再send的时候调用。

@override
Future<ByteData?> send(String channel, ByteData? message) {
final Completer<ByteData?> completer = Completer<ByteData?>();

final Future<ByteData?>? resultFuture;
final MessageHandler? handler = _outboundHandlers[channel];
if (handler != null) {
resultFuture = handler(message);
return resultFuture!;
}

...

这样就实现了拦截。

二。使用

在Main函数中,runApp()之前调用以下方法

MyWidgetsFlutterBinding.ensureInitialized();

附属源码:

import 'dart:async';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:ui' as ui;

class MyWidgetsFlutterBinding extends WidgetsFlutterBinding with MyServicesBinding {
static WidgetsBinding? ensureInitialized() {
MyWidgetsFlutterBinding();
return WidgetsBinding.instance;
}
}

mixin MyServicesBinding on BindingBase, ServicesBinding {
@override
BinaryMessenger createBinaryMessenger() {
return TestDefaultBinaryMessenger(super.createBinaryMessenger());
}
}

class TestDefaultBinaryMessenger extends BinaryMessenger {
final BinaryMessenger origin;

TestDefaultBinaryMessenger(this.origin);

final Map<String, MessageHandler> _outboundHandlers = <String, MessageHandler>{};

@override
Future<ByteData?> send(String channel, ByteData? message) {
final Completer<ByteData?> completer = Completer<ByteData?>();

final Future<ByteData?>? resultFuture;
final MessageHandler? handler = _outboundHandlers[channel];
if (handler != null) {
resultFuture = handler(message);
return resultFuture!;
}

ui.PlatformDispatcher.instance.sendPlatformMessage(channel, message, (ByteData? reply) {
try {
completer.complete(reply);
} catch (exception, stack) {
FlutterError.reportError(FlutterErrorDetails(
exception: exception,
stack: stack,
library: 'services library',
context: ErrorDescription('during a platform message response callback'),
));
}
});
return completer.future;
}

@override
void setMessageHandler(String channel, MessageHandler? handler) {
if (handler != null && handler.toString().contains("_textInputHanlde")) {
_outboundHandlers[channel] = handler;
return;
}
if (handler == null) {
ui.channelBuffers.clearListener(channel);
} else {
ui.channelBuffers.setListener(channel, (ByteData? data, ui.PlatformMessageResponseCallback callback) async {
ByteData? response;
try {
response = await handler(data);
} catch (exception, stack) {
FlutterError.reportError(FlutterErrorDetails(
exception: exception,
stack: stack,
library: 'services library',
context: ErrorDescription('during a platform message callback'),
));
} finally {
callback(response);
}
});
}
}

@override
Future<void> handlePlatformMessage(
String channel,
ByteData? message,
ui.PlatformMessageResponseCallback? callback,
) async {
ui.channelBuffers.push(channel, message, (ByteData? data) {
if (callback != null) callback(data);
});
}
}

上一篇下一篇

猜你喜欢

热点阅读