LEIGHA JARRETT: Hi there.
I'm Leigha, a product manager for Flutter.
Whether you're an experienced iOS developer or just getting
started, this video will provide you
with a comprehensive overview on how Flutter works on iOS
and how it can help you build beautiful apps.
From Flutter's unique architecture
to its integration with the Apple ecosystem,
we'll cover everything you need to know.
So without further ado, let's get started.
[MUSIC PLAYING]
Flutter is a framework for building beautiful, responsive
user interfaces that can run on multiple platforms
with a single code base.
That means that you can use this same code
to create apps for iOS, Android, the web, and even
desktop platforms like Mac, Windows, and Linux.
But before we get into the details of Flutter,
let's review the anatomy of an iOS app built
with Apple's frameworks.
At the top, you have your UI app code.
And this is likely written using the UIkit or SwiftUI frameworks
in either Objective-C or Swift.
Here, you can define the structure and appearance
of your app's UI using a set of views.
OK, so now we have our views.
But we need another step to actually translate
our high level UI code into the shapes and colors that
are displayed on the screen.
This is where the rendering pipeline comes into play.
Frameworks like SwiftUI and UIkit use graphics
APIs to render views.
Most Apple devices use the metal framework,
which provides direct access to the GPU.
So the UI framework generates a metal-based rendering pipeline
when it comes time to displaying graphics.
This is a great approach for iOS app development.
But one downside is if you want to deploy to Android or to web,
you'll need to rewrite your app in a new language
and framework.
And in many cases, you're working within the confines
of the iOS design language.
With a cross-platform framework, the first bit isn't an issue.
You can deploy to multiple platforms
from the same code base.
But the problem is, a lot of these frameworks
act as an abstraction over those native views
that we mentioned before.
Not only do you introduce this extra layer,
which may pose some performance problems.
But you also are still working within those same design
confines.
Flutter, on the other hand, takes
a bit of a different approach.
With Flutter, you still have a unified code base,
but with a native architecture.
You first write your app code using the Flutter framework.
Then, Flutter's engine converts your code
into instructions for the graphics hardware,
just like native frameworks.
When your app is compiled, two iOS frameworks are produced,
the App,framework, which contains your Dart code,
and the Flutter.framework, which contains all the libraries
needed to run the Dart code.
These frameworks are then embedded
into a Native iOS app that's created and compiled
using Xcode.
So when you build a Flutter iOS app,
you're really embedding your compiled Dart code
into a Native iOS app.
This structure provides flexibility, including
the ability to easily add Flutter UI
components to an existing iOS app,
or add Native app extensions like home screen widgets that
are written with SwiftUI.
Let's dig a little bit deeper on how all of this
works under the hood.
When building a Flutter app, you use the Flutter framework
to define your UI.
Flutter code is written in the Dart programming language.
Instead of views, UI components are called widgets.
The Flutter framework is declarative, just like SwiftUI.
So the code often looks pretty similar.
Things might be named differently.
Like, an HStack in SwiftUI is called a Row in Flutter.
But overall, if you're comfortable with SwiftUI,
Flutter should feel familiar.
One notable difference is that Flutter
uses aggressive composition where possible.
In SwiftUI, you might use a modifier to modify your view.
But in Flutter, that modification
is actually its own widget.
Modifications are represented as widgets
which contain other widgets.
It's like a Russian doll of widgets.
To learn more about Flutter syntax
and how it compares to SwiftUI, check out
our guide linked below.
Anyways, back to our architecture.
Just like SwiftUI and UIkit, the Flutter framework
needs to make some calls to create render pipelines.
This is where the Flutter engine comes into play.
It takes the framework code and translates it
into rendering commands that can be understood by the graphics
hardware on the device.
In the case of iOS, the Flutter engine
renders frames using metal, just like a Native iOS app.
OK, so why does this matter?
Well, like we mentioned earlier, getting
rid of that extra abstraction can help with performance.
But what's really cool is that since Flutter talks directly
to the iOS graphics API, you control
every pixel on the screen.
This means you can create custom visuals in your app,
like the Wonderous app that I showed before.
This flexibility is great.
But you may be worried that more flexibility means more work.
To make things easier, there are sets of pre-configured widgets
that match the mobile OS design, material widgets
for Google's design system and Cupertino widgets for Apple's.
My favorite part of these widgets
is that everything is open source,
meaning you can fork and customize them or propose
changes directly to the framework.
See that the padding looks a bit off?
Go ahead and submit an issue.
Or if you've got a fix yourself, submit a pull request
with your adjustments.
Continuing on with our discussion
on Flutter's architecture, so far,
we've talked about the framework and the engine.
But there's something else, the platform embedder.
This is basically the glue between Flutter and the host
operating system, in this case, iOS.
When you start a Flutter app, the embedder
does a bunch of things, like providing
the entry point for the app and initializing the Flutter
engine.
Let's take a closer look.
When you create a new Flutter app,
the tool auto-generates folders for platform-specific files.
So for iOS, that's the iOS Directory.
And we can open this generated workspace in Xcode.
Over in Xcode, there's a target called Runner.
Now, a target represents a single product,
in this case, an iOS app.
Essentially, the Flutter tool auto-generates a Native iOS
app.
And this Native app is the root of our Flutter iOS app.
Let's take a look at these auto-generated components.
So here, we'll open up this app delegate file.
And if you're familiar with UIkit development,
you may have used an app delegate class before.
It serves as the entry point for an application.
It's responsible for setting up the app's initial state
and responding to system level events.
Flutter iOS apps use an app delegate
that inherits from the Flutter app delegate class, which
is responsible for initializing the Flutter engine
and connecting it to the Native iOS platform.
More specifically, it creates a FlutterEngine object,
which is responsible for running the Dart code
and rendering graphics like we discussed before.
The AppDelegate also creates the FlutterViewController,
which passes input events into Flutter
and displays the frames rendered by the engine using metal.
Remember that everything that we've just talked about
is all auto-generated for you.
So unless you're doing something custom,
you don't need to worry about editing it.
Now, you might be wondering, where are
these iOS classes coming from?
When the AppDelegate file imports Flutter,
it's importing the Flutter framework.
The iOS Flutter framework contains the engine
and related libraries needed to execute Dart code and render
UI on iOS devices.
So how does this framework get embedded?
And where does your app code come into play?
Well, remember how we previously mentioned the Runner target
that's automatically created?
It has a default scheme that can be
run from Xcode by pressing the Run button,
just like a Native iOS app.
When you press the Run button, Xcode compiles and creates
the app.
The generated Xcode project executes a build script.
And this script compiles the app start code
into a framework called App.framework and then
copies the Flutter framework into your build directory.
Finally, these frameworks are embedded into your app
and codesigned.
OK, so we've been walking through a Flutter iOS app's
architecture in Xcode.
But in reality, you would use Flutter's own tooling
to run your app.
You can use the Flutter CLI command, Flutter Run,
or the Run button in your Flutter IDE
if you're using VS Code with the Flutter plugin.
Using these tools, you can also take advantage of hot reload.
Hot reload is a Flutter feature that
lets you make changes to your app's code
and see those changes immediately reflected
on your test device, the iPhone, iPad, or simulator.
It maintains your app state.
So say you're testing a screen that requires
a few clicks to get there.
You don't need to click through to that screen
every time you make a change.
Besides hot reload, the process for compiling a Flutter app
is the same under the hood.
When you use the command flutter run
to run your app in debug mode, Flutter
is actually just calling Xcode build
to get you an iOS app, which is the same command that's
used when you build your apps directly from Xcode,
like we showed before.
OK, so we've covered a lot of ground, Flutter's framework,
engine, and platform embedder.
Now, let's talk about leveraging Swift or Objective-C
APIs in your Flutter app.
There are a lot of reasons why you may want to do this.
Maybe you want to use Apple's APIs or frameworks
in your Flutter app, like connecting to the Photos
library or kicking off some on-device machine learning
workflow.
Most
Commonly used APIs have corresponding Flutter plugins.
And a plugin wraps the Native APIs and exposes
a Dart interface that you can use in your Flutter code.
This means that if you want to write the code
to open the photo library, you can write it once
in your Flutter app.
Behind the scenes, the plugin makes the necessary API calls,
depending on which platform your app is running on.
And you can find all kinds of plugins on pup.dev.
For quick overviews of some of our favorite packages,
check out our Package of the Week video series
here on YouTube.
However, Apple is always coming out with a new, exciting APIs
that us developers want to try out.
And maybe you don't want to wait for a plugin to be created.
Fear not.
Most plugins themselves use something
called method channels that let you communicate
with a Native platform.
And you can use them right in your own Flutter app.
Remember that AppDelegate file we showed before?
We won't go into too much detail.
But at a high level, you'll create a new method
in that file.
And then you can call it from your Dart code.
Alternatively, you can leverage Dart's interoperability
with platform languages.
For example, ffigen is a tool that
can be used to automatically generate
bindings, which act as the glue between Dart and Objective-C
or Swift.
You tell ffigen what classes you want to use.
And then you can leverage the generated Dart wrappers
directly in your Flutter app, no Native code needed.
OK, one last bit before we wrap up.
An important aspect of integrating Native SDKs
with your Flutter app is dependency management.
Luckily, Flutter provides a solution
for this using Cocoapods.
Cocoapods is a dependency manager for iOS projects
that allows you to easily include external libraries
and frameworks in your Xcode project.
Don't worry if you've never used Cocoapods
before because Flutter takes care of it for you.
When you add a dependency to your Flutter project
that requires Cocoapods, Flutter will automatically
generate and configure the necessary files
and configurations for you.
This makes it easier to integrate Native iOS
SDKs with your Flutter app without having
to manually manage dependencies or worry about compatibility
issues.
And that's Flutter for iOS.
Now that you know how Flutter works under the hood for iOS
development, give it a try for your next app,
or consider building your next feature for your existing iOS
app with Flutter.
We've curated some great resources on Flutter for iOS
and included them in the video description below.
This includes a reference guide for Swift and SwiftUI
developers, a beautiful reference
app to test out for yourself, and some additional iOS-focused
videos.
And if there's something that you
find is difficult or missing, let us know.
Go ahead and file an issue or add a thumbs up
to an existing one on GitHub.
Each quarter, we have a survey that goes out to get feedback
from developers like you.
So be sure to fill that out there as well.
Our team really appreciates it.
And that's it for Flutter on iOS.
I'm Leigha.
Thanks for following along.
And we can't wait to see what you build.
[MUSIC PLAYING]