[MUSIC PLAYING] EMILY SHACK: Hello, and
welcome to Beyond Mobile-- Building Flutter Apps for iOS,
Android, Chrome OS and the Web. I'm Emily Shack. EMILY FORTUNA:
I'm Emily Fortuna. EMILY SHACK: And if you would
like to live tweet this talk or get in contact with
either of us afterwards, all the information you
need is up on this slide. Now of course, we are here today
to talk to you about Flutter, but first a brief reminder
about the Big Bang Theory. The Big Bang Theory tells us
that the universe we live in started off as a teeny
tiny speck and over time expanded to something
much, much greater. Flutter, too, started off
as simply an idea, the idea that you should be
able to quickly create beautiful, fast,
responsive applications with a best-in-class
developer experience that you can just take
with you everywhere. Over the past few
years, the Flutter team has been hard at work expanding
that idea into a mature UI toolkit for mobile that
checks all of those boxes. And in just a few
moments, we're going to show you why Flutter
is the best choice for your next mobile app today. But like the universe, Flutter
is constantly expanding. And we're also going to
show you a sneak peek of the next frontier
beyond mobile. Flutter apps from
the same code base and with the same
tooling and functionality that Flutter developers
already know and love for desktop and even the web. Now we're going
to be live coding different portions
of a single app to guide us through
this journey. So if we can switch on
over to the devices, we can take a look
at that app now. EMILY FORTUNA: We partnered
with the agency called Two Dimensions to
write an app that we're calling Developer Quest. It is an app that
essentially gamifies the process of app development. Through this game you
can hire team members, assign them to tasks
to work on and all towards gaining users,
increasing their happiness, and ultimately
shipping that 1.0 app. And along the way, when
you complete certain tasks, various mini games will pop up,
as you can see on the larger screen down at the bottom. Now we have this running
on three different devices, including one of which
has maybe gone to sleep. We have it on iOS and
Android as well as an iPad down at the bottom. And this is the same
code base that you can see that we have a
slightly different layout for the different screen sizes. And as Emily mentioned,
we will be live coding different portions of
this app throughout the talk as we talk about
running Flutter on all these different platforms. So if we can go
back to the slides, Emily will talk us
through how we get there EMILY SHACK: Right. So we're going to be using
different portions of this app to demonstrate supporting the
mobile, desktop, and web use cases. For those of you who are
already familiar with Flutter development, the mobile
portion of our talk might be a little bit
of a refresher for you, as we will be covering
some Flutter basics. However, we will also be
introducing some brand new Dart language features. So stay tuned for
how you could further streamline your pre-existing
Flutter projects. In the second half
of our talk, we'll be moving on to new frontiers
for everyone in the room. We'll be showing
you how to optimize your code for a
desktop experience and finally get to the latest
and greatest on Flutter for web. With that, I'll pass
it back to Emily to talk to us about
Flutter on mobile. EMILY FORTUNA:
Flutter started out as something of a
moonshot, the idea that there must be a
better way to write UIs. And we believe we've
delivered on that promise. We've got three
different apps here, all written using
Flutter, and you can see they look very different. This really speaks to
the customizability of writing with Flutter. And they're super fast. They are working at-- if we can
go back to the previous slide, please. They run at 60
frames per second, and they should feel
sleek and responsive as you work with them. This app is the same app
running on iOS and Android, and there were no changes
that we made to the code to make it run on these devices. But while it looks
similar, there are small differences
in terms of navigation, in terms of scroll behavior, and
this all comes out of the box. This allows you,
the app developer, to focus on the interesting
parts of your app rather than worrying
about making it feel like it's running on
the platform appropriately. And lastly, my personal favorite
feature of working with Flutter is a feature called
stateful hot reload. This allows you to
have your app running while you're developing. You can make
changes to the code. And you can see them reflected
nearly instantaneously on the app. In this animated,
GIF you can see that I am making changes to the
background color and font size. But it can be used for much
more complicated things, like changing
functionality and so on. And it really speeds up
your app development. And speaking of zippy
app development, let's go over to
the MacBook and we will work on live coding
a portion of our app on the mobile side. So what we have here is
the version of our app up and running. There are two tabs
if you recall. The first one is the Teams
tab and the second one is the Task tab. For live coding on
the mobile portion, we are going to be
focusing on this Tasks page and adding functionality as
well as improving styling, explaining some Flutter
layout fundamentals. So in the code we've got
our main entry point here, and it's calling runApp
with Developer Quest. Developer Quest is a class
that extends stateless widget. Widgets are the
fundamental building blocks of all Flutter UIs. Everything you see on
screen consists of a widget, and Flutter uses
the build method to determine what is going
to be drawing to screen. So Developer Quest is
something of a complicated app. And when I'm looking at a code
base that I'm unfamiliar with, one way that I like to orient
myself around the code base and find what I'm looking for
is to use the Flutter inspector. This is some tooling
that allows you to tap on something
on the screen, and it shows you the
widget tree as well as it jumps to that
place in the code so that I know what
I need to edit. So in this case, as
I mentioned, we're going to be editing the tasks
that we add to the page. So Emily has jumped to
the task list item class. EMILY SHACK: And I think
this is looking OK, but it's pretty contrasty. I'd really like to
start by changing the color of this task. EMILY FORTUNA: So that
is a pretty easy change. Emily's going to set the
color of material container to be white and then
the text to be black. Now the reason it was colored
that way to begin with is Flutter has this notion
of theming for apps. And this gives you all of your
widgets sort of default styling so you don't have to
specify it everywhere. And in this particular
app we had a dark theme, which gave it white text. But Emily is reversing that
by specifying it directly. And, thanks to hot reload,
there it is, super quick. EMILY SHACK: All right. That's looking a lot better. But now there's more
information that I need to know about this task. For example, what's the reward
I get for completing it? EMILY FORTUNA: Yeah,
so as you can see, the text itself is inside
of this Column widget. Column widgets are a
pretty common widget that you'll be using
to build up layouts. They orient your widgets
vertically from top to bottom. And Emily's going to add
the spacedHeading function to add something on top of it. Now if you look at
the spacedHeading-- it's just above
that build method-- you'll see it returns
a list of widgets. And you can see the
call to spacedHeading has those three dots before it. This is a new
feature in Dart 2.3, which is called the
Spread Operator. And basically, it allows
you to expand that list so that the column
has a list of widgets rather than a list of lists. It's sort of
unwrapping, if you will. So Emily can hit
Save, and there we go. EMILY SHACK: All right. So I'm starting to
see more information. But there's still one
piece that's missing, and that's the skills that I
need to complete this task. EMILY FORTUNA: Yes. So let's look inside
spacedHeading. And you can see that
there is this task header widget, which is where we're
getting our coin information. And inside that, you see it
consists of a row with the coin and the text based
information of the coin reward we're getting from
completing this task. And Emily's adding
skill information. Now, she's doing this by
putting a for-loop inside of this list for the row. And this is yet another
Dart 2.3 new feature. It allows us to iterate
through all of the skills that we need and produce
SkillDot widgets accordingly, sort of like a
list comprehension if you're familiar
with that in Python. EMILY SHACK: All right. That's looking a lot better. Now, I think I'm pretty happy
with how this task is looking. But it's the only
task on the page and I can't do anything with it. I really want to make
this more interactive. EMILY FORTUNA: Yes. The whole point of this game
is to be able to add more tasks and assign people to
work on these tasks. So to do that Emily is
going to wrap this widget. EMILY SHACK: One second. EMILY FORTUNA: There's a
keyboard shortcut gone awry there. EMILY SHACK: You know
what, I'll just-- [INTERPOSING VOICES] EMILY FORTUNA: She's
going to wrap it in a inkwell widget, which
is a built-in widget that allows you to make the enclosing
widgets responsive to touch. So when you tap on it,
the On Tap parameter, what you specify in that
function, will get called. So we've got a function called
Handle Tap that shockingly is going to get called
when we tap on it. And we save, we can
see it in action here. So we've got that nice inkwell
ripple animation and we've got some characters. So let's take a look
inside Handle Tap to see what's going on there. Handle Tap is an
asynchronous function, and that means we can
use this await keyword. What we're doing here is we want
to not proceed to the next line until we receive information
about which characters we want to assign to this task. So the await keyword
tells Flutter not to proceed to the
next line until it's received the information
that picking the character has completed. So Emily can select
characters, and we've assigned them to their task. And-- EMILY SHACK: All right. So I think the task
completed, but I didn't really get to see the
progress along the way. Is there a way that
I could see that? EMILY FORTUNA: Yes. We are lacking some user
feedback about what's going on. So we'll put one more thing
in that column widget, and it's a progress indicator. And this is just a fancy
looking progress indicator that can show the team
members that we've assigned to the tasks as well. So this one, this task,
is already completed. You can launch it, but let's
work through one more task to see it in action. EMILY SHACK: All right. Much more immersive. And look at all
those users I have. EMILY FORTUNA: Yes. So let's go back to
the slides and we can talk a little bit about the
tech behind making this happen. So essentially
you've got your code and you're making calls
into the Flutter framework. Those are all those
built-in widgets, and this does things
like gesture recognition. The level below that
is the Flutter engine. This is written primarily in
C++ and consists of the Skia Graphics Library, the text
rendering engine and so on. Below that is a thin
runner that starts up the Flutter process on iOS and
Android particular to the code. And then of course that
runs on x86 or ARM hardware. Now it's important to
remember that for you as the app developer, you don't
need to know all of this stuff. This is just if you're
curious about how it goes under the covers. And when you want to release
your Flutter app in production mode, that Dart code
gets compiled down to ARM or x86 instructions,
so it's super fast. And with that, I will hand
it over to the other Emily on the stage to talk to
us about desktop style apps with Flutter. EMILY SHACK: Right. So zooming out
from Earth to Mars, we find ourselves looking
at Flutter for Chrome OS. Flutter for Chrome OS is a newer
frontier for the Flutter team, but it actually uses a
lot of the same technology under the hood. Although the Flutter
team has been hard at work creating
a seamless development experience between
desktop and mobile, it's likely you'll still need
to make some changes to your app to optimize your
user experience. This not only includes changes
to the way your UI is laid out to reflect a
different screen size, but also changes to the ways you
allow users to express input. For example, most desktop
apps use a keyboard and mouse or trackpad, while mobile
users tend to rely much more heavily on touch. Why don't we go ahead and
switch over to the Chromebook and see how we can modify
our own application to make it more friendly for desktop. EMILY FORTUNA: All right. So we have literally the
exact same code, just at the same point we
left off on mobile, and it's running on Chromebook. We made no changes to the code. And you can tell it's the
exact same code because it's enormous for the screen size. EMILY SHACK: Yeah, I think that
we could probably do some work to improve our UI layout here
because we're really only doing what we've developed for mobile. So if we jump into the
code we can see right now this is our main function. And we can go into
the game widget, which is where we're building
all of our UI layout. Right now we're just
using a single screen to lay that all out,
but we would really like to switch between two
different widgets based on the size of the
current display device. We're going to do that via
a call to MediaQuery.of. And that is a handy operator
which tells us information about our current device. In this case, we really care
about the width of the device. So we're going to
check the width and then switch between
different layouts, depending on what we get. We're still going to display
that basic game screen if we don't beat that
threshold so that we know that on mobile, our apps
still perform exactly the same. So we can hot reload because of
course just works on desktop. We can switch back over
and see our app running. EMILY FORTUNA: That
looks much better. That is a much better usage
of our screen real estate for sure. So one form of input
that we have much more commonly on desktops that we
don't usually have on mobile is keyboard input. It would be nice if we
could modify our app to make use of that. EMILY SHACK: Very true. So one thing that we
can do in this app is click on a character and see
some information about them, and that brings up a dialogue. In order to dismiss
that dialogue, we have to hunt around
for that x in the corner. But on desktop, we might
want to do something easier, like press the Backspace
key to dismiss it. So let's see if we can
add that to our app. To do that, we're going to
go into the Character Modal widget. The character modal
widget is the wrapper for our entire dialog. And we're going to go ahead
and wrap everything that's being returned in that build
function in another widget called Raw Keyboard Listener. That's going to
accept key events so that we can do
whatever we want with them in the body of our widget. The Keyboard Listener has
an important property, which is that of focus. Now if you think about focus, it
might not make sense right now in terms of a dialogue. But when you think
about it, you really want the key event that's
typed to be piped only to one widget in
the app rather than everything that might be
able to accept a key event. So we're going to put this
widget in focus with a call to requestFocus, and then pass
a focus node into our Keyboard Listener. Finally, we're going to
implement the onKey method, which is a handy method
where all of the magic really happens. That's going to
accept a key event or pass us a key event,
which we can use to perform some application logic. When we see that
key event, we're going to check to see if it
matches the Backspace key, and if so, we're
going to use a call to Navigator.pop to get rid of
that dialog from the screen. One more thing to
point out is the use of the Logical key property. The Logical key
property is a way that we can allow for
international keyboards. So different keyboards
across different countries, different languages
might have keys laid out in a different way. In the US on English
keyboards, we generally have the Backspace or
Delete key in the top right corner of the keyboard. But we don't really
want that spacing to determine what the key is. We want the key itself
to determine it. So when we get that Backspace
key with Logical key, we know that it's the right
key we're looking for. Now when we go
ahead and hot reload we can check out our app. EMILY FORTUNA: So my pointer
is here, nowhere near that x. I'm pressing the Backspace
key, and it works. EMILY SHACK: All right. So we can accept
keyboard inputs. That wasn't a very
visual change. Let's do something that
everyone's really looking for. What can we do next? EMILY FORTUNA: Yeah,
so another form of input that we have
on desktop style apps is the ability to
respond to mouse hover. And it would be nice if we
could modify our app to do that. EMILY SHACK: Very true. So something I think
that could be really cool is if we used mouse hover to
make these characters animate whenever we hovered over them. To do that, we're going to
go into the widget called Character Pool Page. That widget is responsible
for building the whole grid of characters on screen. And within there, we have
a character list item, which is responsible for
building a single character. We're going to go into that
widget and wrap everything there just like we did for our
keyboard input in a Listener, except this Listener
accepts mouse touch and trackpad events. In our case, we care
about the onPointerEnter and onPointerExit methods. Those are going to tell us when
a hover has started or stopped. When we see those we're
going to call startPlaying and stopPlaying, which are
methods that we've already written for this widget. But they're not doing
much yet, and that's because what we
really want them to do is change the state
of our widget. But our widget is
stateless right now, so we're going to need to
turn it into a stateful one so that it can change
state within its lifecycle. Luckily, we have a handy
IDE shortcut to do that. So we're going to go ahead
and convert to stateful, and then we can introduce
a new variable in the state class that just got created. And that variable is going
to be allowed to change during the widget's lifecycle. Within startPlaying
and stopPlaying, we're going to modify
that variable using a call to setState. That call is only available
to stateful widgets, and it tells the
widget that it might be time to rebuild because
something in the build function has changed. So we're not actually doing
anything yet because we're not using that variable anywhere. So let's go ahead and insert
it into our build method. We have this Character
Display widget, and that has an
isAnimating property. So it's pretty simple. We can just add it in there
and go ahead and hot reload. EMILY FORTUNA: And so
now as I hover on them, they do a little animation. EMILY SHACK: Very cool. All right. I'm feeling pretty good about
our desktop application. But you might all
be wondering how it's working under the hood. So why don't we switch
back to the slides. You might be surprised to
learn that on Chrome OS, the underlying tech
stack here is actually very similar to what we
showed you for mobile. The main difference is this
additional layer called ARC++. ARC++ allows Android apps to
run on Chrome OS with the same fidelity as apps that were
designed for Chrome OS from the start. From a developer
perspective, it's very similar to
deploying to an emulator. Except in this case, it's
the real target hardware-- your Chromebook. Now for those of you
in the audience who are thinking that that tech
stack looked a lot like mobile in disguise, we are happy to
announce that Chrome OS is not the only way you can create
desktop experiences in Flutter. Zooming out from Mars to the
further reaches of the galaxy, with the early stage
Flutter for desktop project, your apps can run on Mac OS,
Linux, and Windows as well. And you can read all about
how to do that for your app at flutter.dev/desktop. Now the tech stack behind
Flutter for desktop differs a little bit from
that of Flutter for Chrome OS. Instead of the Android
runner combined with ARC++, we swap out the runner
entirely with one specific to the particular
desktop platform, whether that's Mac
OS, Linux, or Windows. While it is a ways
out, the Flutter team is hard at work,
ensuring that your code up at the top of the stack
can remain unchanged so that you don't really have to
think about any of the details below. With that, I will
pass it back to Emily to talk to us about
Flutter for web. EMILY FORTUNA: So you
probably heard a little bit about Flutter for web, and
we're excited to show you how that works today. And today we're also even
more excited to announce that Flutter for web is
now in technical preview. [APPLAUSE] If you go to flutter.dev/web,
you can download it, check it out, kick the
tires, give us feedback. One thing we do not
recommend you do just yet is deploy all of your apps
to production with Flutter web just yet. There is still some
performance things the team is working out,
and maybe a few usability things that we want--
or tooling items that we want to make Flutter
for web development even better. However, there's lots of
great stuff there already and we're going to
show you how it works. So you have probably visited
a web page or two in your life and probably have written a
web page or two in your life. So you might be curious about
how the tech for Flutter on the web works. From you, the Flutter web
developer perspective, it is no different from
Flutter anywhere else. All those Flutter widgets that
you know and use, they work. We did a lot of
work under the hood to make that tech stack
just work for you. So the nonsolid rectangles
are the different parts. We rewrote the Flutter
web and the Flutter engine to be the Flutter web engine. And essentially that
takes all those lower-- it has a lower
level implementation of the Flutter widgets
and it has rewritten them. It generates Dart code
that produces HTML and CSS. When you can't generate
or produce Flutter widgets with the same
fidelity as you would normally with just those two,
we fall back to the Canvas API. So that is Dart code. And we use the dart2js compiler
or the dartdev compiler to then compile that
down to JavaScript, which then just runs on your browser. So let's go a little bit
deeper into how that works. So as I mentioned you've
got your Dart code, which is Flutter. And the Flutter web engine is
a lower level implementation of all those Flutter widgets. And that's Dart code
that generates HTML, CSS, and Canvas. And the Flutter team is also
looking at potentially-- depending on how
performance goes and how the web
continues to evolve-- swapping out Canvas
for the CSS Paint API. As I mentioned,
that's all Dart code, and that gets compiled
down to JavaScript. And I want to point out that
the Dart-to-JavaScript tool chain is actually quite mature. Dart was originally written to
be a web programming language. And the Dart team has
many years of experience of compiler engineers working on
converting Dart to JavaScript. People such as
myself who do that. So let us all swap
over to the MacBook and we can show you
some Flutter web code. EMILY SHACK: All right. So what you're
looking at right now is the Style Sphinx Mini game. Hopefully you'll
remember that from when we were doing our walk-through
of the app at the beginning. This is the mini game
that you encounter at critical points in your
app development journey. And this is what we have for
you today on Flutter for web. Now admittedly this
is not the full app. And if we look at
the code, we will see why we moved
away from the code that we were working on
for mobile and desktop. We can see that in
these imports we have an import to package
Flutter web rather than Flutter, which
many of you will be familiar with as
the canonical way to find Material widgets. The reason that we have
this different import is that the Flutter team, when
implementing Flutter for web, chose to fork the implementation
so that they could make sure that all of the changes
were stable and performant before merging them back in
with the mobile Stable widgets. But you should see those imports
converging together very soon. All right. Now the rest of
that is basically the same as what you would
see on desktop or on mobile, the rest of the code. EMILY FORTUNA: Yes. EMILY SHACK: With
the exception of that we have not swept over all
of the import statements to show you this today. So Emily, how does
this work on web? Is this something that we
can show for example browser resizing with? EMILY FORTUNA: Yep. So my app is responsive. You can see my Sphinx shrinking
based on the available screen real estate. That's all there. EMILY SHACK: All right. Very cool. What about the
developer tools that I know I really need in
my development process, like the widget Inspector
that we showed earlier? EMILY FORTUNA: Absolutely. So having a great UI
development experience is the crucial part of
working with Flutter. And so we've got our
DevTools available just as a separate browser window. You can select a widget
and it'll take you to that point in the tree. EMILY SHACK: Awesome. OK. But what about something that's
really specific to the web use case, like for example linking? Is that something
that I have to write a whole bunch of extra code
to get working in my app? EMILY FORTUNA: No. So this is actually really cool. I didn't make any
changes for this, but I have deep
linking for my app. So you can see in the URL,
so sort of my home page. And when I click space to
Sphinx, my URL changes. This came out of the box
because I have a material app. And if you write material
apps, you commonly make use of routes. Those are the different pages
that your app will go to. And so it makes use
of those route names to automatically generate URLs. So if I change this URL-- of course I could
use the Back button, but I'll just demonstrate
deep linking that way. That's interesting. EMILY SHACK: We
promise it works, or at least it will when we're
out of technical preview. EMILY FORTUNA: Yes. EMILY SHACK: But--
that's all well and good, but what about the thing that
I care about most as a Flutter developer, hot reload? That's the most important thing
to my development process. Is that something that I'm
going to have available to me when I'm developing for web. EMILY FORTUNA: Yes. So assuming we're on the right
page that we think we are, I'm going to-- I'd like to make a change
to the color of this button, make it a little bit more
fitting in with the sand theme. So in my Sphinx
button I have this-- I'll change the color
of the material. And I want to point
out that I'm going to be demonstrating hot
restart rather than hot reload. The Flutter team is
currently working on getting hot reload
working as well. But for the moment
we've got hot restart, which does not preserve state. And there we go. So-- EMILY SHACK: Just a
few kinks to work out. EMILY FORTUNA: I'm guessing
that's because it was confused on which page I'm on, but yes. So generally speaking,
as you notice hot restart is a little bit slower
but still quite fast. And because you have deep
linking, which normally works as you expect to, you can
still make changes on the page that you're on as well. EMILY SHACK: Very cool. All right. I think I buy that
I'm going to be able to do development on web. Why don't we switch
on back to the slides to talk about everything
we've gone over so far. So now that you've seen
where Flutter is today and where it is
headed tomorrow, let's go over some of the highlights. Flutter is stable
and mature on mobile, giving you a look and feel
that is tailored to the target device you're running on
from a single code base. It allows you to iterate
quickly with hot reload. And finally looking
to the future, we are expanding into the
desktop and web environments, unlocking the ultimate vision of
an app and developer experience that just works everywhere. EMILY FORTUNA: If you would like
to learn more about Flutter, we recommend you
go to flutter.dev. That's where you can
download the APIs-- download Flutter on your
machine with the API docs and their codelabs. We have 12 codelabs
available on the web. And also, if you're
here at I/O, there's a codelab tent where
you can check them out. People like us, or
exactly us, will be there to answer your questions. Also, Flutter has a great
presence at I/O this year. We've got five talks, and I want
to call your attention to two in particular that are
particularly related to the stuff we
talked about today. A little bit later
today there's a talk on Dart, which is the technology
that makes Flutter amazing. And there was another
talk on writing UIs for different screen sizes. And that happened
already yesterday, but it should be up-- it's
actually up already on YouTube, so you can check it out. If you would like to
check out the code that we wrote today, including
the web development branch-- and hopefully it will
work better for you-- you can go to this URL. And if you would like
to play the game, you can download it from the
App Store or the Play Store. Flutter Developer
Quest is the name. And you can also check it out
at the Flutter sandbox as well. There's many different
screens for you to play it on. And this link will be on
the next slide as well. Thank you so much
for coming today, whether you're writing for
mobile, desktop, or the web. [APPLAUSE] [MUSIC PLAYING]
Well done for Flutter for web!
awesome!!!!