New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Revise iOS add-to-app usage page #3309
Revise iOS add-to-app usage page #3309
Conversation
demonstrate creating a `FlutterEngine`, exposed as a property, on app startup in | ||
the app delegate. | ||
|
||
<ul class="nav nav-tabs" id="engine-language" role="tablist"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@matthew-carroll see staging at https://add-to-app-2019.firebaseapp.com/docs/development/add-to-app/ios/add-flutter-screen.
This seems like a reasonable way of toggling languages for instructions in a way consistent with the aesthetics of the rest of the website.
It's generally recommended pre-warm a long-lived `FlutterEngine` for your | ||
application. This way, | ||
|
||
- Flutter screens will render their first frames faster. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Flutter will start the work to get to its first frame earlier
.
Not sure how much the precision matters here, but having no VC doesn't actually speed up frame time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
VC doesn't actually speed up frame time
Not sure I understood the comment about having no VCs. I rephrased it a little bit nevertheless.
application. This way, | ||
|
||
- Flutter screens will render their first frames faster. | ||
- Your Flutter and Dart state could outlive one `FlutterViewController`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm always a bit thrown by what Flutter
means in this context. Do we just mean Flutter-specific application state
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"could" or "will"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dnfield I'm actually just kinda thinking anything you can conceptually assign to the term 'Flutter' and that has a state. Can you think of counter examples?
Replaced could with will
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is Flutter
here the Flutter framework? The Flutter engine excluding the embedding? The embedding? Plugin code (dart/platform)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't the sentence apply the same way for all those definitions?
Tangent: I don't think the word embedding means anything for people outside the team and I'm tempted to keep it so for as long as we can since the distinction isn't necessarily useful unless you're trying to contribute to the engine code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess a way of looking at this is: here you use Flutter
in a way that excludesFlutterViewController
. Later in the document you're using Flutter
in a way that explicitly means showing the FlutterViewController
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't it a bit like having the sentences:
- Having a backup of your movie will make it outlive one VHS tape
- 'showMovie'
and then asking whether readers will be struggling understanding whether 'movie' here encompasses the mylar tape ribbons or not :)
It's likely immaterial whether they assume one way or another.
self.flutterEngine = [[FlutterEngine alloc] initWithName:@"io.flutter" project:nil]; | ||
[self.flutterEngine runWithEntrypoint:nil]; | ||
self.flutterEngine = [[FlutterEngine alloc] initWithName:@"my flutter engine"]; | ||
// Runs the default Dart entrypoint with a default Flutter route. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should avoid mentioning the default Flutter route
here. If anything, we should outline how to use platform channels to communicate with the Dart side to set up any non-default state.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We do have a default flutter route on the flutter side https://api.flutter.dev/flutter/widgets/Navigator/defaultRouteName-constant.html
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It exists but it causes much confusion and frequently doesn't work as expected
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which part specifically are you commenting on?
The fact that there's a cascade going on in https://api.flutter.dev/flutter/material/MaterialApp-class.html and the top level navigator behavior? It's indeed confusing but it's just how Flutter works at the moment. I don't think it should ever not work as expected. This is a mostly orthogonal concept to the timing of calls to setInitialRoute.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's confusing becuase you can only set it at one point in the lifecycle of starting the engine, and you lose access to that completely if you set it up in certain ways, and if you call it too late it just silently fails, and there's no good way to fix any of those problems with the API so if you open a bug about it you'll be told you should be doing it differently anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ya, the mechanism for changing the default route is confusing as mentioned below. But it doesn't change the fact that there is a default route when you don't change it.
override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { | ||
self.flutterEngine = FlutterEngine(name: "io.flutter", project: nil); | ||
self.flutterEngine?.run(withEntrypoint: nil); | ||
// Runs the default Dart entrypoint with a default Flutter route. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto.
- (void)viewDidLoad { | ||
[super viewDidLoad]; | ||
|
||
// Make a button to call the showFlutter function below when pressed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: maybe showFlutterViewController
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it adds much. Which misunderstanding are you trying to avoid for the reader?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what Flutter
is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which user-self-assigned definition of the term Flutter would be problematic?
|
||
// Make a button to call the showFlutter function below when pressed. | ||
let button = UIButton(type:UIButton.ButtonType.custom) | ||
button.addTarget(self, action: #selector(showFlutter), for: .touchUpInside) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto.
sometimes useful if the Flutter screen is rarely shown and there are no good | ||
heuristics to determine when the Dart VM should be started. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if the Flutter screen is rarely shown and does not need to persist state between apperances
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added into an enumeration
- Forwarding status bar taps (which can only be detected in the AppDelegate) to | ||
Flutter for scroll-to-top behavior. | ||
|
||
If your app delegate cannot directly subclass `FlutterAppDelegate`, you can |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/can/must right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
changed to should
. Must is a bit strong since it's not like the app won't run anymore without it. We can point out the reasoning and let them decide.
|
||
## Building and running your app | ||
An initial route can be set for your Flutter [`WidgetsApp`]({{site.api}}/flutter/widgets/WidgetsApp-class.html) | ||
when constructing the engine. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you choose to do this, you _must_ do so before running the engine, and any calls after running the engine will be ignored.
IMO, we should avoid documenting this API, deprecate it, and remove it. It's the source of a lot of confusion and it has no clear fix.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a warning box.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually there is already a warning box.
In order to change your Flutter route after the `FlutterEngine` is already | ||
running, use [pushRoute]({{site.api}}/objcdoc/Classes/FlutterViewController.html#/c:objc(cs)FlutterViewController(im)pushRoute:) | ||
or [popRoute]({{site.api}}/objcdoc/Classes/FlutterViewController.html#/c:objc(cs)FlutterViewController(im)popRoute) | ||
on the `FlutterViewController`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is markedly different from setting an initial route, since it mutates the route stack, whereas setInitialRoute
initializes the route stack to a specific spot.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wasn't suggesting that they are equivalent. It's in a tangential tip box.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a few more words to clarify difference.
The above only illustrates a few examples of ways to customize how a Flutter | ||
instance is initiated. Using [platform channels](/docs/development/platform-integration/platform-channels), | ||
you're free to push data or prepare your Flutter environment in any way you'd | ||
like before presenting the Flutter UI via a `FlutterViewController`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IME, the routing logic for add-to-app is pretty limited and does unexpected things. I think it would be better to document how to use platform channels in this way here, and then we could mention that you can use any of the framework-provided channels as well (such as the navigation channel).
In other words, I think a user is far more likely to want to tell the running Dart program what state it should initialize to before presenting, and that it's very unlikely that the right way to do that will be setting an initial route (that only works at a specific point in execution) or pushing/popping routes blindly from the background.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Document platform channels in this way here -> document how to use a custom platform channel to communicate with Dart
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I generally agree with you and using a custom channel to push an application specific DI-like bundle is what I would do too.
Though on the flip side, there's a bit of a personal taste involved in the trade off in the sense that writing platform channels is extremely boring and tedious (and error-prone in its own right as well) currently until we do flutter/flutter#32930.
Routes is also a non-trivially important concept in Flutter and we're not necessarily going to be avoiding pains for developers if we avoided this whole topic in documentations.
TL;DR, I think all present solutions have annoyances and we should properly handle this very common initialization use case and present a proper best practice with infrastructure in the coming phases, but we should have some docs in the meantime.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a bit more caveating for more transparency in the docs in the meantime.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh look there's flutter/flutter#34170 already
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SG. I can understand including this - my comment above sketches out a bit more of why (I think users will read this, get it wrong, think it's a bug, and be frustrated when we tell them it's by design). But maybe that's what we have right now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ya, I hear you. There are tons of bugs around this already and I don't think routes are generally useful APIs, but I don't think we can move the weight of the community right now and we might get more confusion if the code is in the repo but we avoided talking about it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Won't merge yet but LGTM.
|
||
```objective-c | ||
{{site.alert.tip}} | ||
It's generally recommended pre-warm a long-lived `FlutterEngine` for your |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing "to": "recommended to pre-warm"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks!
application. This way, | ||
|
||
- Flutter screens will render their first frames faster. | ||
- Your Flutter and Dart state could outlive one `FlutterViewController`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"could" or "will"?
|
||
- Flutter screens will render their first frames faster. | ||
- Your Flutter and Dart state could outlive one `FlutterViewController`. | ||
- You and your plugins can interact with Flutter and your Dart logic before |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Your application and plugins"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sg, changed
|
||
#import "AppDelegate.h" | ||
|
||
@implementation AppDelegate | ||
|
||
// This override can be omitted if you do not have any Flutter Plugins. | ||
- (BOOL)application:(UIApplication *)application | ||
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know you didn't edit this, but it should be NSDictionary<UIApplicationLaunchOptionsKey, id> *)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah thanks
var flutterEngine : FlutterEngine?; | ||
// Only if you have Flutter plugins. | ||
class AppDelegate: FlutterAppDelegate { // More on the FlutterAppDelegate below. | ||
lazy var flutterEngine = FlutterEngine(name: "my flutter engine") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we have engine naming conventions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looking into it a bit, this only adds a debug label for the thread in instruments. I want to get rid of it so I'll not over-emphasize it here for now
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; | ||
[button addTarget:self | ||
action:@selector(showFlutter) | ||
forControlEvents:UIControlEventTouchUpInside]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is missing a few tabs in the front.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is just lining up the semi-columns. Did I understand correctly?
action:@selector(showFlutter) | ||
forControlEvents:UIControlEventTouchUpInside]; | ||
[button setTitle:@"Show Flutter!" forState:UIControlStateNormal]; | ||
[button setBackgroundColor:[UIColor blueColor]]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would love to start "modernizing" (~2012) at least our sample code with dot notation.
button.backgroundColor = UIColor.blueColor;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
square brackets forever!
|
||
- (void)showFlutter { | ||
FlutterEngine *flutterEngine = | ||
[(AppDelegate *)[[UIApplication sharedApplication] delegate] flutterEngine]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
UIApplication.sharedApplication.delegate.flutterEngine
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<I didn't try it yet> don't we need to keep the cast to AppDelegate? </>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh yeah ((AppDelegate *)UIApplication.sharedApplication.delegate).flutterEngine
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
<div class="tab-pane active" id="vc-objc-tab" role="tabpanel" aria-labelledby="vc-objc-tab" markdown="1"> | ||
|
||
<?code-excerpt "ViewController.m" title?> | ||
```objectivec |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought the markdown language marker was objective-c?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure which syntax highlighter library we're using for the website. objective-c
and objc
don't work.
``` | ||
|
||
- Swift: | ||
or |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should the two languages be in tabs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lol you caught me. I got tired of writing all that html boilerplate
@@ -346,7 +622,7 @@ Connected views: | |||
|
|||
Attach to specific isolates instead in two steps: | |||
|
|||
1. Name the Flutter root isolate of interest in its Dart source. | |||
1- Name the Flutter root isolate of interest in its Dart source. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is 1-
the normal markdown way to number steps on the website?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not, but the code highlighter makes the markdown parser crap out (it would have the wrong indent with the code block anyway)
@@ -358,7 +637,7 @@ void main() { | |||
} | |||
``` | |||
|
|||
2. Run `flutter attach` with the `--isolate-filter` option. | |||
2- Run `flutter attach` with the `--isolate-filter` option. | |||
|
|||
```bash |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is supposed to be ```terminal
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh nice, it looks way nicer, thanks
|
||
- (void)showFlutter { | ||
FlutterEngine *flutterEngine = | ||
[(AppDelegate *)[[UIApplication sharedApplication] delegate] flutterEngine]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh yeah ((AppDelegate *)UIApplication.sharedApplication.delegate).flutterEngine
Fixes flutter/flutter#32863
Staging on https://add-to-app-2019.firebaseapp.com/docs/development/add-to-app/ios/add-flutter-screen