BoostFlutterActivity implements FlutterActivityAndFragmentDelegate.Host {
private FlutterActivityAndFragmentDelegate delegate;
public static class NewEngineIntentBuilder {
public Intent build(@NonNull Context context) {
SerializableMap serializableMap = new SerializableMap();
serializableMap.setMap(params);
return new Intent(context, activityClass)
.putExtra(EXTRA_BACKGROUND_MODE, backgroundMode)
.putExtra(EXTRA_DESTROY_ENGINE_WITH_ACTIVITY, false)
.putExtra(EXTRA_URL, url)
.putExtra(EXTRA_PARAMS, serializableMap);
}
@Override
public String getContainerUrl() {
if (getIntent().hasExtra(EXTRA_URL)) {
return getIntent().getStringExtra(EXTRA_URL);
}
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
delegate = new FlutterActivityAndFragmentDelegate(this);
delegate.onAttach(this);
setContentView(createFlutterView());
}
FlutterActivityAndFragmentDelegate implements IFlutterViewContainer {
protected IOperateSyncer mSyncer;
FlutterActivityAndFragmentDelegate(@NonNull Host host) {
this.host = host;
}
void onAttach(@NonNull Context context) {
// When "retain instance" is true, the FlutterEngine will survive configuration
// changes. Therefore, we create a new one only if one does not already exist.
if (flutterEngine == null) {
setupFlutterEngine();
}
}
private void setupFlutterEngine() {
// Second, defer to subclasses for a custom FlutterEngine.
flutterEngine = host.provideFlutterEngine(host.getContext());
if (flutterEngine != null) {
isFlutterEngineFromHost = true;
return;
}
}
}
/**
\* Hook for subclasses to easily provide a custom {@link FlutterEngine}.
\* <p>
\* This hook is where a cached {@link FlutterEngine} should be provided, if a cached
\* {@link FlutterEngine} is desired.
*/
@Nullable
@Override
public FlutterEngine provideFlutterEngine(@NonNull Context context) {
// No-op. Hook for subclasses.
return FlutterBoost.instance().engineProvider();
}
@NonNull
protected View createFlutterView() {
return delegate.onCreateView(
null /* inflater */,
null /* container */,
null /* savedInstanceState */);
}
View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
Log.v(TAG, "Creating FlutterView.");
flutterEngine.getActivityControlSurface().attachToActivity(
host.getActivity(),
host.getLifecycle()
);
mSyncer = FlutterBoost.instance().containerManager().generateSyncer(this);
mSyncer.onCreate();
}
ContainerRecord implements IContainerRecord implements IOperateSyncer {
private MethodChannelProxy mProxy = new MethodChannelProxy();
@Override
public void onCreate() {
mState = STATE_CREATED;
mProxy.create();
}
private class MethodChannelProxy {
private void create() {
if (mState == STATE_UNKNOW) {
invokeChannelUnsafe("didInitPageContainer",
mContainer.getContainerUrl(),
mContainer.getContainerUrlParams(),
mUniqueId
);
//Debuger.log("didInitPageContainer");
mState = STATE_CREATED;
}
}
public void invokeChannelUnsafe(String method, String url, Map params, String uniqueId) {
HashMap<String, Object> args = new HashMap<>();
args.put("pageName", url);
args.put("params", params);
args.put("uniqueId", uniqueId);
FlutterBoost.instance().channel().invokeMethodUnsafe(method, args);//goto dart ContainerCoordinator._onMethodCall,利用flutter自带的methodChannel机制,反向调用native->dart
}
}
//ContainerCoordinator
Future<dynamic> _onMethodCall(MethodCall call) {
Logger.log("onMetohdCall ${call.method}");
switch (call.method) {
case "didInitPageContainer":
{
String pageName = call.arguments["pageName"];
Map params = call.arguments["params"];
String uniqueId = call.arguments["uniqueId"];
_nativeContainerDidInit(pageName, params, uniqueId);
}
break;
case "didShowPageContainer":
{
String pageName = call.arguments["pageName"];
Map params = call.arguments["params"];
String uniqueId = call.arguments["uniqueId"];
nativeContainerDidShow(pageName, params, uniqueId);
}
break;
bool _nativeContainerDidInit(String name, Map params, String pageId) {
performContainerLifeCycle(_createContainerSettings(name, params, pageId),ContainerLifeCycle.Init);
return true;
}
BoostContainerSettings _createContainerSettings(
String name, Map params, String pageId) {
Widget page;
final BoostContainerSettings routeSettings = BoostContainerSettings(//main
uniqueId: pageId,
name: name,
params: params,
builder: (BuildContext ctx) {//main
//Try to build a page using keyed builder.
if (_pageBuilders[name] != null) {
page = _pageBuilders[name](name, params, pageId);//main
}
//Build a page using default builder.
if (page == null && _defaultPageBuilder != null) {
page = _defaultPageBuilder(name, params, pageId);
}
assert(page != null);
Logger.log('build widget:$page for page:$name($pageId)');
return page;
});
return routeSettings;
}
@Override
protected void onResume() {
super.onResume();
lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
delegate.onResume();
}
void onResume() {
mSyncer.onAppear();//main
ensureAlive();
flutterEngine.getLifecycleChannel().appIsResumed();
BoostPluginRegistry registry = (BoostPluginRegistry) FlutterBoost.instance().getPluginRegistry();
ActivityPluginBinding binding = registry.getRegistrarAggregate().getActivityPluginBinding();
if (binding != null && (binding.getActivity() != this.host.getActivity())) {
flutterEngine.getActivityControlSurface().attachToActivity(
host.getActivity(),
host.getLifecycle()
);
}
}
//ContainerRecord
@Override
public void onAppear() {
Utils.assertCallOnMainThread();
if (mState != STATE_CREATED && mState != STATE_DISAPPEAR) {
Debuger.exception("state error");
}
mState = STATE_APPEAR;
mManager.pushRecord(this);
mProxy.appear();
mContainer.getBoostFlutterView().onAttach();
}
bool nativeContainerDidShow(String name, Map params, String pageId) {
FlutterBoost.containerManager?.showContainer(_createContainerSettings(name, params, pageId));
BoostContainerManager extends StatefulWidget {
@override
ContainerManagerState createState() => ContainerManagerState();
}
ContainerManagerState extends State<BoostContainerManager> {
BoostContainer _onstage;
//Current visible container.
BoostContainerState get onstageContainer => _stateOf(_onstage);
List<_ContainerOverlayEntry> _leastEntries;
final GlobalKey<OverlayState> _overlayKey = GlobalKey<OverlayState>();
//If container exists bring it to front else create a container.
void showContainer(BoostContainerSettings settings) {
if (settings.uniqueId == _onstage.settings.uniqueId) {
_onShownContainerChanged(null, settings.uniqueId);
return;
}
final int index = _offstage.indexWhere((BoostContainer container) =>
container.settings.uniqueId == settings.uniqueId);
if (index > -1) {
_offstage.add(_onstage);
_onstage = _offstage.removeAt(index);
setState(() {});
} else {
pushContainer(settings);//main
}
}
}
void pushContainer(BoostContainerSettings settings) {
assert(settings.uniqueId != _onstage.settings.uniqueId);
assert(_offstage.every((BoostContainer container) => container.settings.uniqueId != settings.uniqueId));
_offstage.add(_onstage);
_onstage = BoostContainer.obtain(widget.initNavigator, settings);//main
setState(() {});
}
BoostContainer extends Navigator {
final BoostContainerSettings settings;
@override
BoostContainerState createState() => BoostContainerState();
@override
StatefulElement createElement() => ContainerElement(this);
factory BoostContainer.obtain(
Navigator navigator, BoostContainerSettings settings) =>
BoostContainer(
key: GlobalKey<BoostContainerState>(),
settings: settings,
onGenerateRoute: (RouteSettings routeSettings) {//main
if (routeSettings.name == '/') {
return BoostPageRoute<dynamic>(//main
pageName: settings.name,
params: settings.params,
uniqueId: settings.uniqueId,
animated: false,
settings: routeSettings,
builder: settings.builder);//main
} else {
return navigator.onGenerateRoute(routeSettings);
}
},
observers: <NavigatorObserver>[
ContainerNavigatorObserver.bindContainerManager()
],
onUnknownRoute: navigator.onUnknownRoute);
}
BoostContainerState extends NavigatorState
@override
void setState(VoidCallback fn) {
if (SchedulerBinding.instance.schedulerPhase ==
SchedulerPhase.persistentCallbacks) {
SchedulerBinding.instance.addPostFrameCallback((Duration duration) {
_refreshOverlayEntries();
});
} else {
_refreshOverlayEntries();//main
}
fn();
//return super.setState(fn);
}
void _refreshOverlayEntries() {
final OverlayState overlayState = _overlayKey.currentState;
final List<BoostContainer> containers = <BoostContainer>[];
containers.addAll(_offstage);
assert(_onstage != null, 'Should have a least one BoostContainer');
containers.add(_onstage);
_leastEntries = containers
.map<_ContainerOverlayEntry>(
(BoostContainer container) => _ContainerOverlayEntry(container))
.toList(growable: false);
overlayState.insertAll(_leastEntries);//main
SchedulerBinding.instance.addPostFrameCallback((Duration timeStamp) {
final String now = _onstage.settings.uniqueId;
if (_lastShownContainer != now) {
final String old = _lastShownContainer;
_lastShownContainer = now;
_onShownContainerChanged(old, now);//main
}
updateFocuse();
});
void _onShownContainerChanged(String old, String now) {
FlutterBoost.singleton.channel.invokeMethod('onShownContainerChanged',properties);
}
BoostPageRoute<T> extends MaterialPageRoute<T> {
final String pageName;
final String uniqueId;
final Map params;
final bool animated;
final WidgetBuilder builder;
final RouteSettings settings;
static BoostPageRoute<T> of<T>(BuildContext context) {
final Route<T> route = ModalRoute.of(context);
if (route != null && route is BoostPageRoute<T>) {
return route;
} else {
throw Exception('not in a BoostPageRoute');
}
}
BoostPageRoute(
{Key stubKey,
this.pageName,
this.params,
this.uniqueId,
this.animated,
this.builder,
this.settings})
: super(
builder: (BuildContext context) => Stub(stubKey, builder(context)),
settings: settings);
@immutable
class Stub extends StatefulWidget {
final Widget child;
const Stub(Key key, this.child) : super(key: key);
@override
_StubState createState() => _StubState();
}
class _StubState extends State<Stub> {
@override
Widget build(BuildContext context) => widget.child;
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
void initState() {
super.initState();
FlutterBoost.singleton.registerPageBuilders({//main
'embeded': (pageName, params, _)=>EmbededFirstRouteWidget(),
'first': (pageName, params, _) => FirstRouteWidget(),
'second': (pageName, params, _) => SecondRouteWidget(),
'tab': (pageName, params, _) => TabRouteWidget(),
'platformView': (pageName, params, _) => PlatformRouteWidget(),
'flutterFragment': (pageName, params, _) => FragmentRouteWidget(params),
///可以在native层通过 getContainerParams 来传递参数
'flutterPage': (pageName, params, _) {
print("flutterPage params:$params");
return FlutterRouteWidget(params:params);
},
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Boost example',
builder: FlutterBoost.init(postPush: _onRoutePushed),//main
home: Container());
}
void _onRoutePushed(
String pageName, String uniqueId, Map params, Route route, Future _) {
}
FlutterBoost
typedef Widget PageBuilder(String pageName, Map params, String uniqueId);
///Register a map builders
void registerPageBuilders(Map<String, PageBuilder> builders) {
ContainerCoordinator.singleton.registerPageBuilders(builders);
}
ContainerCoordinator
void registerPageBuilders(Map<String, PageBuilder> builders) {
if (builders?.isNotEmpty == true) {
_pageBuilders.addAll(builders);//main
}
}
FlutterBoost
static TransitionBuilder init(
{TransitionBuilder builder,
PrePushRoute prePush,
PostPushRoute postPush}) {
if (Platform.isAndroid) {
onPageStart();
}
return (BuildContext context, Widget child) {
assert(child is Navigator, 'child must be Navigator, what is wrong?');
final BoostContainerManager manager = BoostContainerManager(
key: _instance.containerManagerKey,
initNavigator: child,
prePushRoute: prePush,
postPushRoute: postPush);
if (builder != null) {
return builder(context, manager);
} else {
return manager;
}
};
Future<Map<dynamic, dynamic>> open(String url,
{Map<dynamic, dynamic> urlParams, Map<dynamic, dynamic> exts}) {
Map<dynamic, dynamic> properties = new Map<dynamic, dynamic>();
properties["url"] = url;
properties["urlParams"] = urlParams;
properties["exts"] = exts;
return channel.invokeMethod<Map<dynamic, dynamic>>('openPage', properties);
}
/* <p>
* To start rendering Flutter content to the screen, use {@link #getRenderer()} to obtain a
* {@link FlutterRenderer} and then attach a {@link RenderSurface}. Consider using a
* {@link io.flutter.embedding.android.FlutterView} as a {@link RenderSurface}.*/
FlutterEngine
/**
\* The rendering system associated with this {@code FlutterEngine}.
\* <p>
\* To render a Flutter UI that is produced by this {@code FlutterEngine}'s Dart code, attach
\* a {@link RenderSurface} to this
\* {@link FlutterRenderer}.
*/
@NonNull
public FlutterRenderer getRenderer() {
return renderer;
}
private final FlutterJNI flutterJNI;
/**
\* Notifies Flutter that the given {@code surface} was created and is available for Flutter
\* rendering.
\* <p>
\* See {@link android.view.SurfaceHolder.Callback} and
\* {@link android.view.TextureView.SurfaceTextureListener}
*/
public void startRenderingToSurface(@NonNull Surface surface) {
if (this.surface != null) {
stopRenderingToSurface();
}
this.surface = surface;
flutterJNI.onSurfaceCreated(surface);
}
/**
\* Owns a {@code Surface} that {@code FlutterRenderer} would like to paint.
\* <p>
\* {@code RenderSurface} is responsible for providing a {@code Surface} to a given
\* {@code FlutterRenderer} when requested, and then notify that {@code FlutterRenderer} when
\* the {@code Surface} changes, or is destroyed.
\* <p>
\* The behavior of providing a {@code Surface} is delegated to this interface because the timing
\* of a {@code Surface}'s availability is determined by Android. Therefore, an accessor method
\* would not fulfill the requirements. Therefore, a {@code RenderSurface} is given a
\* {@code FlutterRenderer}, which the {@code RenderSurface} is expected to notify as a
\* {@code Surface} becomes available, changes, or is destroyed.
*/
RenderSurface {
/**
\* Returns the {@code FlutterRenderer} that is attached to this {@code RenderSurface}, or
\* null if no {@code FlutterRenderer} is currently attached.
*/
@Nullable
FlutterRenderer getAttachedRenderer();
/**
\* Instructs this {@code RenderSurface} to give its {@code Surface} to the given
\* {@code FlutterRenderer} so that Flutter can paint pixels on it.
\* <p>
\* After this call, {@code RenderSurface} is expected to invoke the following methods on
\* {@link FlutterRenderer} at the appropriate times:
\* <ol>
\* <li>{@link FlutterRenderer#startRenderingToSurface(Surface)}</li>
\* <li>{@link FlutterRenderer#surfaceChanged(int, int)}}</li>
\* <li>{@link FlutterRenderer#stopRenderingToSurface()}</li>
\* </ol>
*/
void attachToRenderer(@NonNull FlutterRenderer renderer);
}
public void attachToFlutterEngine(
@NonNull FlutterEngine flutterEngine
) {
Log.d(TAG, "Attaching to a FlutterEngine: " + flutterEngine);
if (isAttachedToFlutterEngine()) {
if (flutterEngine == this.flutterEngine) {
// We are already attached to this FlutterEngine
Log.d(TAG, "Already attached to this engine. Doing nothing.");
return;
}
// Detach from a previous FlutterEngine so we can attach to this new one.
Log.d(TAG, "Currently attached to a different engine. Detaching and then attaching"
\+ " to new engine.");
detachFromFlutterEngine();
}
this.flutterEngine = flutterEngine;
// Instruct our FlutterRenderer that we are now its designated RenderSurface.
FlutterRenderer flutterRenderer = this.flutterEngine.getRenderer();
flutterRenderer.attachToRenderSurface(renderSurface);
ContainerManagerState extends State<BoostContainerManager> {
class _ContainerOverlayEntry extends OverlayEntry {
bool _removed = false;
_ContainerOverlayEntry(BoostContainer container)
: super(
builder: (BuildContext ctx) => container,
opaque: true,
maintainState: true);
@override
Widget build(BuildContext context) {
return Overlay(
key: _overlayKey,
initialEntries: const <OverlayEntry>[],
);
}
}
public PlatformPlugin providePlatformPlugin(@Nullable Activity activity, @NonNull FlutterEngine flutterEngine) {
if (activity != null) {
return new PlatformPlugin(getActivity(), flutterEngine.getPlatformChannel());
}
}