Commit 7ead767f authored by nightfallsad's avatar nightfallsad Committed by GitHub

Merge pull request #9 from alibaba/master

update
parents 14d8bfa0 1571d96c
...@@ -17,3 +17,4 @@ example/android/app/.classpath ...@@ -17,3 +17,4 @@ example/android/app/.classpath
example/android/app/.project example/android/app/.project
example/android/.project example/android/.project
flutter_boost flutter_boost
.flutter-plugins-dependencies
...@@ -9,7 +9,7 @@ addons: ...@@ -9,7 +9,7 @@ addons:
- libstdc++6 - libstdc++6
# - fonts-droid # - fonts-droid
before_script: before_script:
- git clone https://github.com/flutter/flutter.git -b v1.12.13-hotfixes --depth 1 - git clone https://github.com/flutter/flutter.git -b flutter-1.17-candidate.3 --depth 1
- ./flutter/bin/flutter doctor - ./flutter/bin/flutter doctor
script: script:
- ./flutter/bin/flutter test --coverage --coverage-path=lcov.info - ./flutter/bin/flutter test --coverage --coverage-path=lcov.info
......
...@@ -30,7 +30,7 @@ bool isTopContainer = FlutterBoost.BoostContainer.of(context).onstage ...@@ -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 回答:无障碍模式下目前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会闪退 ### 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的代码注释: 可参考Engine的代码注释:
```c++ ```c++
#if TARGET_OS_SIMULATOR #if TARGET_OS_SIMULATOR
......
...@@ -56,7 +56,11 @@ flutter_boost: ...@@ -56,7 +56,11 @@ flutter_boost:
# Boost Integration # 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 # FAQ
please read this document: please read this document:
......
...@@ -55,7 +55,10 @@ flutter_boost: ...@@ -55,7 +55,10 @@ flutter_boost:
## boost集成 ## boost集成
集成请看boost的Examples 集成请看:
1. boost的Examples
2. 集成文档 <a href="INTEGRATION.md">Integration </a>
......
...@@ -64,6 +64,18 @@ public class FlutterBoost { ...@@ -64,6 +64,18 @@ public class FlutterBoost {
//fix crash:'FlutterBoostPlugin not register yet' //fix crash:'FlutterBoostPlugin not register yet'
//case: initFlutter after Activity.OnCreate method,and then called start/stop crash //case: initFlutter after Activity.OnCreate method,and then called start/stop crash
// In SplashActivity ,showDialog(in OnCreate method) to check permission, if authorized, then init sdk and jump homePage) // In SplashActivity ,showDialog(in OnCreate method) to check permission, if authorized, then init sdk and jump homePage)
// fix bug : The LauncherActivity will be launch by clicking app icon when app enter background in HuaWei Rom, cause missing forgoround event
if(mEnterActivityCreate && mCurrentActiveActivity == null) {
Intent intent = activity.getIntent();
if (!activity.isTaskRoot()
&& intent != null
&& intent.hasCategory(Intent.CATEGORY_LAUNCHER)
&& intent.getAction() != null
&& intent.getAction().equals(Intent.ACTION_MAIN)) {
return;
}
}
mEnterActivityCreate = true; mEnterActivityCreate = true;
mCurrentActiveActivity = activity; mCurrentActiveActivity = activity;
if (mPlatform.whenEngineStart() == ConfigBuilder.ANY_ACTIVITY_CREATED) { if (mPlatform.whenEngineStart() == ConfigBuilder.ANY_ACTIVITY_CREATED) {
......
...@@ -74,6 +74,8 @@ public class FlutterViewContainerManager implements IContainerManager { ...@@ -74,6 +74,8 @@ public class FlutterViewContainerManager implements IContainerManager {
} }
void popRecord(IContainerRecord record) { void popRecord(IContainerRecord record) {
if(mRecordStack.empty()) return;
if(mRecordStack.peek() == record) { if(mRecordStack.peek() == record) {
mRecordStack.pop(); mRecordStack.pop();
} }
......
package com.idlefish.flutterboost;
import android.content.Context;
import android.graphics.SurfaceTexture;
import android.util.AttributeSet;
import android.view.Surface;
import android.view.TextureView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import io.flutter.Log;
import io.flutter.embedding.engine.renderer.FlutterRenderer;
import io.flutter.embedding.engine.renderer.RenderSurface;
public class XFlutterTextureView extends TextureView implements RenderSurface {
private static final String TAG = "FlutterTextureView";
private boolean isSurfaceAvailableForRendering = false;
private boolean isAttachedToFlutterRenderer = false;
@Nullable
private FlutterRenderer flutterRenderer;
private Surface renderSurface;
// Connects the {@code SurfaceTexture} beneath this {@code TextureView} with Flutter's native code.
// Callbacks are received by this Object and then those messages are forwarded to our
// FlutterRenderer, and then on to the JNI bridge over to native Flutter code.
private final SurfaceTextureListener surfaceTextureListener = new SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) {
Log.v(TAG, "SurfaceTextureListener.onSurfaceTextureAvailable()");
isSurfaceAvailableForRendering = true;
// If we're already attached to a FlutterRenderer then we're now attached to both a renderer
// and the Android window, so we can begin rendering now.
if (isAttachedToFlutterRenderer) {
connectSurfaceToRenderer();
}
}
@Override
public void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surface, int width, int height) {
Log.v(TAG, "SurfaceTextureListener.onSurfaceTextureSizeChanged()");
if (isAttachedToFlutterRenderer) {
changeSurfaceSize(width, height);
}
}
@Override
public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) {
// Invoked every time a new frame is available. We don't care.
}
@Override
public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface) {
Log.v(TAG, "SurfaceTextureListener.onSurfaceTextureDestroyed()");
isSurfaceAvailableForRendering = false;
// If we're attached to a FlutterRenderer then we need to notify it that our SurfaceTexture
// has been destroyed.
if (isAttachedToFlutterRenderer) {
disconnectSurfaceFromRenderer();
}
// Return true to indicate that no further painting will take place
// within this SurfaceTexture.
return true;
}
};
/**
* Constructs a {@code FlutterTextureView} programmatically, without any XML attributes.
*/
public XFlutterTextureView(@NonNull Context context) {
this(context, null);
}
/**
* Constructs a {@code FlutterTextureView} in an XML-inflation-compliant manner.
*/
public XFlutterTextureView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
// Listen for when our underlying SurfaceTexture becomes available, changes size, or
// gets destroyed, and take the appropriate actions.
setSurfaceTextureListener(surfaceTextureListener);
}
@Nullable
@Override
public FlutterRenderer getAttachedRenderer() {
return flutterRenderer;
}
/**
* Invoked by the owner of this {@code FlutterTextureView} when it wants to begin rendering
* a Flutter UI to this {@code FlutterTextureView}.
*
* If an Android {@link SurfaceTexture} is available, this method will give that
* {@link SurfaceTexture} to the given {@link FlutterRenderer} to begin rendering
* Flutter's UI to this {@code FlutterTextureView}.
*
* If no Android {@link SurfaceTexture} is available yet, this {@code FlutterTextureView}
* will wait until a {@link SurfaceTexture} becomes available and then give that
* {@link SurfaceTexture} to the given {@link FlutterRenderer} to begin rendering
* Flutter's UI to this {@code FlutterTextureView}.
*/
public void attachToRenderer(@NonNull FlutterRenderer flutterRenderer) {
Log.v(TAG, "Attaching to FlutterRenderer.");
if (this.flutterRenderer != null) {
Log.v(TAG, "Already connected to a FlutterRenderer. Detaching from old one and attaching to new one.");
this.flutterRenderer.stopRenderingToSurface();
}
this.flutterRenderer = flutterRenderer;
isAttachedToFlutterRenderer = true;
// If we're already attached to an Android window then we're now attached to both a renderer
// and the Android window. We can begin rendering now.
if (isSurfaceAvailableForRendering) {
Log.v(TAG, "Surface is available for rendering. Connecting FlutterRenderer to Android surface.");
connectSurfaceToRenderer();
}
}
/**
* Invoked by the owner of this {@code FlutterTextureView} when it no longer wants to render
* a Flutter UI to this {@code FlutterTextureView}.
*
* This method will cease any on-going rendering from Flutter to this {@code FlutterTextureView}.
*/
public void detachFromRenderer() {
if (flutterRenderer != null) {
// If we're attached to an Android window then we were rendering a Flutter UI. Now that
// this FlutterTextureView is detached from the FlutterRenderer, we need to stop rendering.
// TODO(mattcarroll): introduce a isRendererConnectedToSurface() to wrap "getWindowToken() != null"
if (getWindowToken() != null) {
Log.v(TAG, "Disconnecting FlutterRenderer from Android surface.");
disconnectSurfaceFromRenderer();
}
flutterRenderer = null;
isAttachedToFlutterRenderer = false;
} else {
Log.w(TAG, "detachFromRenderer() invoked when no FlutterRenderer was attached.");
}
}
// FlutterRenderer and getSurfaceTexture() must both be non-null.
private void connectSurfaceToRenderer() {
if (flutterRenderer == null || getSurfaceTexture() == null) {
throw new IllegalStateException("connectSurfaceToRenderer() should only be called when flutterRenderer and getSurfaceTexture() are non-null.");
}
// flutterRenderer.startRenderingToSurface(new Surface(getSurfaceTexture()));
renderSurface = new Surface(getSurfaceTexture());
flutterRenderer.startRenderingToSurface(renderSurface);
}
// FlutterRenderer must be non-null.
private void changeSurfaceSize(int width, int height) {
if (flutterRenderer == null) {
throw new IllegalStateException("changeSurfaceSize() should only be called when flutterRenderer is non-null.");
}
Log.v(TAG, "Notifying FlutterRenderer that Android surface size has changed to " + width + " x " + height);
flutterRenderer.surfaceChanged(width, height);
}
// FlutterRenderer must be non-null.
private void disconnectSurfaceFromRenderer() {
if (flutterRenderer == null) {
throw new IllegalStateException("disconnectSurfaceFromRenderer() should only be called when flutterRenderer is non-null.");
}
flutterRenderer.stopRenderingToSurface();
if(renderSurface!=null){
renderSurface.release();
renderSurface = null;
}
}
}
\ No newline at end of file
...@@ -38,32 +38,12 @@ import io.flutter.embedding.engine.FlutterEngine; ...@@ -38,32 +38,12 @@ import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.embedding.engine.renderer.FlutterRenderer; import io.flutter.embedding.engine.renderer.FlutterRenderer;
import io.flutter.embedding.engine.renderer.FlutterUiDisplayListener; import io.flutter.embedding.engine.renderer.FlutterUiDisplayListener;
import io.flutter.embedding.engine.renderer.RenderSurface; import io.flutter.embedding.engine.renderer.RenderSurface;
import io.flutter.embedding.engine.systemchannels.TextInputChannel; import io.flutter.embedding.engine.systemchannels.SettingsChannel;
import io.flutter.plugin.editing.TextInputPlugin; import io.flutter.plugin.editing.TextInputPlugin;
import io.flutter.plugin.platform.PlatformViewsController; import io.flutter.plugin.platform.PlatformViewsController;
import io.flutter.view.AccessibilityBridge; 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 { public class XFlutterView extends FrameLayout {
private static final String TAG = "FlutterView"; private static final String TAG = "FlutterView";
...@@ -150,26 +130,12 @@ public class XFlutterView extends FrameLayout { ...@@ -150,26 +130,12 @@ public class XFlutterView extends FrameLayout {
this(context, null, null, null); 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) { public XFlutterView(@NonNull Context context, @NonNull FlutterView.RenderMode renderMode) {
this(context, null, renderMode, null); 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) { public XFlutterView(@NonNull Context context, @NonNull FlutterView.TransparencyMode transparencyMode) {
this(context, null, FlutterView.RenderMode.surface, transparencyMode); this(context, null, FlutterView.RenderMode.surface, transparencyMode);
} }
...@@ -217,7 +183,7 @@ public class XFlutterView extends FrameLayout { ...@@ -217,7 +183,7 @@ public class XFlutterView extends FrameLayout {
break; break;
case texture: case texture:
Log.v(TAG, "Internally using a FlutterTextureView."); Log.v(TAG, "Internally using a FlutterTextureView.");
FlutterTextureView flutterTextureView = new FlutterTextureView(getContext()); XFlutterTextureView flutterTextureView = new XFlutterTextureView(getContext());
renderSurface = flutterTextureView; renderSurface = flutterTextureView;
addView(flutterTextureView); addView(flutterTextureView);
break; break;
...@@ -434,7 +400,7 @@ public class XFlutterView extends FrameLayout { ...@@ -434,7 +400,7 @@ public class XFlutterView extends FrameLayout {
*/ */
@Override @Override
public boolean checkInputConnectionProxy(View view) { public boolean checkInputConnectionProxy(View view) {
return flutterEngine != null return flutterEngine != null&&view!=null
? flutterEngine.getPlatformViewsController().checkInputConnectionProxy(view) ? flutterEngine.getPlatformViewsController().checkInputConnectionProxy(view)
: super.checkInputConnectionProxy(view); : super.checkInputConnectionProxy(view);
} }
...@@ -609,16 +575,9 @@ public class XFlutterView extends FrameLayout { ...@@ -609,16 +575,9 @@ public class XFlutterView extends FrameLayout {
this.flutterEngine.getPlatformViewsController().attachToView(this); this.flutterEngine.getPlatformViewsController().attachToView(this);
textInputPlugin= XTextInputPlugin.getTextInputPlugin( this.flutterEngine.getDartExecutor(),
if(textInputPlugin==null){ this.flutterEngine.getPlatformViewsController());
textInputPlugin = new XTextInputPlugin( textInputPlugin.updateView(this);
this,
flutterEngine.getTextInputChannel(),
this.flutterEngine.getPlatformViewsController()
);
}
textInputPlugin.setTextInputMethodHandler();
textInputPlugin.getInputMethodManager().restartInput(this); textInputPlugin.getInputMethodManager().restartInput(this);
...@@ -717,7 +676,7 @@ public class XFlutterView extends FrameLayout { ...@@ -717,7 +676,7 @@ public class XFlutterView extends FrameLayout {
} }
public void release(){ public void release(){
if(textInputPlugin!=null){ if(textInputPlugin!=null){
textInputPlugin.release(); textInputPlugin.release(this);
} }
} }
...@@ -788,9 +747,17 @@ public class XFlutterView extends FrameLayout { ...@@ -788,9 +747,17 @@ public class XFlutterView extends FrameLayout {
*/ */
private void sendUserSettingsToFlutter() { private void sendUserSettingsToFlutter() {
if(flutterEngine!=null&&flutterEngine.getSettingsChannel()!=null){ if(flutterEngine!=null&&flutterEngine.getSettingsChannel()!=null){
flutterEngine.getSettingsChannel().startMessage() // Lookup the current brightness of the Android OS.
boolean isNightModeOn = (getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
SettingsChannel.PlatformBrightness brightness = isNightModeOn
? SettingsChannel.PlatformBrightness.dark
: SettingsChannel.PlatformBrightness.light;
flutterEngine.getSettingsChannel().startMessage()
.setTextScaleFactor(getResources().getConfiguration().fontScale) .setTextScaleFactor(getResources().getConfiguration().fontScale)
.setUse24HourFormat(DateFormat.is24HourFormat(getContext())) .setUse24HourFormat(DateFormat.is24HourFormat(getContext()))
.setPlatformBrightness(brightness)
.send(); .send();
} }
} }
......
...@@ -31,6 +31,7 @@ import io.flutter.plugin.common.ErrorLogResult; ...@@ -31,6 +31,7 @@ import io.flutter.plugin.common.ErrorLogResult;
import io.flutter.plugin.common.MethodChannel; import io.flutter.plugin.common.MethodChannel;
class XInputConnectionAdaptor extends BaseInputConnection { class XInputConnectionAdaptor extends BaseInputConnection {
private final View mFlutterView; private final View mFlutterView;
private final int mClient; private final int mClient;
private final TextInputChannel textInputChannel; private final TextInputChannel textInputChannel;
...@@ -221,7 +222,6 @@ class XInputConnectionAdaptor extends BaseInputConnection { ...@@ -221,7 +222,6 @@ class XInputConnectionAdaptor extends BaseInputConnection {
mEditable.delete(selStart, selEnd); mEditable.delete(selStart, selEnd);
mEditable.insert(selStart, String.valueOf((char) character)); mEditable.insert(selStart, String.valueOf((char) character));
setSelection(selStart + 1, selStart + 1); setSelection(selStart + 1, selStart + 1);
updateEditingState();
} }
return true; return true;
} }
......
...@@ -28,8 +28,8 @@ public class XPlatformPlugin { ...@@ -28,8 +28,8 @@ public class XPlatformPlugin {
public static final int DEFAULT_SYSTEM_UI = View.SYSTEM_UI_FLAG_LAYOUT_STABLE public static final int DEFAULT_SYSTEM_UI = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
private Activity activity; private Activity activity;
private PlatformChannel platformChannel; private PlatformChannel platformChannel;
private PlatformChannel.SystemChromeStyle currentTheme; private PlatformChannel.SystemChromeStyle currentTheme;
private int mEnabledOverlays; private int mEnabledOverlays;
...@@ -95,26 +95,25 @@ public class XPlatformPlugin { ...@@ -95,26 +95,25 @@ public class XPlatformPlugin {
} }
}; };
public XPlatformPlugin( PlatformChannel platformChannel) { public XPlatformPlugin(PlatformChannel platformChannel) {
this.platformChannel = platformChannel; this.platformChannel = platformChannel;
this.platformChannel.setPlatformMessageHandler(mPlatformMessageHandler);
mEnabledOverlays = DEFAULT_SYSTEM_UI; mEnabledOverlays = DEFAULT_SYSTEM_UI;
} }
public void attachToActivity(Activity activity ){ public void attachToActivity(Activity activity ){
this.activity = activity; this.activity = activity;
this.platformChannel.setPlatformMessageHandler(mPlatformMessageHandler);
} }
/** /**
* Releases all resources held by this {@code PlatformPlugin}. * Releases all resources held by this {@code PlatformPlugin}.
* <p> * <p>
* Do not invoke any methods on a {@code PlatformPlugin} after invoking this method. * Do not invoke any methods on a {@code PlatformPlugin} after invoking this method.
*/ */
public void detachActivity() { public void detachActivity(Activity activity) {
this.activity=null; if (activity == this.activity) {
this.mPlatformMessageHandler=null; this.activity = null;
}
} }
private void playSystemSound(PlatformChannel.SoundType soundType) { private void playSystemSound(PlatformChannel.SoundType soundType) {
......
// 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; package com.idlefish.flutterboost;
import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import androidx.annotation.NonNull; import android.os.Build;
import androidx.annotation.Nullable; import android.provider.Settings;
import android.text.Editable; import android.text.Editable;
import android.text.InputType; import android.text.InputType;
import android.text.Selection; import android.text.Selection;
...@@ -11,6 +17,11 @@ import android.view.inputmethod.BaseInputConnection; ...@@ -11,6 +17,11 @@ import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.EditorInfo; import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager; 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.dart.DartExecutor;
import io.flutter.embedding.engine.systemchannels.TextInputChannel; import io.flutter.embedding.engine.systemchannels.TextInputChannel;
...@@ -21,11 +32,11 @@ import io.flutter.plugin.platform.PlatformViewsController; ...@@ -21,11 +32,11 @@ import io.flutter.plugin.platform.PlatformViewsController;
*/ */
public class XTextInputPlugin { public class XTextInputPlugin {
@NonNull @NonNull
private View mView; private View mView;
@NonNull @NonNull
private final InputMethodManager mImm; private InputMethodManager mImm;
@NonNull @NonNull
private final TextInputChannel textInputChannel; private TextInputChannel textInputChannel;
@NonNull @NonNull
private InputTarget inputTarget = new InputTarget(InputTarget.Type.NO_TARGET, 0); private InputTarget inputTarget = new InputTarget(InputTarget.Type.NO_TARGET, 0);
@Nullable @Nullable
...@@ -37,26 +48,43 @@ public class XTextInputPlugin { ...@@ -37,26 +48,43 @@ public class XTextInputPlugin {
private InputConnection lastInputConnection; private InputConnection lastInputConnection;
@NonNull @NonNull
private PlatformViewsController platformViewsController; private PlatformViewsController platformViewsController;
private boolean restartAlwaysRequired;
// When true following calls to createInputConnection will return the cached lastInputConnection if the input // 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. // target is a platform view. See the comments on lockPlatformViewInputConnection for more details.
private boolean isInputConnectionLocked; 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; textInputChannel = new TextInputChannel(dartExecutor);
mImm = (InputMethodManager) view.getContext().getSystemService(
Context.INPUT_METHOD_SERVICE);
this.textInputChannel = textInputChannel; textInputChannel.requestExistingInputState();
this.platformViewsController = platformViewsController; this.platformViewsController = platformViewsController;
// this.platformViewsController.attachTextInputPlugin(this); // this.platformViewsController.attachTextInputPlugin(this);
} }
public void release() {
mView = null;
public void release(View v){
if(mView!=null && mView.hashCode()==v.hashCode()){
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() { textInputChannel.setTextInputMethodHandler(new TextInputChannel.TextInputMethodHandler() {
@Override @Override
...@@ -89,8 +117,12 @@ public class XTextInputPlugin { ...@@ -89,8 +117,12 @@ public class XTextInputPlugin {
clearTextInputClient(); clearTextInputClient();
} }
}); });
restartAlwaysRequired = isRestartAlwaysRequired();
} }
@NonNull @NonNull
public InputMethodManager getInputMethodManager() { public InputMethodManager getInputMethodManager() {
return mImm; return mImm;
...@@ -114,7 +146,7 @@ public class XTextInputPlugin { ...@@ -114,7 +146,7 @@ public class XTextInputPlugin {
/** /**
* Unlocks the input connection. * Unlocks the input connection.
* <p> *
* See also: @{link lockPlatformViewInputConnection}. * See also: @{link lockPlatformViewInputConnection}.
*/ */
public void unlockPlatformViewInputConnection() { public void unlockPlatformViewInputConnection() {
...@@ -123,7 +155,7 @@ public class XTextInputPlugin { ...@@ -123,7 +155,7 @@ public class XTextInputPlugin {
/** /**
* Detaches the text input plugin from the platform views controller. * Detaches the text input plugin from the platform views controller.
* <p> *
* The TextInputPlugin instance should not be used after calling this. * The TextInputPlugin instance should not be used after calling this.
*/ */
public void destroy() { public void destroy() {
...@@ -134,6 +166,7 @@ public class XTextInputPlugin { ...@@ -134,6 +166,7 @@ public class XTextInputPlugin {
TextInputChannel.InputType type, TextInputChannel.InputType type,
boolean obscureText, boolean obscureText,
boolean autocorrect, boolean autocorrect,
boolean enableSuggestions,
TextInputChannel.TextCapitalization textCapitalization TextInputChannel.TextCapitalization textCapitalization
) { ) {
if (type.type == TextInputChannel.TextInputType.DATETIME) { if (type.type == TextInputChannel.TextInputType.DATETIME) {
...@@ -168,6 +201,7 @@ public class XTextInputPlugin { ...@@ -168,6 +201,7 @@ public class XTextInputPlugin {
textType |= InputType.TYPE_TEXT_VARIATION_PASSWORD; textType |= InputType.TYPE_TEXT_VARIATION_PASSWORD;
} else { } else {
if (autocorrect) textType |= InputType.TYPE_TEXT_FLAG_AUTO_CORRECT; if (autocorrect) textType |= InputType.TYPE_TEXT_FLAG_AUTO_CORRECT;
if (!enableSuggestions) textType |= InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
} }
if (textCapitalization == TextInputChannel.TextCapitalization.CHARACTERS) { if (textCapitalization == TextInputChannel.TextCapitalization.CHARACTERS) {
...@@ -191,19 +225,15 @@ public class XTextInputPlugin { ...@@ -191,19 +225,15 @@ public class XTextInputPlugin {
if (isInputConnectionLocked) { if (isInputConnectionLocked) {
return lastInputConnection; return lastInputConnection;
} }
View platformView = platformViewsController.getPlatformViewById(inputTarget.id); lastInputConnection = platformViewsController.getPlatformViewById(inputTarget.id).onCreateInputConnection(outAttrs);
if (platformView != null) { return lastInputConnection;
lastInputConnection = platformView.onCreateInputConnection(outAttrs);
return lastInputConnection;
} else {
return null;
}
} }
outAttrs.inputType = inputTypeFromTextInputType( outAttrs.inputType = inputTypeFromTextInputType(
configuration.inputType, configuration.inputType,
configuration.obscureText, configuration.obscureText,
configuration.autocorrect, configuration.autocorrect,
configuration.enableSuggestions,
configuration.textCapitalization configuration.textCapitalization
); );
outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN; outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN;
...@@ -243,7 +273,7 @@ public class XTextInputPlugin { ...@@ -243,7 +273,7 @@ public class XTextInputPlugin {
/** /**
* Clears a platform view text input client if it is the current input target. * 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 * This is called when a platform view is disposed to make sure we're not hanging to a stale input
* connection. * connection.
*/ */
...@@ -269,7 +299,8 @@ public class XTextInputPlugin { ...@@ -269,7 +299,8 @@ public class XTextInputPlugin {
mImm.hideSoftInputFromWindow(view.getApplicationWindowToken(), 0); 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); inputTarget = new InputTarget(InputTarget.Type.FRAMEWORK_CLIENT, client);
this.configuration = configuration; this.configuration = configuration;
mEditable = Editable.Factory.getInstance().newEditable(""); mEditable = Editable.Factory.getInstance().newEditable("");
...@@ -301,8 +332,8 @@ public class XTextInputPlugin { ...@@ -301,8 +332,8 @@ public class XTextInputPlugin {
} }
} }
private void setTextInputEditingState(View view, TextInputChannel.TextEditState state) { @VisibleForTesting void setTextInputEditingState(View view, TextInputChannel.TextEditState state) {
if (!mRestartInputPending && state.text.equals(mEditable.toString())) { if (!restartAlwaysRequired && !mRestartInputPending && state.text.equals(mEditable.toString())) {
applyStateToSelection(state); applyStateToSelection(state);
mImm.updateSelection(mView, Math.max(Selection.getSelectionStart(mEditable), 0), mImm.updateSelection(mView, Math.max(Selection.getSelectionStart(mEditable), 0),
Math.max(Selection.getSelectionEnd(mEditable), 0), Math.max(Selection.getSelectionEnd(mEditable), 0),
...@@ -316,6 +347,30 @@ public class XTextInputPlugin { ...@@ -316,6 +347,30 @@ 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.
if(keyboardName==null) return false;
return keyboardName.contains("Samsung");
}
private void clearTextInputClient() { private void clearTextInputClient() {
if (inputTarget.type == InputTarget.Type.PLATFORM_VIEW) { 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 // Focus changes in the framework tree have no guarantees on the order focus nodes are notified. A node
...@@ -358,4 +413,4 @@ public class XTextInputPlugin { ...@@ -358,4 +413,4 @@ public class XTextInputPlugin {
// For platform views this is the platform view's ID. // For platform views this is the platform view's ID.
int id; int id;
} }
} }
\ No newline at end of file
package com.idlefish.flutterboost.containers; package com.idlefish.flutterboost.containers;
import android.annotation.SuppressLint;
import android.app.Activity; import android.app.Activity;
import androidx.lifecycle.Lifecycle; import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LifecycleOwner;
...@@ -56,6 +57,7 @@ public class BoostFlutterActivity extends Activity ...@@ -56,6 +57,7 @@ public class BoostFlutterActivity extends Activity
// Default configuration. // Default configuration.
protected static final String DEFAULT_BACKGROUND_MODE = BackgroundMode.opaque.name(); protected static final String DEFAULT_BACKGROUND_MODE = BackgroundMode.opaque.name();
private static XPlatformPlugin sXPlatformPlugin;
public static Intent createDefaultIntent(@NonNull Context launchContext) { public static Intent createDefaultIntent(@NonNull Context launchContext) {
return withNewEngine().build(launchContext); return withNewEngine().build(launchContext);
...@@ -185,6 +187,7 @@ public class BoostFlutterActivity extends Activity ...@@ -185,6 +187,7 @@ public class BoostFlutterActivity extends Activity
*/ */
@Nullable @Nullable
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
@SuppressLint("WrongConstant")
private Drawable getSplashScreenFromManifest() { private Drawable getSplashScreenFromManifest() {
try { try {
ActivityInfo activityInfo = getPackageManager().getActivityInfo( ActivityInfo activityInfo = getPackageManager().getActivityInfo(
...@@ -440,8 +443,8 @@ public class BoostFlutterActivity extends Activity ...@@ -440,8 +443,8 @@ public class BoostFlutterActivity extends Activity
@Nullable @Nullable
@Override @Override
public XPlatformPlugin providePlatformPlugin( @NonNull FlutterEngine flutterEngine) { public XPlatformPlugin providePlatformPlugin(@NonNull FlutterEngine flutterEngine) {
return new XPlatformPlugin( flutterEngine.getPlatformChannel()); return BoostViewUtils.getPlatformPlugin(flutterEngine.getPlatformChannel());
} }
/** /**
......
package com.idlefish.flutterboost.containers;
import com.idlefish.flutterboost.XPlatformPlugin;
import io.flutter.embedding.engine.systemchannels.PlatformChannel;
class BoostViewUtils {
private static volatile XPlatformPlugin mInstance;
private BoostViewUtils() {
}
public static XPlatformPlugin getPlatformPlugin(PlatformChannel channel) {
if (mInstance == null) {
synchronized (BoostViewUtils.class) {
if (mInstance == null) {
mInstance = new XPlatformPlugin(channel);
}
}
}
return mInstance;
}
}
...@@ -230,7 +230,7 @@ public class FlutterActivityAndFragmentDelegate implements IFlutterViewContainer ...@@ -230,7 +230,7 @@ public class FlutterActivityAndFragmentDelegate implements IFlutterViewContainer
// Null out the platformPlugin to avoid a possible retain cycle between the plugin, this Fragment, // Null out the platformPlugin to avoid a possible retain cycle between the plugin, this Fragment,
// and this Fragment's Activity. // and this Fragment's Activity.
if (platformPlugin != null) { if (platformPlugin != null) {
platformPlugin.detachActivity(); platformPlugin.detachActivity(getContextActivity());
platformPlugin = null; platformPlugin = null;
} }
......
...@@ -14,13 +14,11 @@ import androidx.fragment.app.Fragment; ...@@ -14,13 +14,11 @@ import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentActivity;
import com.idlefish.flutterboost.FlutterBoost; import com.idlefish.flutterboost.FlutterBoost;
import com.idlefish.flutterboost.Utils;
import com.idlefish.flutterboost.XFlutterView; import com.idlefish.flutterboost.XFlutterView;
import com.idlefish.flutterboost.XPlatformPlugin; import com.idlefish.flutterboost.XPlatformPlugin;
import io.flutter.embedding.android.*; import io.flutter.embedding.android.*;
import io.flutter.embedding.engine.FlutterEngine; import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.embedding.engine.FlutterShellArgs; import io.flutter.embedding.engine.FlutterShellArgs;
import io.flutter.plugin.platform.PlatformPlugin;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
...@@ -469,8 +467,7 @@ public class FlutterFragment extends Fragment implements FlutterActivityAndFragm ...@@ -469,8 +467,7 @@ public class FlutterFragment extends Fragment implements FlutterActivityAndFragm
@Nullable @Nullable
@Override @Override
public XPlatformPlugin providePlatformPlugin( @NonNull FlutterEngine flutterEngine) { public XPlatformPlugin providePlatformPlugin( @NonNull FlutterEngine flutterEngine) {
return new XPlatformPlugin(flutterEngine.getPlatformChannel()); return BoostViewUtils.getPlatformPlugin(flutterEngine.getPlatformChannel());
} }
/** /**
......
...@@ -69,6 +69,7 @@ ...@@ -69,6 +69,7 @@
@property (nonatomic,assign) long long identifier; @property (nonatomic,assign) long long identifier;
@property (nonatomic, copy) NSString *flbNibName; @property (nonatomic, copy) NSString *flbNibName;
@property (nonatomic, strong) NSBundle *flbNibBundle; @property (nonatomic, strong) NSBundle *flbNibBundle;
@property (nonatomic, assign) BOOL deallocNotified;
@end @end
#pragma clang diagnostic push #pragma clang diagnostic push
...@@ -163,6 +164,7 @@ static NSUInteger kInstanceCounter = 0; ...@@ -163,6 +164,7 @@ static NSUInteger kInstanceCounter = 0;
pageName:_name pageName:_name
params:_params params:_params
uniqueId:[self uniqueIDString]]; uniqueId:[self uniqueIDString]];
self.deallocNotified = NO;
} }
[super willMoveToParentViewController:parent]; [super willMoveToParentViewController:parent];
} }
...@@ -171,23 +173,29 @@ static NSUInteger kInstanceCounter = 0; ...@@ -171,23 +173,29 @@ static NSUInteger kInstanceCounter = 0;
if (!parent) { if (!parent) {
//当VC被移出parent时,就通知flutter层销毁page //当VC被移出parent时,就通知flutter层销毁page
[self notifyWillDealloc]; [self notifyWillDealloc];
self.deallocNotified = YES;
} }
[super didMoveToParentViewController:parent]; [super didMoveToParentViewController:parent];
} }
- (void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion { - (void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion {
__weak __typeof__(self) weakSelf = self;
[super dismissViewControllerAnimated:flag completion:^(){ [super dismissViewControllerAnimated:flag completion:^(){
__strong __typeof__(weakSelf) self = weakSelf;
if (completion) { if (completion) {
completion(); completion();
} }
//当VC被dismiss时,就通知flutter层销毁page //当VC被dismiss时,就通知flutter层销毁page
[self notifyWillDealloc]; [self notifyWillDealloc];
self.deallocNotified = YES;
}]; }];
} }
- (void)dealloc - (void)dealloc
{ {
if (!self.deallocNotified) {
[self notifyWillDealloc];
}
[NSNotificationCenter.defaultCenter removeObserver:self]; [NSNotificationCenter.defaultCenter removeObserver:self];
} }
......
...@@ -39,9 +39,9 @@ enum ContainerLifeCycle { ...@@ -39,9 +39,9 @@ enum ContainerLifeCycle {
} }
typedef BoostContainerLifeCycleObserver = void Function( typedef BoostContainerLifeCycleObserver = void Function(
ContainerLifeCycle state, ContainerLifeCycle state,
BoostContainerSettings settings, BoostContainerSettings settings,
); );
class BoostContainer extends Navigator { class BoostContainer extends Navigator {
const BoostContainer({ const BoostContainer({
...@@ -52,17 +52,17 @@ class BoostContainer extends Navigator { ...@@ -52,17 +52,17 @@ class BoostContainer extends Navigator {
RouteFactory onUnknownRoute, RouteFactory onUnknownRoute,
List<NavigatorObserver> observers, List<NavigatorObserver> observers,
}) : super( }) : super(
key: key, key: key,
initialRoute: initialRoute, initialRoute: initialRoute,
onGenerateRoute: onGenerateRoute, onGenerateRoute: onGenerateRoute,
onUnknownRoute: onUnknownRoute, onUnknownRoute: onUnknownRoute,
observers: observers, observers: observers,
); );
factory BoostContainer.copy( factory BoostContainer.copy(
Navigator navigator, [ Navigator navigator, [
BoostContainerSettings settings = const BoostContainerSettings(), BoostContainerSettings settings = const BoostContainerSettings(),
]) => ]) =>
BoostContainer( BoostContainer(
key: GlobalKey<BoostContainerState>(), key: GlobalKey<BoostContainerState>(),
settings: settings, settings: settings,
...@@ -73,9 +73,9 @@ class BoostContainer extends Navigator { ...@@ -73,9 +73,9 @@ class BoostContainer extends Navigator {
); );
factory BoostContainer.obtain( factory BoostContainer.obtain(
Navigator navigator, Navigator navigator,
BoostContainerSettings settings, BoostContainerSettings settings,
) => ) =>
BoostContainer( BoostContainer(
key: GlobalKey<BoostContainerState>(), key: GlobalKey<BoostContainerState>(),
settings: settings, settings: settings,
...@@ -86,7 +86,11 @@ class BoostContainer extends Navigator { ...@@ -86,7 +86,11 @@ class BoostContainer extends Navigator {
params: settings.params, params: settings.params,
uniqueId: settings.uniqueId, uniqueId: settings.uniqueId,
animated: false, animated: false,
settings: routeSettings, settings: RouteSettings(
name: settings.name,
isInitialRoute: routeSettings.isInitialRoute,
arguments: routeSettings.arguments,
),
builder: settings.builder, builder: settings.builder,
); );
} else { } else {
...@@ -110,18 +114,20 @@ class BoostContainer extends Navigator { ...@@ -110,18 +114,20 @@ class BoostContainer extends Navigator {
static BoostContainerState tryOf(BuildContext context) { static BoostContainerState tryOf(BuildContext context) {
final BoostContainerState container = final BoostContainerState container =
context.findAncestorStateOfType<BoostContainerState>(); context.findAncestorStateOfType<BoostContainerState>();
return container; return container;
} }
static BoostContainerState of(BuildContext context) { static BoostContainerState of(BuildContext context) {
final BoostContainerState container = final BoostContainerState container =
context.findAncestorStateOfType<BoostContainerState>(); context.findAncestorStateOfType<BoostContainerState>();
assert(container != null, 'not in flutter boost'); assert(container != null, 'not in flutter boost');
return container; return container;
} }
String desc() => '{uniqueId=${settings.uniqueId},name=${settings.name}}'; String desc() => '{uniqueId=${settings.uniqueId},name=${settings.name}}';
RouteListFactory get initialRoutes => super.onGenerateInitialRoutes;
} }
class BoostContainerState extends NavigatorState { class BoostContainerState extends NavigatorState {
...@@ -161,6 +167,15 @@ class BoostContainerState extends NavigatorState { ...@@ -161,6 +167,15 @@ class BoostContainerState extends NavigatorState {
void initState() { void initState() {
super.initState(); super.initState();
backPressedHandler = () => maybePop(); backPressedHandler = () => maybePop();
final String initRoute = widget.initialRoute ?? Navigator.defaultRouteName;
if (initRoute != null && routerHistory.isEmpty) {
routerHistory.addAll(
widget.initialRoutes(
this,
widget.initialRoute ?? Navigator.defaultRouteName
)
);
}
} }
@override @override
...@@ -177,22 +192,28 @@ class BoostContainerState extends NavigatorState { ...@@ -177,22 +192,28 @@ class BoostContainerState extends NavigatorState {
@override @override
Future<bool> maybePop<T extends Object>([T result]) async { Future<bool> maybePop<T extends Object>([T result]) async {
if(routerHistory.isEmpty) {
pop(result);
return true;
}
final Route<T> route = routerHistory.last as Route<T>; final Route<T> route = routerHistory.last as Route<T>;
final RoutePopDisposition disposition = await route.willPop(); final RoutePopDisposition disposition = await route.willPop();
if (mounted) { if (mounted) {
switch (disposition) { switch (disposition) {
case RoutePopDisposition.pop: case RoutePopDisposition.pop:
pop(result); pop(result);
return true; return true;
break; break;
case RoutePopDisposition.doNotPop: case RoutePopDisposition.doNotPop:
return false; return false;
break; break;
case RoutePopDisposition.bubble: case RoutePopDisposition.bubble:
pop(result); pop(result);
return true; return true;
break; break;
} }
} }
return false; return false;
} }
...@@ -238,10 +259,10 @@ class BoostContainerState extends NavigatorState { ...@@ -238,10 +259,10 @@ class BoostContainerState extends NavigatorState {
VoidCallback addLifeCycleObserver(BoostContainerLifeCycleObserver observer) { VoidCallback addLifeCycleObserver(BoostContainerLifeCycleObserver observer) {
return FlutterBoost.singleton.addBoostContainerLifeCycleObserver( return FlutterBoost.singleton.addBoostContainerLifeCycleObserver(
( (
ContainerLifeCycle state, ContainerLifeCycle state,
BoostContainerSettings settings, BoostContainerSettings settings,
) { ) {
if (settings.uniqueId == uniqueId) { if (settings.uniqueId == uniqueId) {
observer(state, settings); observer(state, settings);
} }
......
...@@ -62,4 +62,9 @@ class BoostPageRoute<T> extends MaterialPageRoute<T> { ...@@ -62,4 +62,9 @@ class BoostPageRoute<T> extends MaterialPageRoute<T> {
return null; return null;
} }
} }
@override
Future<RoutePopDisposition> willPop() {
return Future<RoutePopDisposition>.value(RoutePopDisposition.pop);
}
} }
...@@ -69,7 +69,6 @@ class ContainerManagerState extends State<BoostContainerManager> { ...@@ -69,7 +69,6 @@ class ContainerManagerState extends State<BoostContainerManager> {
final List<BoostContainer> _offstage = <BoostContainer>[]; final List<BoostContainer> _offstage = <BoostContainer>[];
List<_ContainerOverlayEntry> _leastEntries; List<_ContainerOverlayEntry> _leastEntries;
BoostContainer _onstage; BoostContainer _onstage;
bool _foreground = true; bool _foreground = true;
...@@ -158,15 +157,20 @@ class ContainerManagerState extends State<BoostContainerManager> { ...@@ -158,15 +157,20 @@ class ContainerManagerState extends State<BoostContainerManager> {
} }
} }
final List<BoostContainer> containers = <BoostContainer>[]; final List<Widget> containers = <Widget>[];
containers.addAll(_offstage); containers.addAll(_offstage.map<Widget>(
(BoostContainer container) => HeroControllerScope(
controller: null,
child: container
)
));
assert(_onstage != null, 'Should have a least one BoostContainer'); assert(_onstage != null, 'Should have a least one BoostContainer');
containers.add(_onstage); containers.add(_onstage);
_leastEntries = containers _leastEntries = containers
.map<_ContainerOverlayEntry>( .map<_ContainerOverlayEntry>(
(BoostContainer container) => _ContainerOverlayEntry(container)) (Widget container) => _ContainerOverlayEntry(container))
.toList(growable: false); .toList(growable: false);
overlayState.insertAll(_leastEntries); overlayState.insertAll(_leastEntries);
...@@ -336,7 +340,7 @@ class ContainerManagerState extends State<BoostContainerManager> { ...@@ -336,7 +340,7 @@ class ContainerManagerState extends State<BoostContainerManager> {
} }
class _ContainerOverlayEntry extends OverlayEntry { class _ContainerOverlayEntry extends OverlayEntry {
_ContainerOverlayEntry(BoostContainer container) _ContainerOverlayEntry(Widget container)
: super( : super(
builder: (BuildContext ctx) => container, builder: (BuildContext ctx) => container,
opaque: true, opaque: true,
......
...@@ -38,7 +38,7 @@ class ObserversHolder { ...@@ -38,7 +38,7 @@ class ObserversHolder {
void removeObserver<T>(T observer) => void removeObserver<T>(T observer) =>
_observers[T.toString()]?.remove(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>{}; 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