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


    5e0577fa429ee51680be183ebe857c9c.jpeg用了两年的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 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 handlePlatformMessage(
    String channel,
    ByteData? message,
    ui.PlatformMessageResponseCallback? callback,
    ) async {
    ui.channelBuffers.push(channel, message, (ByteData? data) {
    if (callback != null)
    callback(data);
    });
    }

    @override
    Future send(String channel, ByteData? message) {
    final Completer completer = Completer();
    // 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 _outboundHandlers = {};


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

    ...

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

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

    final Future? 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 _outboundHandlers = {};

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

    final Future? 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 handlePlatformMessage(
    String channel,
    ByteData? message,
    ui.PlatformMessageResponseCallback? callback,
    ) async {
    ui.channelBuffers.push(channel, message, (ByteData? data) {
    if (callback != null) callback(data);
    });
    }
    }

  • 相关阅读:
    丹麦能源袭击预示着更关键的基础设施成为目标
    CSS3媒体查询与页面自适应
    14.1 Socket 套接字编程入门
    京东方Java实习面经
    Xshell远程连接配置 Ubuntu 18.04.6 + Anaconda + CUDA + Cudnn + Pytorch(GPU+CPU)
    【Kotlin 协程】协程的挂起和恢复 ② ( 协程挂起 和 线程阻塞 对比 )
    Tuxera NTFS 2022 for Mac破解版百度网盘免费下载安装激活教程
    Android测试常用的adb命令
    使用global route快速分析绕线
    Spring Boot+MyBatis+MySQL+Spring MVC之论坛首页开发
  • 原文地址:https://blog.csdn.net/hantian616/article/details/125909473