Commit 959e749e authored by justin's avatar justin

Merge branch 'master' into v1.17.1-hotfixes

parents fedaf160 4ee76454
......@@ -30,7 +30,7 @@ bool isTopContainer = FlutterBoost.BoostContainer.of(context).onstage
回答:无障碍模式下目前Flutter Engine有bug,已经提交issue和PR给flutter啦。请参考这个issue:https://github.com/alibaba/flutter_boost/issues/488及其分析。提交给flutter的PR见这里:https://github.com/flutter/engine/pull/14155
### 5. 在ios模拟器下运行最新的flutter boost会闪退
回答:如上面第4条所说的,最新的flutter engine在voice over下有bug,会导致crash。因为模拟器下flutter默认会将voice over模式打开,所以其实就是辅助模式,这触发上面的bug:“在ios中voice over打开,demo在点击交互会crash”。
回答:如上面第4条所说的,最新的flutter engine在voice over下有bug,会导致crash。因为模拟器下flutter默认会将voice over模式打开,所以其实就是辅助模式,这触发上面的bug:“在ios中voice over打开,demo在点击交互会crash”。
可参考Engine的代码注释:
```c++
#if TARGET_OS_SIMULATOR
......
1、为何使用flutter_boost?
官方的集成方案有诸多弊病:
- 日志不能输出到原生端;
- 存在内存泄漏的问题,使用boost可以让内存稳定;
- native调用flutter,flutter调用native,通道的封装,使开发更加简便;
- 同时对于页面生命周期的管理,也梳理的比较整齐
2、集成流程IOS
在delegate中做flutter初始化的工作
```
在appDelegate中引入,PlatformRouterImp,用于实现平台侧的页面打开和关闭,不建议直接使用用于页面打开,建议使用FlutterBoostPlugin中的open和close方法来打开或关闭页面;
PlatformRouterImp内部实现打开各种native页面的映射。
self.router = [PlatformRouterImp new];
//初始化FlutterBoost混合栈环境。应在程序使用混合栈之前调用。如在AppDelegate中。本函数默认需要flutter boost来注册所有插件。
[FlutterBoostPlugin.sharedInstance startFlutterWithPlatform:self.router
onStart:^(FlutterEngine *engine) {
}];
```
PlatformRouterImp,内部实现打开native页面的路由代码截图
```
- (void)open:(NSString *)name
urlParams:(NSDictionary *)params
exts:(NSDictionary *)exts
completion:(void (^)(BOOL))completion
{
if ([name isEqualToString:@"page1"]) {//打开页面1
// 打开页面1的vc
return;
}
BOOL animated = [exts[@"animated"] boolValue];
FLBFlutterViewContainer *vc = FLBFlutterViewContainer.new;
[vc setName:name params:params];
[self.navigationController pushViewController:vc animated:animated];
if(completion) completion(YES);
}
```
如何打开,关闭flutter页面,直接调用FlutterBoostPlugin的类方法即可。
```
/**
* 关闭页面,混合栈推荐使用的用于操作页面的接口
*
* @param uniqueId 关闭的页面唯一ID符
* @param resultData 页面要返回的结果(给上一个页面),会作为页面返回函数的回调参数
* @param exts 额外参数
* @param completion 关闭页面的即时回调,页面一旦关闭即回调
*/
+ (void)close:(NSString *)uniqueId
result:(NSDictionary *)resultData
exts:(NSDictionary *)exts
completion:(void (^)(BOOL))completion;
/**
* 打开新页面(默认以push方式),混合栈推荐使用的用于操作页面的接口;通过urlParams可以设置为以present方式打开页面:urlParams:@{@"present":@(YES)}
*
* @param url 打开的页面资源定位符
* @param urlParams 传人页面的参数; 若有特殊逻辑,可以通过这个参数设置回调的id
* @param exts 额外参数
* @param resultCallback 当页面结束返回时执行的回调,通过这个回调可以取得页面的返回数据,如close函数传入的resultData
* @param completion 打开页面的即时回调,页面一旦打开即回调
*/
+ (void)open:(NSString *)url
urlParams:(NSDictionary *)urlParams
exts:(NSDictionary *)exts
onPageFinished:(void (^)(NSDictionary *))resultCallback
completion:(void (^)(BOOL))completion;
/**
* Present方式打开新页面,混合栈推荐使用的用于操作页面的接口
*
* @param url 打开的页面资源定位符
* @param urlParams 传人页面的参数; 若有特殊逻辑,可以通过这个参数设置回调的id
* @param exts 额外参数
* @param resultCallback 当页面结束返回时执行的回调,通过这个回调可以取得页面的返回数据,如close函数传入的resultData
* @param completion 打开页面的即时回调,页面一旦打开即回调
*/
+ (void)present:(NSString *)url
urlParams:(NSDictionary *)urlParams
exts:(NSDictionary *)exts
onPageFinished:(void (^)(NSDictionary *))resultCallback
completion:(void (^)(BOOL))completion;
```
IOS如何传递数据给flutter
```
//name是事件的名称,arguments中是一个NSDictionary,OC代码
[FlutterBoostPlugin.sharedInstance sendEvent:@"name" arguments:@{}];
//flutter部分接收数据,dart代码
FlutterBoost.singleton.channel.addEventListener('name',
(name, arguments){
//todo
return;
});
```
flutter如何传递数据给native
```
//flutter代码,ChannelName是通道名称与native部分一致即可,tmp是map类型的参数
Map<String,dynamic> tmp = Map<String,dynamic>();
try{
FlutterBoost.singleton.channel.sendEvent(ChannelName, tmp);
}catch(e){
}
//IOS侧代码
[FlutterBoostPlugin.sharedInstance addEventListener:^(NSString *name, NSDictionary *arguments) {
} forName:@"statistic"];
```
flutter中页面的生命周期管理
```
enum ContainerLifeCycle {
Init,
Appear,//已经出现,很遗憾的是如果在native部分present页面,这里是不会回调的。
WillDisappear,
Disappear,
Destroy,
Background,
Foreground
}
FlutterBoost.singleton.addBoostContainerLifeCycleObserver(//这个类是单例,再每个页面的initState方法中添加即可监听
(ContainerLifeCycle state, BoostContainerSettings settings) {
//setttings是配置,name表示页面的名称,建议一定要等到当前页面Appear状态的时候再做操作,
print(
'FlutterBoost.singleton.addBoostContainerLifeCycleObserver '+state.toString()+' '+settings.name);
},
);
```
关于flutter部分打开新页面的回调的一些坑。
```
FlutterBoost.singleton
.open(CoursePage.routeName, urlParams: {
}).then((Map<dynamic, dynamic> value) {
print(
'call me when page is finished. did recieve second route result $value');
//这个方法会优先于addBoostContainerLifeCycleObserver中的appear方法调用,所以说有些方法在这里调用,如果appear还没有出现的话就会有问题。
});
```
3、集成流程Android(待补充)
\ No newline at end of file
......@@ -5,18 +5,19 @@
<b></b><br>
<a href="README_CN.md">中文文档</a>
<a href="https://mp.weixin.qq.com/s?__biz=MzU4MDUxOTI5NA==&mid=2247484367&idx=1&sn=fcbc485f068dae5de9f68d52607ea08f&chksm=fd54d7deca235ec86249a9e3714ec18be8b2d6dc580cae19e4e5113533a6c5b44dfa5813c4c3&scene=0&subscene=131&clicktime=1551942425&ascene=7&devicetype=android-28&version=2700033b&nettype=ctnet&abtest_cookie=BAABAAoACwASABMABAAklx4AVpkeAMSZHgDWmR4AAAA%3D&lang=zh_CN&pass_ticket=1qvHqOsbLBHv3wwAcw577EHhNjg6EKXqTfnOiFbbbaw%3D&wx_header=1">中文介绍</a>
<a href="INTEGRATION.md">集成相关</a>
</p>
# Release Note
Please checkout the release note for the latest 0.1.64 to see changes [0.1.64 release note](https://github.com/alibaba/flutter_boost/releases)
Please checkout the release note for the latest 1.12.13+1 to see changes [1.12.13+1 release note](https://github.com/alibaba/flutter_boost/releases)
# FlutterBoost
A next-generation Flutter-Native hybrid solution. FlutterBoost is a Flutter plugin which enables hybrid integration of Flutter for your existing native apps with minimum efforts.The philosophy of FlutterBoost is to use Flutter as easy as using a WebView. Managing Native pages and Flutter pages at the same time is non-trivial in an existing App. FlutterBoost takes care of page resolution for you. The only thing you need to care about is the name of the page(usually could be an URL). 
<a name="bf647454"></a>
# Prerequisites
You need to add Flutter to your project before moving on.The version of the flutter SDK requires v1.9.1+hotfixes, or it will compile error.
You need to add Flutter to your project before moving on.The version of the flutter SDK requires to match boost's version, or it will compile error.
......@@ -24,13 +25,8 @@ You need to add Flutter to your project before moving on.The version of the flut
| Flutter Boost Version | Support Flutter SDK Version | Description | Support AndroidX? |
| --------------------- | --------------------------- | ------------------------------------------------------------ | ------------------ |
| 0.1.50 | 1.5.4-hotfixes | android if other flutter versions or branches will compile incorrectly. | No |
| 0.1.51-0.1.59 | 1.5.4-hotfixes | bugfix for 0.1.50. | No |
| 0.1.60 | 1.9.1-hotfixes | Android does not support andriodx if other flutter branches will compile incorrectly. | No |
| 0.1.61-0.1.69 | 1.9.1-hotfixes | bugfix for 0.1.60. | No |
| 0.1.63 | 1.9.1-hotfixes | If other branches will compile incorrectly. Synchronize with the 0.1.60 code, and bugfix also merge to this branch. | No |
| 1.9.1+2 | 1.9.1-hotfixes | Rename the version number and start supporting androidx by default | Yes |
| 1.12.13 | 1.12.13-hotfixes | supporting androidx | Yes |
| 1.12.13+1 | 1.12.13-hotfixes | supporting androidx | Yes |
......@@ -39,10 +35,7 @@ You need to add Flutter to your project before moving on.The version of the flut
| Flutter Boost branch | Support Flutter SDK Version | Description | Support AndroidX? |
| --------------------- | --------------------------- | ------------------------------------------------------------ | ------------------ |
| v1.9.1-hotfixes | 1.9.1-hotfixes | for androidx | Yes |
| task/task_v1.9.1_support_hotfixes| 1.9.1-hotfixes | for support | NO |
| v1.12.13-hotfixes | 1.12.13-hotfixes | for androidx | Yes |
| task/task_v1.12.13_support_hotfixes| 1.12.13-hotfixes | for support | NO |
# Getting Started
......@@ -56,21 +49,18 @@ androidx branch
flutter_boost:
git:
url: 'https://github.com/alibaba/flutter_boost.git'
ref: ' 1.12.13'
```
support branch
```json
flutter_boost:
git:
url: 'https://github.com/alibaba/flutter_boost.git'
ref: 'task/task_v1.12.13_support_hotfixes'
ref: '1.12.13+1'
```
# Boost Integration
Please see the boost example for details.
Please see
1. Boost detail example
2. integrated document <a href="INTEGRATION.md">Integration </a>
# FAQ
please read this document:
......
......@@ -5,7 +5,7 @@
# Release Note
请查看最新版本0.1.64的release note 确认变更,[0.1.64 release note](https://github.com/alibaba/flutter_boost/releases)
请查看最新版本1.12.13+1的release note 确认变更,[1.12.13+1 release note](https://github.com/alibaba/flutter_boost/releases)
# FlutterBoost
......@@ -14,7 +14,7 @@
# 前置条件
在继续之前,您需要将Flutter集成到你现有的项目中。flutter sdk 的版本需要 v1.9.1-hotfixes,否则会编译失败.
在继续之前,您需要将Flutter集成到你现有的项目中。flutter sdk 的版本需要和boost版本适配,否则会编译失败.
# FAQ
请阅读这篇文章:
......@@ -24,25 +24,17 @@
| Flutter Boost 版本 | 支持的 Flutter SDK 版本 | Description | 是否支持 AndroidX? |
| ----------------------- | ----------------------- | ------------------------------------------------------------ | ------------------- |
| 0.1.50 | 1.5.4-hotfixes | android 如果其他 flutter 版本或者分支会编译错误。 | No |
| 0.1.51-0.1.59 | 1.5.4-hotfixes | 0.1.50 的 bugfix。 | No |
| 0.1.60 | 1.9.1-hotfixes | android 如果其他 flutter 分支会编译错误。 | No |
| 0.1.63 | 1.9.1-hotfixes | 和 0.1.60 代码同步, bugfix 也会合入该分支,如果其他分支会编译错误。 | No |
| 0.1.61-0.1.69 | 1.9.1-hotfixes | 0.1.60 的 bugfix。 | No |
| 1.9.1+2 | 1.9.1-hotfixes | 版本号重新命名,开始默认支持androidx | Yes |
| 1.12.13 | 1.12.13 -hotfixes | 支持androidx | Yes |
| 1.12.13+1 | 1.12.13 -hotfixes | 支持androidx | Yes |
| Flutter Boost 分支 | 支持的 Flutter SDK 版本 | Description | Support AndroidX? |
| Flutter Boost 分支 | 支持的 Flutter SDK 版本 | Description | 是否支持 AndroidX? |
| --------------------- | --------------------------- | ------------------------------------------------------------ | ------------------ |
| v1.9.1-hotfixes | 1.9.1-hotfixes | for androidx | Yes |
| task/task_v1.9.1_support_hotfixes| 1.9.1-hotfixes | for support | NO |
| v1.12.13-hotfixes | 1.12.13-hotfixes | for androidx | Yes |
| task/task_v1.12.13_support_hotfixes| 1.12.13-hotfixes | for support | NO |
# 安装
......@@ -56,22 +48,17 @@ androidx branch
flutter_boost:
git:
url: 'https://github.com/alibaba/flutter_boost.git'
ref: '1.12.13'
```
support branch
```json
flutter_boost:
git:
url: 'https://github.com/alibaba/flutter_boost.git'
ref: 'task/task_v1.12.13_support_hotfixes'
ref: '1.12.13+1'
```
## boost集成
集成请看boost的Examples
集成请看:
1. boost的Examples
2. 集成文档 <a href="INTEGRATION.md">Integration </a>
......
......@@ -74,6 +74,8 @@ public class FlutterViewContainerManager implements IContainerManager {
}
void popRecord(IContainerRecord record) {
if(mRecordStack.empty()) return;
if(mRecordStack.peek() == record) {
mRecordStack.pop();
}
......
......@@ -43,27 +43,7 @@ import io.flutter.plugin.editing.TextInputPlugin;
import io.flutter.plugin.platform.PlatformViewsController;
import io.flutter.view.AccessibilityBridge;
/**
* Displays a Flutter UI on an Android device.
* <p>
* A {@code FlutterView}'s UI is painted by a corresponding {@link FlutterEngine}.
* <p>
* A {@code FlutterView} can operate in 2 different {@link RenderMode}s:
* <ol>
* <li>{@link RenderMode#surface}, which paints a Flutter UI to a {@link android.view.SurfaceView}.
* This mode has the best performance, but a {@code FlutterView} in this mode cannot be positioned
* between 2 other Android {@code View}s in the z-index, nor can it be animated/transformed.
* Unless the special capabilities of a {@link android.graphics.SurfaceTexture} are required,
* developers should strongly prefer this render mode.</li>
* <li>{@link RenderMode#texture}, which paints a Flutter UI to a {@link android.graphics.SurfaceTexture}.
* This mode is not as performant as {@link RenderMode#surface}, but a {@code FlutterView} in this
* mode can be animated and transformed, as well as positioned in the z-index between 2+ other
* Android {@code Views}. Unless the special capabilities of a {@link android.graphics.SurfaceTexture}
* are required, developers should strongly prefer the {@link RenderMode#surface} render mode.</li>
* </ol>
* See <a>https://source.android.com/devices/graphics/arch-tv#surface_or_texture</a> for more
* information comparing {@link android.view.SurfaceView} and {@link android.view.TextureView}.
*/
public class XFlutterView extends FrameLayout {
private static final String TAG = "FlutterView";
......@@ -150,26 +130,12 @@ public class XFlutterView extends FrameLayout {
this(context, null, null, null);
}
/**
* Constructs a {@code FlutterView} programmatically, without any XML attributes,
* and allows selection of a {@link #renderMode}.
* <p>
* {@link #transparencyMode} defaults to {@link TransparencyMode#opaque}.
* <p>
* {@code FlutterView} requires an {@code Activity} instead of a generic {@code Context}
* to be compatible with {@link PlatformViewsController}.
*/
public XFlutterView(@NonNull Context context, @NonNull FlutterView.RenderMode renderMode) {
this(context, null, renderMode, null);
}
/**
* Constructs a {@code FlutterView} programmatically, without any XML attributes,
* assumes the use of {@link RenderMode#surface}, and allows selection of a {@link #transparencyMode}.
* <p>
* {@code FlutterView} requires an {@code Activity} instead of a generic {@code Context}
* to be compatible with {@link PlatformViewsController}.
*/
public XFlutterView(@NonNull Context context, @NonNull FlutterView.TransparencyMode transparencyMode) {
this(context, null, FlutterView.RenderMode.surface, transparencyMode);
}
......@@ -609,16 +575,9 @@ public class XFlutterView extends FrameLayout {
this.flutterEngine.getPlatformViewsController().attachToView(this);
if(textInputPlugin==null){
textInputPlugin = new XTextInputPlugin(
this,
flutterEngine.getTextInputChannel(),
this.flutterEngine.getPlatformViewsController()
);
}
textInputPlugin.setTextInputMethodHandler();
textInputPlugin= XTextInputPlugin.getTextInputPlugin( this.flutterEngine.getDartExecutor(),
this.flutterEngine.getPlatformViewsController());
textInputPlugin.updateView(this);
textInputPlugin.getInputMethodManager().restartInput(this);
......@@ -717,7 +676,7 @@ public class XFlutterView extends FrameLayout {
}
public void release(){
if(textInputPlugin!=null){
textInputPlugin.release();
// textInputPlugin.release();
}
}
......
......@@ -31,6 +31,7 @@ import io.flutter.plugin.common.ErrorLogResult;
import io.flutter.plugin.common.MethodChannel;
class XInputConnectionAdaptor extends BaseInputConnection {
private final View mFlutterView;
private final int mClient;
private final TextInputChannel textInputChannel;
......@@ -221,7 +222,6 @@ class XInputConnectionAdaptor extends BaseInputConnection {
mEditable.delete(selStart, selEnd);
mEditable.insert(selStart, String.valueOf((char) character));
setSelection(selStart + 1, selStart + 1);
updateEditingState();
}
return true;
}
......
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package com.idlefish.flutterboost;
import android.annotation.SuppressLint;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import android.os.Build;
import android.provider.Settings;
import android.text.Editable;
import android.text.InputType;
import android.text.Selection;
......@@ -11,6 +17,11 @@ import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import io.flutter.embedding.engine.dart.DartExecutor;
import io.flutter.embedding.engine.systemchannels.TextInputChannel;
......@@ -21,11 +32,11 @@ import io.flutter.plugin.platform.PlatformViewsController;
*/
public class XTextInputPlugin {
@NonNull
private View mView;
private View mView;
@NonNull
private final InputMethodManager mImm;
private InputMethodManager mImm;
@NonNull
private final TextInputChannel textInputChannel;
private TextInputChannel textInputChannel;
@NonNull
private InputTarget inputTarget = new InputTarget(InputTarget.Type.NO_TARGET, 0);
@Nullable
......@@ -37,26 +48,34 @@ public class XTextInputPlugin {
private InputConnection lastInputConnection;
@NonNull
private PlatformViewsController platformViewsController;
private boolean restartAlwaysRequired;
// When true following calls to createInputConnection will return the cached lastInputConnection if the input
// target is a platform view. See the comments on lockPlatformViewInputConnection for more details.
private boolean isInputConnectionLocked;
private static XTextInputPlugin xTextInputPlugin;
public static XTextInputPlugin getTextInputPlugin( DartExecutor dartExecutor, @NonNull PlatformViewsController platformViewsController){
if(xTextInputPlugin!=null) return xTextInputPlugin;
xTextInputPlugin =new XTextInputPlugin(dartExecutor,platformViewsController);
return xTextInputPlugin;
}
public XTextInputPlugin(@NonNull DartExecutor dartExecutor, @NonNull PlatformViewsController platformViewsController) {
public XTextInputPlugin(View view, @NonNull TextInputChannel textInputChannel, @NonNull PlatformViewsController platformViewsController) {
mView = view;
mImm = (InputMethodManager) view.getContext().getSystemService(
Context.INPUT_METHOD_SERVICE);
this.textInputChannel = textInputChannel;
textInputChannel = new TextInputChannel(dartExecutor);
textInputChannel.requestExistingInputState();
this.platformViewsController = platformViewsController;
// this.platformViewsController.attachTextInputPlugin(this);
}
public void release() {
mView = null;
}
public void setTextInputMethodHandler() {
public void updateView(View view){
mView = view;
mImm = (InputMethodManager) view.getContext().getSystemService(
Context.INPUT_METHOD_SERVICE);
textInputChannel.setTextInputMethodHandler(new TextInputChannel.TextInputMethodHandler() {
@Override
......@@ -89,8 +108,12 @@ public class XTextInputPlugin {
clearTextInputClient();
}
});
restartAlwaysRequired = isRestartAlwaysRequired();
}
@NonNull
public InputMethodManager getInputMethodManager() {
return mImm;
......@@ -114,7 +137,7 @@ public class XTextInputPlugin {
/**
* Unlocks the input connection.
* <p>
*
* See also: @{link lockPlatformViewInputConnection}.
*/
public void unlockPlatformViewInputConnection() {
......@@ -123,7 +146,7 @@ public class XTextInputPlugin {
/**
* Detaches the text input plugin from the platform views controller.
* <p>
*
* The TextInputPlugin instance should not be used after calling this.
*/
public void destroy() {
......@@ -134,6 +157,7 @@ public class XTextInputPlugin {
TextInputChannel.InputType type,
boolean obscureText,
boolean autocorrect,
boolean enableSuggestions,
TextInputChannel.TextCapitalization textCapitalization
) {
if (type.type == TextInputChannel.TextInputType.DATETIME) {
......@@ -168,6 +192,7 @@ public class XTextInputPlugin {
textType |= InputType.TYPE_TEXT_VARIATION_PASSWORD;
} else {
if (autocorrect) textType |= InputType.TYPE_TEXT_FLAG_AUTO_CORRECT;
if (!enableSuggestions) textType |= InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
}
if (textCapitalization == TextInputChannel.TextCapitalization.CHARACTERS) {
......@@ -191,19 +216,15 @@ public class XTextInputPlugin {
if (isInputConnectionLocked) {
return lastInputConnection;
}
View platformView = platformViewsController.getPlatformViewById(inputTarget.id);
if (platformView != null) {
lastInputConnection = platformView.onCreateInputConnection(outAttrs);
return lastInputConnection;
} else {
return null;
}
lastInputConnection = platformViewsController.getPlatformViewById(inputTarget.id).onCreateInputConnection(outAttrs);
return lastInputConnection;
}
outAttrs.inputType = inputTypeFromTextInputType(
configuration.inputType,
configuration.obscureText,
configuration.autocorrect,
configuration.enableSuggestions,
configuration.textCapitalization
);
outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN;
......@@ -243,7 +264,7 @@ public class XTextInputPlugin {
/**
* Clears a platform view text input client if it is the current input target.
* <p>
*
* This is called when a platform view is disposed to make sure we're not hanging to a stale input
* connection.
*/
......@@ -269,7 +290,8 @@ public class XTextInputPlugin {
mImm.hideSoftInputFromWindow(view.getApplicationWindowToken(), 0);
}
private void setTextInputClient(int client, TextInputChannel.Configuration configuration) {
@VisibleForTesting
void setTextInputClient(int client, TextInputChannel.Configuration configuration) {
inputTarget = new InputTarget(InputTarget.Type.FRAMEWORK_CLIENT, client);
this.configuration = configuration;
mEditable = Editable.Factory.getInstance().newEditable("");
......@@ -301,8 +323,8 @@ public class XTextInputPlugin {
}
}
private void setTextInputEditingState(View view, TextInputChannel.TextEditState state) {
if (!mRestartInputPending && state.text.equals(mEditable.toString())) {
@VisibleForTesting void setTextInputEditingState(View view, TextInputChannel.TextEditState state) {
if (!restartAlwaysRequired && !mRestartInputPending && state.text.equals(mEditable.toString())) {
applyStateToSelection(state);
mImm.updateSelection(mView, Math.max(Selection.getSelectionStart(mEditable), 0),
Math.max(Selection.getSelectionEnd(mEditable), 0),
......@@ -316,6 +338,28 @@ public class XTextInputPlugin {
}
}
// Samsung's Korean keyboard has a bug where it always attempts to combine characters based on
// its internal state, ignoring if and when the cursor is moved programmatically. The same bug
// also causes non-korean keyboards to occasionally duplicate text when tapping in the middle
// of existing text to edit it.
//
// Fully restarting the IMM works around this because it flushes the keyboard's internal state
// and stops it from trying to incorrectly combine characters. However this also has some
// negative performance implications, so we don't want to apply this workaround in every case.
@SuppressLint("NewApi") // New API guard is inline, the linter can't see it.
@SuppressWarnings("deprecation")
private boolean isRestartAlwaysRequired() {
InputMethodSubtype subtype = mImm.getCurrentInputMethodSubtype();
// Impacted devices all shipped with Android Lollipop or newer.
if (subtype == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP || !Build.MANUFACTURER.equals("samsung")) {
return false;
}
String keyboardName = Settings.Secure.getString(mView.getContext().getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
// The Samsung keyboard is called "com.sec.android.inputmethod/.SamsungKeypad" but look
// for "Samsung" just in case Samsung changes the name of the keyboard.
return keyboardName.contains("Samsung");
}
private void clearTextInputClient() {
if (inputTarget.type == InputTarget.Type.PLATFORM_VIEW) {
// Focus changes in the framework tree have no guarantees on the order focus nodes are notified. A node
......@@ -358,4 +402,4 @@ public class XTextInputPlugin {
// For platform views this is the platform view's ID.
int id;
}
}
\ No newline at end of file
}
......@@ -3,9 +3,11 @@ package com.taobao.idlefish.flutterboostexample;
import android.app.Application;
import android.content.Context;
import android.os.Build;
import android.util.Log;
import com.idlefish.flutterboost.*;
import java.util.HashMap;
import java.util.Map;
import com.idlefish.flutterboost.interfaces.INativeRouter;
......@@ -13,7 +15,7 @@ import io.flutter.embedding.android.FlutterView;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.plugins.GeneratedPluginRegistrant;
import io.flutter.plugin.common.StandardMessageCodec;
public class MyApplication extends Application {
......@@ -41,6 +43,26 @@ public class MyApplication extends Application {
@Override
public void onEngineCreated() {
// 注册MethodChannel,监听flutter侧的getPlatformVersion调用
MethodChannel methodChannel = new MethodChannel(FlutterBoost.instance().engineProvider().getDartExecutor(), "flutter_native_channel");
methodChannel.setMethodCallHandler((call, result) -> {
if (call.method.equals("getPlatformVersion")) {
result.success(Build.VERSION.RELEASE);
} else {
result.notImplemented();
}
});
// 注册PlatformView viewTypeId要和flutter中的viewType对应
FlutterBoost
.instance()
.engineProvider()
.getPlatformViewsController()
.getRegistry()
.registerViewFactory("plugins.test/view", new TextPlatformViewFactory(StandardMessageCodec.INSTANCE));
}
@Override
......
package com.taobao.idlefish.flutterboostexample;
import io.flutter.app.FlutterPluginRegistry;
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.plugin.common.StandardMessageCodec;
public class TextPlatformViewPlugin {
public static void register(PluginRegistry.Registrar registrar) {
registrar.platformViewRegistry().registerViewFactory("plugins.test/view",
new TextPlatformViewFactory(StandardMessageCodec.INSTANCE));
}
}
......@@ -23,8 +23,22 @@
PlatformRouterImp *router = [PlatformRouterImp new];
[FlutterBoostPlugin.sharedInstance startFlutterWithPlatform:router
onStart:^(FlutterEngine *engine) {
}];
// 注册MethodChannel,监听flutter侧的getPlatformVersion调用
FlutterMethodChannel *flutterMethodChannel = [FlutterMethodChannel methodChannelWithName:@"flutter_native_channel" binaryMessenger:engine.binaryMessenger];
[flutterMethodChannel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult _Nonnull result) {
NSString *method = call.method;
if ([method isEqualToString:@"getPlatformVersion"]) {
NSString *sysVersion = [[UIDevice currentDevice] systemVersion];
result(sysVersion);
} else {
result(FlutterMethodNotImplemented);
}
}];
}];
self.window = [[UIWindow alloc] initWithFrame: [UIScreen mainScreen].bounds];
......
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_boost/flutter_boost.dart';
import 'package:flutter_boost_example/platform_view.dart';
......@@ -11,6 +12,24 @@ class FirstRouteWidget extends StatefulWidget {
class _FirstRouteWidgetState extends State<FirstRouteWidget> {
_FirstRouteWidgetState();
// flutter 侧MethodChannel配置,channel name需要和native侧一致
static const MethodChannel _methodChannel = MethodChannel('flutter_native_channel');
String _systemVersion = '';
Future<dynamic> _getPlatformVersion() async {
try {
final String result = await _methodChannel.invokeMethod('getPlatformVersion');
print('getPlatformVersion:' + result);
setState(() {
_systemVersion = result;
});
} on PlatformException catch (e) {
print(e.message);
}
}
@override
void initState() {
print('initState');
......@@ -111,6 +130,10 @@ class _FirstRouteWidgetState extends State<FirstRouteWidget> {
});
},
),
RaisedButton(
child: Text('Get system version by method channel:' + _systemVersion),
onPressed: () => _getPlatformVersion(),
),
],
),
),
......@@ -318,6 +341,25 @@ class FlutterRouteWidget extends StatefulWidget {
}
class _FlutterRouteWidgetState extends State<FlutterRouteWidget> {
// flutter 侧MethodChannel配置,channel name需要和native侧一致
static const MethodChannel _methodChannel = MethodChannel('flutter_native_channel');
String _systemVersion = '';
Future<dynamic> _getPlatformVersion() async {
try {
final String result = await _methodChannel.invokeMethod('getPlatformVersion');
print('getPlatformVersion:' + result);
setState(() {
_systemVersion = result;
});
} on PlatformException catch (e) {
print(e.message);
}
}
@override
Widget build(BuildContext context) {
final String message = widget.message;
......@@ -509,6 +551,17 @@ class _FlutterRouteWidgetState extends State<FlutterRouteWidget> {
onTap: () =>
FlutterBoost.singleton.open('sample://flutterFragmentPage'),
),
InkWell(
child: Container(
padding: const EdgeInsets.all(8.0),
margin: const EdgeInsets.all(8.0),
color: Colors.yellow,
child: Text(
'get system version by method channel:' + _systemVersion,
style: TextStyle(fontSize: 22.0, color: Colors.black),
)),
onTap: () => _getPlatformVersion(),
),
],
),
),
......
......@@ -38,7 +38,7 @@ class ObserversHolder {
void removeObserver<T>(T observer) =>
_observers[T.toString()]?.remove(observer);
void cleanObservers<T>(T observer) => _observers[T.toString()]?.clear();
void cleanObservers<T>() => _observers[T.toString()]?.clear();
Set<T> observersOf<T>() => _observers[T.toString()] as Set<T> ?? <T>{};
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment