美文网首页Flutter实战
Flutter实战-自定义键盘(三)

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

作者: 蓝面书生IT | 来源:发表于2022-07-20 11:56 被阅读0次

    用了两年的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);
    });
    }
    }

    相关文章

      网友评论

        本文标题:Flutter实战-自定义键盘(三)

        本文链接:https://www.haomeiwen.com/subject/fnyuirtx.html