Building for iOS with Flutter (Google I/O'19)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[MUSIC PLAYING] ANDREW BROGDON: Hello, everybody. Welcome to Building for iOS with Flutter. I'm Andrew Brogdon from the Flutter team. BRETT MORGAN: And I'm Brett Morgan, also from the Flutter team. ANDREW BROGDON: And today, we're going to tell you all about how you can build great iOS apps using Flutter. Let's take a second, though, and do a little audience demographics. How many of you aren't that familiar with Flutter yet? Maybe you haven't used it. We got some hands. OK. How many of you are familiar with Flutter? Seasoned coders-- wow. OK. So you guys and gals might be thinking this. Why are we even having this talk? Building for iOS with Flutter-- there's a command for that-- flutter build ios. Job done. Shortest I/O talk ever, right? No. In reality, there's a lot more to building a great app for iOS than just compiling your code. iOS users are a demanding bunch, and rightfully so. They're used to world-class apps, polished experiences. They're used to apps that respect certain design guidelines and follow particular paradigms. And if you don't respect those expectations, it's very easy to end up with a user who looks like this. Unhappy, confused. Why doesn't this look like all my other apps? Why doesn't this thing move to the other thing when I do this? That's not what you want. You want users who look like this-- happy and at ease. The apps feel natural to them. They know how to use them just by looking at the interface. Now to be fair, that's not always easy to pull off, but Flutter can help. One of the ways it can help is by doing some things automatically. This is a list view widget. And it's running on Android on the left and on iOS on the right. And as you can see on Android, it has that nice Material glow. And on iOS, it has a bounce. It's updating its scroll physics automatically to match the platform that it's running on. And the reason Flutter can do this is because the people who build the SDK can be confident that people want this in almost every app. If you're building for iOS, there's a 99% chance you want a list that bounces, and so that's built into the SDK, and it happens automatically with no code from you. But there are decisions that Flutter can't make for you, that you need to be in control of. And that's because different apps take different paths toward adapting to a platform. One team, for example, they might say, we need our app to be as native as possible. The fonts, the animations, the way it navigates, the chrome that goes around the app, we want all of that to look very native to the platform. We just have a few things in the middle. Maybe those yellow widgets there, we have our own design for those. Another team might say, we have the content down. We have our own design language for that. But we like the navigation paradigm. We want to keep that. Another team might say, shoot the works. We have our own brand. We have our own very customized look. We'll do our own thing for everything except alerts. That's the only little bit that we care about. That's what we want. And so you end up with three apps that take very different approaches to matching the platform, but can still look and feel great. Now the bad news is, again, Flutter can't make these decisions for you. You need to be aware of and in control of how you make them. But the good news is that Flutter is designed to power your apps no matter where they fall on this spectrum, and we're going to spend the rest of this talk showing you how. BRETT MORGAN: So I can start coding now, right? ANDREW BROGDON: Almost. Just a couple things to cover quickly. Number one, we're going to start with a little data here. This talk is about adapting the user experience to the platform. We're not really talking about state management or architecture. We do have a model that's going to store some data. If you're interested in those topics, there is another talk on this stage tomorrow by Matt Sullivan and Filip Hracek, so check that out. Another thing-- we're going to start with a few widgets, and we're going to talk about how to take the widgets that have our design in them and adapt them to iOS. And last, we're not going to be using Material on iOS, and that absolutely is an option. If you saw the talk yesterday, you know that Material's very adaptable, very expressive and customizable, but this is just a choice that we made for this talk. And here is the app that we're going to be hacking on. It has one screen that presents a list of fruits and vegetables. It has another screen where you can record that you ate something and say, I had this many servings. So if I had a kiwi for breakfast this morning, you can record that. And it has a third screen that'll keep track of a log of what you've eaten, what you've gotten in terms of calories and some vitamins. So that's the app we're going to be working on. And to build it, we're going to use the Cupertino package. This is the Cupertino widget gallery, which you can find on flutter.dev. And it has screenshots, images, sample code, all sorts of fun stuff for each of the widgets that's in the package. And originally, we were just sort of going to go give a tour of the website as a way to introduce you all to the widgets that are in the package. And then we remembered, Flutter has hot reload. It can change things very quickly. Why don't we just run it and we'll see how that goes? So thus was born Cupertino widget lightning round. So we're just going to show you some widgets here. BRETT MORGAN: Switch to the demo computer, please. There we go. My very clean desktop here. Here, we have the traditional Hello World with one slight difference from the ones you've seen before-- is we're importing the Cupertino package. So let's get started. First step, we have a Cupertino button. It's a great little button, but it seems to be lost in this sea of white. So let's spruce things up, give it a bit of color. We can now see we have a button we can tap on. It'd be nice if it actually did something. So, of course, we can fill in our onPressed handler, and we can throw up a Cupertino alert dialog. So when we do this, we click on it, and of course today is all about eating our vegetables, so do we like desserts? Sadly, no. Next, we're going to have a look at Cupertino switches. We can see it's actually quite quick. Little guy here, and it looks all nice and native. We have that paragon of iOS design, the segmented control. We have a slider of course, which we'll be seeing later. ANDREW BROGDON: Yeah, we'll be seeing that once again in a minute. BRETT MORGAN: Yep. We can turn around and have a picker. Sorry, this time 'round we're going to-- god, I love my auto-typing. We have a nice little picker we can slide through here. If we can do that, we might as well have a date. And of course, we can make a guess when I/O will be on next year. Apparently, it's going to be early next year. Oh, we have activity indicator. Now at the top, it looks a little bit blank. I wonder what we can do about that? We can use a Cupertino page scaffold and put a navigation bar in there. And, of course, the piece de resistance, all sorts of-- ooh, it's a little bit slow at typing this. It's getting tired. ANDREW BROGDON: We'll be using this widget again in just a second-- CupertinoTabScaffold. It presents the tabs at the bottom and also handles building the contents that go into the tab views. BRETT MORGAN: So now we have three separate activity indicators. Back to the slides. ANDREW BROGDON: Back to the slides. There we go. All right, so let's talk about one decision that you will probably come to in building any app, and that's navigation. There are a lot of ways to handle navigation. This is one, for example. This is hierarchical navigation, where you start at a root location, and you have some choices. You pick one, then you get new choices. You pick one, you sort of make a path that way. The web works this way. You dive deep and then you hit the Back button. Android apps use this very frequently, but iOS has another common pattern called flat navigation, where you have multiple routes that you access with tabs, and each one has its own navigation stack. So you can dive deep on one of them, then still get to the others using the tabs. And when you come back, the stack is right where you left it. Let's see if we can put this to work on our app. So we'll have two roots-- one that's a list of fruits and veggies, and one that's our log of what we've eaten. And then we'll have a pop-up from the list that's a form where we can record some data, record a new log entry of what we've eaten. How can we get that working? BRETT MORGAN: Oh, back to the demo computer. ANDREW BROGDON: It's so easy when you're doing all the work here. This is great. BRETT MORGAN: OK. Here we are, back on our little iOS simulator. We have an app. We've added a bit more imports because we have a bit of a data model here with vegetables and logging what we've eaten. We have the application. Most importantly, we have a CupertinoApp here instead of a MaterialApp, which you're probably used to by now. We have some theme data. I don't know if you can see it at the back there, but this little log icon is in red because that's red. If we go down to the main screen, we have a tab bar, which I showed you before, a pair of navigation items, and well, we're not building anything yet. So let's fill in the tab builder. Oops. Hehe. Now I'm showing away the magic. There we go. OK. So we've got a pair of CupertinoTabViews. ANDREW BROGDON: And CupertinoTabView is the root view that creates those separate navigation stacks. So it's going to make a separate navigator for each page that goes inside this tab view. BRETT MORGAN: OK. So we are actually showing views when we click on these, but there's nothing there yet. So let's fill in our stubs for our Log screen. So now we have some text, and we can see when we click on the buttons in the tab bar, we get changes. But we've also got an Add to Log screen. And well, how are we going to bring this up to screen? Let's use our good old friend GestureDetector to give ourselves an onTap handler so that when we click on this, we get that iOS style animation of bringing something in. ANDREW BROGDON: And here you see the CupertinoPageRoute there is the class that encapsulates that animation that you see. The new screen slides in, just like you'd expect, and that's the widget that does that. BRETT MORGAN: So looking at our Log screen, it'd be nice if we had something at the top here to make it feel a bit more at higher for an iOS user. And how we stop doing that is a CupertinoPageScaffold. The PageScaffold doesn't actually put anything on screen itself, but gives us the capability to put a navigation bar at the top. ANDREW BROGDON: There we go. BRETT MORGAN: Next, we'll turn around and do the same with our list view and our Add to Log page, giving us-- there you go. We got the veggie list. And when we click on our handler, we get same up here, and also the themed back chevron, so iOS users start feeling all at home. And back to slides. ANDREW BROGDON: Back to slides, please. All right. So let's take a look at what we just did and the decisions that we made. We started with my app. It's a widget. In Flutter, everything is a widget. And that's what we made our first decision. We decided we want to use the Cupertino package in this app, and we created a widget for it-- the CupertinoApp widget. That encapsulates that first decision that says, I need-- I'm going to need a Cupertino theme later, and I'm going to need the navigator set up properly, and so on. That's the first decision we made. And then from there, we made our main screen, and we made another decision. We said, I want to use flat navigation. That's another design decision we made about adapting to the platform, so we created the CupertinoTabScaffold and the CupertinoTabView. One presents the tabs and has that tab builder, and one creates that navigator that we need. So that's another place where we encapsulated a decision into a widget. Now after that, we have our Add to Log screen. And there, we had another decision. We said, I want that navigation bar across the top. I want to have that back button on my pop-up. I want my title up there the way the users expect. So that's another decision we made. It helps to put it on the screen. [LAUGHS] So these decisions-- they're all encapsulated in widgets. And so we end up with four places in our widget hierarchy here, which I've sort of abbreviated, where we've taken each decision and made a widget do the work for us. The one thing we don't have right now is content. So let's take a look at that and how we can put widgets to work on one of these screens. The Cupertino package has a number of widgets, from navigation stuff all the way down to input controls, as you just saw. And let's take a look at our app, and we're going to focus just on the middle screen here, just on this form. We've got the segmented control there. We've got a slider. We've got a button that looked very platform-specific. How can we put these to work in our app? BRETT MORGAN: Back to the demo computer. Here we go. We're on our Add to Log screen, and it's time to add some content. Oop, wrong button. There we go. First thing we do is we add a model. This is where we get our content from. So on iOS, typography is very important. We have a class that turns around and adds a whole bunch of capability around theming of text. We won't show that, but we'll put that in the open source project which is coming up shortly. We're also splitting out our Add to Log screen into a separate widget and stateful form. ANDREW BROGDON: Yes. So one of the questions we get a lot from Flutter developers is how do I separate business logic from presentation logic? And that's something that we've just done here. We have a screen widget that will manage interacting with our model for us. It'll actually read data from it, save data back to it. And then we have a separate widget that's going to be in charge of how the form looks and is presented. So it just takes a veggie record, and it takes a callback to use when somebody hits the button. Otherwise, it's fairly ignorant of how the business logic works. BRETT MORGAN: OK. Next up, we're going to start adding some state to our form. So we've got a servingsTextController, and this is going to be where our user enters how many servings of the apple or whatever vegetable or fruit they're eating. Now we're going to start putting things on the screen. We're going to have a stack here, because we're going to have a list that has our form, and then overlaid over the top of that at the bottom of the page is going to be a summary. Now we're creating a row across the top. Don't know if you can see it, but there's a little hint of a box here. That's our flat card. Probably help if I actually put some content in it so you can actually see it. There we go. We've got some apples. Now it'd be nice to actually put next to that a description. So what we're going to do is across our row, we've got a column which we're going to add a pair of text items here that are themed using our adaptive theme-- text theming. So now we've got apples and fruit. And underneath this, we're going to start bringing in our Cupertino widgets. ANDREW BROGDON: Right. We BRETT MORGANe-- So we have-- ANDREW BROGDON: Sorry. So here's CupertinoSegmentedControl. This is our first Cupertino widget in this form. As you can see, the image above in the text, they could feel at home in an app running on any number of platforms. But this is something specific to iOS, CupertinoSegmentedControl, where we give it the list of children that it's going to display, we tell it which value to have highlighted at the moment, and we give it a callback to invoke when the user taps on it. BRETT MORGAN: Next up, we're going to put a text field down. It's going to go right about here, giving us the ability to enter the number of servings. But it's looking a bit-- oh, yeah. We need a callback handler so that we can turn around and properly deal with input. I've hidden my keyboard on the iOS simulator. And next, I want to make it a bit larger and put something beside it describing what that number actually represents. So as you saw, it grew up in size. Now we know what the number actually means. Next, we'd actually like to come down the page and start putting in a slider. Now for the slider to work, you kind of need to put some labels on the ends of it, and then put the CupertinoSlider itself in. ANDREW BROGDON: So here we have our third Cupertino widget, CupertinoSlider. It takes the value that it should be displaying, a minimum and a maximum range, a value for the number of divisions or stops to show along the way, and another onChanged callback that's invoking setState with the new value when it's moved. BRETT MORGAN: OK, on with the show. Put a nice little quote there, so we know what apples are all about. And now we're going to start-- sorry, that went a little bit quickly. What we're doing is we're putting a summary at the bottom of the page, and it's important to insert the list that this is in so this doesn't interact with it too badly. And now, what are we going to put on it? We're going to put on a summary of the total calories and the vitamin A and vitamin C. So let's start painting it. So to do that, on iOS, I have this wonderful frosted glass effect. And we're implementing that using image filter. So let's get it on the screen. We're positioning it at the bottom here so it won't move. There we go. Sorry. If I really pull this, you can actually see it sort of sliding in behind there. ANDREW BROGDON: Yeah, so that blurred background widget is doing that frosted effect that you see at the bottom there. And so that's not something that's provided by the Cupertino package, but you can still create something like that and reuse it in exactly the same way throughout your app. BRETT MORGAN: So we've got this wonderful form. Wouldn't it be great if we had some sort of call to action at the bottom of the page, some sort of button to actually-- so let's do that. Let's turn around and add a CupertinoButton. There we go. Add to Log. It's seeming kind of bland and not really findable. ANDREW BROGDON: Yeah, it's hard to spot down there. BRETT MORGAN: Let's make a really small change. I don't know if you saw it, but this became a CupertinoFilledButton, and there we go. We've got this nice, big call to action for our users to turn around and remember that they've eaten apples at lunch. Back to slides, please. ANDREW BROGDON: Back to slides. All right. So there's our Add to Log screen. Now imagine you built that out using Flutter, you built out the other two screens using Flutter. That's right about the time somebody's going to come to you and say, hey, that iOS app you built? It looks great. How quickly can you get it running on Android, right? Which brings us to our next topic, multi-platform design. Can we, without turning our code into a giant bowl of spaghetti, achieve something like this? Update the apps so that these little details are specific to the platform, the way navigation works is slightly different to adapt to the platform. How can we do that? Again, this is not something that's very easy to pull off normally, but Flutter can help. We still have the SDK working on our behalf, adapting many of its widgets automatically, with no code changes, to the platform that it's running on. Also good news, because we encapsulated these decisions about which aspects of the platform we wanted to use in our app, we only have a few particular points that we need to revisit within our code base. A lot of our code doesn't particularly care which platform it's running on. There's specific ones that we need to revisit. And so we can do that and make decisions there as well. We can check the platform and say, am I running on Android, or am I running on iOS? Well, if I'm on iOS, I'll stick with the CupertinoApp as the root. If I'm running on Android, I'll use the MaterialApp. Further down in our widget hierarchy, we can say, I want that navigation bar across the top. I want the Cupertino one on iOS, and I want a Material one on Android. And so I can make that decision and still encapsulate it in a widget. And then at the very end of our widget hierarchy, the leaf nodes that we're using for input, we can make the same kind of decision. Right there we can say. If I'm on iOS, I want to use a CupertinoSlider. If I'm on Android, I'll use a Material slider. And we can encapsulate that decision into a widget, as well. So time to code? BRETT MORGAN: Almost. ANDREW BROGDON: And fair warning, you're about to hear us use the word adaptive quite a bit when we're making widgets. That just means it's a widget that's going to make a decision about how to compose itself based on the platform. BRETT MORGAN: OK. My computer's finally caught up. Off to the demo computer, please. Ah, there we go. So as you may have noticed, there's been a slight change in scenery. One, our software team have been busy writing code and we now have implemented our list of vegetables. But we're also on the Android device, and well, as much as it's great that our code works, it does look a little weird to have this iOS feel on an Android device. So let's turn around and start bringing this back and making it feel correct for our Android users. So the first thing we've done is, of course, we pulled in Material, and we've given ourselves an ability to actually figure out which platform we're running on. So the first thing we need to think about changing is our friend CupertinoApp. What we're going to do is, depending on which platform we're on, whether on iOS or a Material device, we pull in CupertinoApp or MaterialApp. Next thing we're going to do is we're going to-- of course, as Andrew mentioned, we're going to use the word adaptive a lot-- and so it's time to actually adapt our main screen. So to implement, as Andrew was talking about, the difference between the flat hierarchy and the-- what did you call it? ANDREW BROGDON: Flat navigation and hierarchical navigation. BRETT MORGAN: Thank you. What we're using is on iOS, we're using the TabScaffold to give ourselves the buttons across the bottom-- these guys. And on Material, we're just showing the log screen as the root of our application, and I'll show you how we gave our hierarchical navigation in a second. So the first point we're starting with is AdaptivePageScaffold. That takes the title of the scaffold and the child. And when we're on iOS, we just, as we were before, use the CupertinoPageScaffold. And on Material, we use the material Scaffold, and we introduce a drawer on the side. So let's go ahead and wire that in. And as you can see, we suddenly have this Material-looking screen. We don't have the buttons across the bottom. And when I click correctly, we can navigate into our list. And ooh, that looks a little wonky. ANDREW BROGDON: We got some text styles to work on. BRETT MORGAN: We should fix that. ANDREW BROGDON: One thing to note-- the widget that we created to make that decision for us isn't very big, and we're still able to be very declarative and not too imperative. We can make one decision and then say, this is how I'm built this way. This is how I'm built another way. BRETT MORGAN: OK. Next step. Let's wire that all in so that we're using the AdaptivePageScaffold everywhere, and suddenly, everything looks a bit better. We still have our CupertinoSlider and our SegmentedControl, which we need to deal with next. ANDREW BROGDON: Yes. BRETT MORGAN: Oops. Now you know how I do it. ANDREW BROGDON: And it works if you rotate the device. BRETT MORGAN: OK. So next thing we're going to do is introduce a layer above our CupertinoSlider so we can correctly figure out which one we're using depending on platform. We've passing the value, the min, the max, the changed on callback handler. And on iOS, we just go out to CupertinoSlider, and on Material, we go out to Material Slider. Why is that not working? Click it once more. ANDREW BROGDON: We have some I/O gremlins in our demo. BRETT MORGAN: We do. OK. ANDREW BROGDON: One of the reasons that we're making these widgets specifically-- because it might look like we're just taking some properties and drilling them down into other widgets-- is that they allow us to make the decision ourselves. When we replaced that CupertinoSegmentedControl-- BRETT MORGAN: You know what I did wrong? I didn't wire it in. It wasn't the system. It was me. ANDREW BROGDON: There you go. You do still have to wire in the widget and actually deal with it. BRETT MORGAN: There we go. ANDREW BROGDON: There we are. BRETT MORGAN: We got Material Slider. Next step, we're going to adapt our text. So this is still looking a bit Cupertino. It's a lot of typing, I know. ANDREW BROGDON: It's doing a lot of work for us. BRETT MORGAN: If only I could get this in my day job. The next thing we're going to do is adapt the text button down at the bottom. We're going to selectively use either a CupertinoButton or a RaisedButton. Let's go ahead and wire them in. And as you can see, this is now a Material text field and the raised button. And back to slides. ANDREW BROGDON: So there's one topic we haven't covered yet. We've been focusing mostly on how to adapt your user experience and your UI to the various platforms. But there's more to an app than just its UI. There are native APIs that we might want to interact with. How do I get the user's location in a convenient way on both iOS and Android? How do I interact with other aspects of the platform? The good news here is that Flutter offers a technology called Platform Channels. Every Flutter app has a little bit of Dart code and a little bit of native code running in it, and they can talk to each other. The Dart code, for example, can ask the native side, hey, would you run a method for me? And the native side will say, no problem. I'll catch that message, run the method. Here's the return value. And this works the other way, as well. Native code can call into Dart and ask it to do something for them. You can also use what's called an event channel to expose a stream of data coming from the native side. So if you wanted to know what the air pressure was with your device, you can expose that as a stream of values that are continually getting updated and exposed as a native Dart Stream. And so you can use this technology to add a little bit of native code to your apps to do whatever it is you need to do. A little bit of good news on that, though, is that for most of the common use cases, packages and plugins have already been built. This is a screenshot of Pub. It's the package manager for Dart and Flutter, where you can find thousands of open source plugins and packages for both Dart and Flutter. And we're going to be talking about one called path_provider. This is one that's actually maintained by the Flutter team. There's a group of engineers within the team that's just dedicated to the package ecosystem. And what this does is it figures out where documents should be stored on a particular platform. So Dart can totally save files, knows how to read things back. No problem there. But the temporary directory on iOS is different than the location of the temporary directory on Android. So the package exposes an interface in Dart to say, hey, where should I put a file in the temporary directory? Can you find that directory for me? And the Dart code will invoke a method, using Platform Channels, and the plugin includes a little bit of native code for both platforms that listens for those calls and says, oh, you want the temporary directory? No problem. I'll run off and invoke my method to figure out where that is, and then I'll return that to you as a string. And the Platform Channels handles marshaling the data back and forth and automatically converts types for you. I was a slide behind. All right, so let's look at how we can put that to work in our app. We're going to take a look at our model for the first time. BRETT MORGAN: Oh, yes. ANDREW BROGDON: Oh, over to code. BRETT MORGAN: Like magic. OK, as you can see, we're back here on iOS. We have all of our wonderful application, and we can turn around and eat like 10 apples, add that to our log. We go over to our log. We've got this wonderful thing. Now, I'm just going to motivate what we're about to build by showing you something that obviously you guys will have hit on the odd occasion. If I turn around and do a full restart, I lose my state. So wouldn't it be really great if we turned round and actually saved what the user was doing to disk so we don't wind up with this disappearing state problem? So what we're going to do is we're here in the models file, and I'm going to utilize the package that Andrew just spoke about, which is our path_provider. So what we're going to do is down here in the AppState object, which is the state of the application, as it were, we've been using it up 'til now-- it has all the vegetables that we have, including fruit. Naming things is hard. And we have our log entries. So what we're going to do is we going to create a pair of methods here that give us the ability to write entries to disk and read them back in. So let's use our path_provider to find out where the application documents directory is, create a file-- oh, name a file, entries.json in there, and then write our entries out using the power of JSON. Next, let's do the same with reading entries in. And we can again get the application documents directory, use the same file name, deal with the obvious case of there is no file here, and finally, actually read it in and decoded it as JSON. Next thing we need to do is when we actually click that Add to Log button, actually write it out to disk. And on the flip side, when the AppState object is created, read entries in from our storage and log all the entries, or add them all to the log so they're actually visible. So now that we've done that, assuming I've gone all the way to the end, I should now be able to go over here, again eat 10 apples for breakfast, add them to our log. ANDREW BROGDON: 1,300 calories? Good lord. BRETT MORGAN: That was a lot, wasn't it? Why are we eating apples? And now I should, all things willing, do a full restart and we still have state. ANDREW BROGDON: There we go. BRETT MORGAN: Back to the slides, please. ANDREW BROGDON: And so that's our talk for you guys. And everybody that's here and everybody that's watching on YouTube, as well, hopefully you've seen how we can build out an app that has a first-class interface, still feels natural to people on iOS, and if we need to go to multi-platform, we can adapt it quickly and easily while keeping our code clean. Hopefully you have everything you need to end up with users that look like this-- happy with your app. Again, I've been Andrew Brogdon from the Flutter team. This is Brett Morgan, also from the Flutter team. We have some resources for you here on the slide. flutter.dev is the website. You can find samples, including a published app that uses the Cupertino package, at github.com/flutter/samples. We're headed straight from this to the Office Hours tent where we would love to answer every question every single one of you has. Thank you so much. [APPLAUSE] [MUSIC PLAYING]
Info
Channel: Flutter
Views: 75,995
Rating: undefined out of 5
Keywords: type: Conference Talk (Full production);, purpose: Educate, pr_pr: Google I/O
Id: ZBJa-xjZl3w
Channel Id: undefined
Length: 31min 42sec (1902 seconds)
Published: Wed May 08 2019
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.