-
Notifications
You must be signed in to change notification settings - Fork 29.8k
Description
Use case
As we know, flutter was designed to only support one flutter view container in the whole app at the very beginning. As a result, the life cycle of FlutterViewController is as same as the whole app. The FlutterViewController will start, resume and pause the app life cycle state according to the view appear/disappear events. What is more, it handle the surface update process and not allowed the sub class to override.
And now, we try to support adding it to existing app, and support multiple flutter view controller sometimes.
However, due to the first design, we met a lot of problems, as following:
- the app's life cycle is not as same as one FlutterViewController, because we have more Flutter VC.
- The sequence of multiple views' life cycle have side effect on the surface updates. for example:
I start a FlutterVC#1 to render flutter page#2, then I push a second FlutterVC#2 to render flutter page#2. The FlutterVC#2 will cover the FlutterVC#1. Note that we have only one FlutterEngine which hold only one surface, so when I started the FlutterVC#2, the FlutterVC#2 view will appear and surfaceUpdate:YES, then for a while, the FlutterVC#1 will view disappear, and surfaceUpdate:NO, which will let the FlutterEngine hold a nulled surface, and introduce failure of FlutterVC#2.
Proposal
I suggest that:
- refactoring the FlutterViewController to extract the sub functions occurred in view's appear and disappear event, and let those sub function public accessed by sub class.
Take the viewWillAppear as an example:
- (void)viewWillAppear:(BOOL)animated {
TRACE_EVENT0("flutter", "viewWillAppear");
if (_engineNeedsLaunch) {
[_engine.get() launchEngine:nil libraryURI:nil];
[_engine.get() setViewController:self];
_engineNeedsLaunch = NO;
}
// Send platform settings to Flutter, e.g., platform brightness.
[self onUserSettingsChanged:nil];
// Only recreate surface on subsequent appearances when viewport metrics are known.
// First time surface creation is done on viewDidLayoutSubviews.
if (_viewportMetrics.physical_width) {
[self surfaceUpdated:YES];
}
[[_engine.get() lifecycleChannel] sendMessage:@"AppLifecycleState.inactive"];
[super viewWillAppear:animated];
}We can refactor it to following sub functions:
- (void)onEngineNeedsLaunch{....}//this function should be public to access by sub class
- (void)deactivateEngine{...}//public function to sub class
- (void)surfaceUpdated:(BOOL)appeared {...}//make this function public to sub class
- (int)physical_width{...}//just an example
- (void)viewWillAppear:(BOOL)animated {
TRACE_EVENT0("flutter", "viewWillAppear");
[self onEngineNeedsLaunch];
// Only recreate surface on subsequent appearances when viewport metrics are known.
// First time surface creation is done on viewDidLayoutSubviews.
if ([self physical_width]) {
[self surfaceUpdated:YES];
}
[self deactivateEngine:YES];
[super viewWillAppear:animated];
}Then in my MyFlutterViewController which inherit from FlutterViewController, I can decide when I need to call surfaceUpdated function during view's life cycle, and avoid to introduce the problem mentioned in problem#2.
@implementaton MYFlutterViewController : public FlutterViewController
- (void)viewWillAppear:(BOOL)animated {
TRACE_EVENT0("myflutter", "viewWillAppear");
[self onEngineNeedsLaunch];
// Only recreate surface on subsequent appearances when viewport metrics are known.
// First time surface creation is done on viewDidLayoutSubviews.
if ([self physical_width]) {
[self surfaceUpdated:YES];
}
[self deactivateEngine:YES];
// here I am able to not call super's implementation, and avoid surfaceUpdate:YES, and move this call to viewAppear handler
// [super viewWillAppear:animated];
}