Adding WebView to your Flutter app

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[MUSIC PLAYING] ANDREW BROGDON: Hey, everybody. I'm Andrew from the Flutter team, and welcome to our workshop on adding a webview to your Flutter app. You know, Flutter widgets are a fantastic way to display content, but sometimes you just want to show your privacy policy or maybe you have a registration flow on your website that you haven't implemented in mobile yet. Webviews can be really handy for that kind of thing, and the webview_Flutter package is a great way to handle getting a webview into your app. In this workshop, we'll be taking a mobile app that runs for iOS and Android and adding a webview to it. Fun fact, by the way. I became a manager about two years ago, and since then I've been slowly forgetting how to code, so I may make some mistakes as I go through this. You probably won't see them, though, because I work with a fantastic production team that's literally paid to make me look smart. So if you make some mistakes, don't worry about it. You probably just don't have a production team, so keep at it, get yourself a great job, and hire one for yourself. All right, let's get to it. So this is adding webview to your Flutter app. You can find a link to this in the description for this video, by the way, if you're watching on a desktop. And it's all about adding a webview to a Flutter app and then manipulating it using the API that the plugin makes available. And of course, we'll be using the Flutter website as we do that. That'll be the website that we pull up in this as we go through this. So setting up your Flutter environment, I've already got myself sort of squared away here. I have IntelliJ IDEA. That's my preferred IDE. I've got the Flutter SDK installed, and I'm going to be doing this on Android. Works just as well, by the way, for iOS. I just happen to have an emulator already up and running, so I'm going to use that. And I'll have that and my editor side by side, and I'll walk through the codelab step by step and we'll see how far we get. All right. So getting started here. Looks like we're going to create a project on the command line, so let's start with that. Get on here. I've got a clean folder. Right in here I'm going to use the Flutter SDK to create a new project. And so it's Flutter pub get. There we go. And then I'm going to run IntelliJ. As I said, that's my IDE of choice here. And let's get this open. Awesome. OK, let me go ahead immediately and make this a little bit bigger so everybody can see. I know some of y'all are watching on phones. I want to make sure you can see the code here. And let's go from there. So we made that. We're going to add the webview Flutter plugin as a dependency, and we can do that also right from the command line. I could do it by editing the pub spec directly, but for simple stuff like this, just adding a plugin-- oh, in the wrong one. It does help to be in the right directory. There we go. For simple stuff like this, I can just add it using Flutter pub right on the command line. It's running pub get. Awesome. All right. So created an app, added the webview Flutter plugin. Now I need to configure my Android minimum SDK. It's something that comes up occasionally. With plugins they include both Dart code and Native code. Of course, sometimes that Native code requires a certain minimum SDK level. In this case it looks like the min SDK is 20, so I just need to hop into my Android sub project. Here, let me pull this up. Android app. I'm going to go ahead and get into the build.gradle file for the app. Let me do my zooming trick for this as well. There we go. I'm just going to scroll down here, and so there we go. So we've got minimum SDK is on line 48, and it's just using the default minimum SDK version from the SDK itself. I'm going to update that to 20 because that is what is recommended by the codelab. And let's run it just to-- let's just make sure everything runs. Always a good idea. So I've got my emulator open here and let's run this bad boy. There's our app. It's coming up. Flutter's reminding us that it supports hot reload, and there we go. The counter app, as we've all seen from Flutter create. So this is-- we haven't done any Dart code changes yet, but we have included that webview plugin and we have changed the minimum SDK for Android, so we're ready. Let's go on to step four, adding webview-- adding the webview widget to your Flutter app. Replace the content of lib/main.dart. So we're basically going to erase everything in that main file and put it a bunch of new stuff. Piece of cake. I wish I could cut and paste all my code. So we've gotten rid of the previous contents. Then we have web app that just looks like a Scaffold. You see that on line 22. We have an app bar and then there's that webview widget on line 26 here, right there, and we're giving it an initial URL of flutter.dev. So yeah, let's do-- let's save this, and that's going to freak out because I need to do a hot restart, so let me just go run that. We'll do a hot restart because when you replace your entire app, hot reload can occasionally freak out on you. Don't worry about it. You just do a hot restart and there you go. Awesome. So that's how easy it is, apparently, to get a webview into your Flutter app. You just add that dependency and pop in the widget. There we go. There's the Flutter website. I can scroll around in here. I should be able to click and go to places. Yep, there it is. The emulator is giving me a little trouble with the icon font. Don't worry about that. It does work on real phones. Otherwise one of our tech writers would be freaking out right about now. But the icons are there on a real phone. Cool. So that's step four, adding, again, webview widget, a webview widget to your Flutter app. Want a little note in here about enabling hybrid composition. There are a couple different modes. Webview is, of course, a platform widget. It's actually running a native widget-- a native view component, and then finding a way to incorporate that UI into the Flutter widget hierarchy. There are a number of different ways to do that, and hybrid composition is one way to do it. It is not something you need to mess with though unless you're running into issues. It's more efficient to use-- to allow webview to pick its composition mode by default. And so unless you have a good reason to do it, I would just leave that bit alone. So we're going to skip past it. That is optional in the codelab. And then there we go. So we've got the app running, we've got a webview widget in it, and it's loading the Flutter website. All right, let's keep going here. Listening for page load events. So sometimes we might want to know how long it's taking for a page to load, right? Maybe you're not on the greatest network connection in the world, something like that. You might want to be able to put a little progress indicator up there. And so we've got some stuff for that. So this is webview stack, so it looked like we're going to make a new file. Let's go back into our editor here, open up the project side. I'll close that file. We no longer need it. And close Android. We don't need that anymore. Lib-- let's make a little source directory. We'll make a new file in here. I believe it's called webview stack is what it wanted us to use. Awesome. So new source file, and fill it with the following content. I can absolutely do that. Grab all of this here, paste it in there, and once again, I'll do my zooming trick here. There we go. All right, so now we have a webview in a stack. As you can see, we got a stack widget here, so we've got the webview and then a linear progress indicator right below it. And so we're going to incorporate this into that main file we already have. Let's put that in there. What else do we need? So we just imported that new file. Ah, OK. We're going to change our web view here from webview to webview stack, which I do believe takes a parameter. There we go. Awesome. Can we remove that? I didn't see it load. Let me try this again. Do a hot restart, see if we can see that linear progress indicator as it goes. OK, so it was there, but that is really subtle, especially at this zoom level. Let's just mess with that a little bit. I think there's a minimum height. Yeah. Let's make it tall. And I bet there's a-- yep, color. OK. How about let's do yellow. That's also a Flutter color. There we go. And now let's do a little hot restart, see if we see it now. Ah, there it went. You make things much taller and yellow and they're easier to spot. OK. Let's look at how that's being done. So webview has a few methods here on page started. This is on line 20. There's also on progress and on page finished. And so those are callbacks that webview offers. We can set those, give Dart functions to those properties, and it will call them. And then inside each of these, it's just call and set state to set the loading percentage, which is a field right here in the state object, and that's what-- that is what the loading progress indicator is using. So it looks like we're using those three methods to listen and then once loading percentage hits 100, the linear progress indicator is no longer displayed. You can see this collection if right here. All right. Well, cool. So we got that working. We are now listening properly for pageload events. All right. Now let's work with the webview controller. So this is a pattern you may have seen in other places, if you've ever done text input in Flutter, for example, the text edit controller. It's a pattern that's used whenever you-- widget's going to have a state object and maybe you might need to give that state object a poke or listen to it from somewhere else in the application. You need some reference that you can hold on to somewhere else, and that's the same thing here with webview controller. So this is going to allow us to control the webview from different parts of the app's UI. All right, so we're going to enter async here-- oh, and yeah, we get to use a completer too. This is awesome. So not something that you use very often in Flutter. Not something you have call to use, really. You may be more familiar, maybe, with a stream controller. A stream controller is an object you can instantiate and you end up with-- it allows you to create a stream from scratch, basically. You get both ends of the pipe, so to speak. You get a sync that you can put data into and you have the stream end where the data pops out. A completer is the same thing, but for a future rather than a stream. So a stream, multiple values can pass through a stream over time with a future-- with a completer, you just have the one future. It's going to be uncompleted to start and then you'll complete it once either with data or value-- or an error, rather. And so we're going to add dart async here to-- are we still in webview stack? Yup. All right, so we're going to add the Dart library at the top. Those always go at the top. I'm going to-- webview stack is now going to take a parameter. And it's yelling at me because I don't have that field of font. We can do that right now. And so this is where it takes a completer as a parameter, and you'll see why right here. So the way the webview works is it is managing a native view, right? This instance of the webview is a platform view, and so the webview widget is responsible for managing that. And so you create a web view, it runs off to create the platform view that it depends on, and then it comes back when it's done and says, OK, I'm ready. Here's a controller to use when controlling this widget. And so you can see right here, this onWebViewCreated is a callback that gets called when that webview is created. So we can pop this into here and you can see it's completing that completer with a value. So once it gets the web view controller from the web view widget in this callback, it completes that future. And so now we can use that future elsewhere in the app to interact with the webview, which I'm guessing is what the rest of this page is all about. So crafting some navigation control. Yep. OK. So we got another new file here. This is navigationcontrols.dart. Let's make that happen. So new file. Controls.dart. There we go. And I've got a lot of content to stick in there. So this is just making a row of buttons, it looks like. Once again, zooming in for everybody. And so yeah, this is making-- so it's using this future for the web controller. If the future is uncompleted or the controller is null, it's just making some buttons that aren't enabled. They don't have an onPressed. If it is, then it's passing back a whole bunch of icon buttons, looks like, with handlers, onPressed handlers, that are using the controller. Actually, these aren't even buttons. These are just icons. Yeah. All right, so now we have this file. I'm guessing now we need to go into main.dart to use it. So we do. OK. That's not cutting and pasting the right thing. There we go. Let's go back into main. There we go. And put the webview import back. Let;s go with spacing in here. That would go there. And there's this note. I'm going to import the navigation controls file we just created. I'm working real hard to keep my imports tidy. It's very important, alphabetical order. And here is where that completer is created. There we go. Does that go in the state object? Yes, it does. Mhm. And then-- OK, so here on the app bar we're going to have those buttons. Let's get those in after the title. And then webview stack now takes that completer as a parameter. And it's going to yell at me because it can't be constant anymore because I'm giving it a nonconstant parameter. And I want to say that's it. Do I have anything else in Dart analysis? Oh, my widget test hold on. Testing is not actually part of this codelab, which makes sense that the widget tests would be losing its mind over the fact that I've changed all the code. None of the syntax in that test even applies anymore, so let's get rid of that. Won't have to worry about it. OK. Let's save this real quick. And I got a message from Dart, reload rejected. Const class cannot remove fields. Try performing a hot restart instead. OK. I always do whatever the SDK tells me. We do a hot restart here. And there we go. You saw those buttons pop in once the webview had loaded. And so now we should be able to reload, I believe. Yep, it's reloading. And if I go in somewhere, I should be able to navigate back. Awesome. And so let's take a look again at those navigation controls. Let's see. So here is the arrow back, and it's just calling-- calling some methods on the controller, canGoBack. It's checking to see if that's possible. Oh, so actually if I do this, it's going to pop up a snack bar and say, hey, there's nothing in the history. I can't go back anymore. Interesting. And then arrow forward is wired up to controller.canGoForward, and then goForward. And then the last icon button just goes controller.reload. Awesome. Now we got the web view controller in place and we have some working navigation buttons. Not too shabby for, what? 15 minutes work so far. All right. Anything else we needed to do in this section? I think we're good. All right, keeping track of navigation with a navigation delegate. So this is a handy thing. Let's say you are using a privacy policy. You're using a webview to display a dock like that from your team's website or something like that. Maybe you have a site footer that's got all your social media icons at the bottom, and in theory somebody could just scroll the webview down and start going to social media or pulling up YouTube and watching videos in the webview in your app. That might not be a problem, but maybe it's something you want to prevent, right? And so you can use this navigation delegate to sort of build a little fence around your webview and make sure it only goes to the places you want it to. So let's take a look here, grab this. This is going to go right into, again, webview stack. It's just part of our constructor for the webview. And it's going to go all the way at the bottom here, looks like. Right there. And so now we've got this in place. This is just a simple method that's going to get called any time the webview's about to navigate somewhere, and it allows our Dart code to say, hold on, I don't want you to do that. All right. Let's go on to the menu here, and we're going to add a little bit to the menu to allow us to test that navigation. So we got another new file, lib source menu. All right. Go back into here, do menu.dart. Wait. Cancel that. Misspelled. I would like to delete that menu folder. Thank you. It was not menu.dart, it was menu/dart. Managed to confuse my IDE. There we go. And so we've got some code in here. Let me grab this stuff. We're going to define a menu in here. And once again, zooming in. There we go. So we're defining a menu here. We have one menu option for navigation delegates. Looks like we have a future builder here so once that webview controller is available, it'll start building the menu. And it's got a pop up menu button here that has a switch statement for which choice and has an item builder where it's just kicking out the individual menu items. Awesome. OK. So in this case when that first menu item is selected, it's just going to try to load YouTube. So this will allow us to test our navigation delegate. All right. And then we need to get this wired into main real quick. So let's go back there. Menu comes before navigation in alphabetical order, and we're just going to pop the menu into the app bar right after the navigation control. So we'll have those three buttons. Boom, boom, boom, and then there'll be a menu right after it. And so let's see. There's our menu. Awesome. So navigate to YouTube, and it's hard to see because I can't zoom in on this bit. But there's a snack bar that just popped up and said no, basically. You're not allowed to go to YouTube, not going to let you do it. And so let's go back and look at that real quick in the webview stack. So that is this navigation delegate making that decision. So it's a simple function. It takes a navigation request as its only parameter, and this code is parsing the URL out of that request and then just checking the host to see if it includes youtube.com. And if it does, it blocks it by returning prevent, navigationdecision.prevent as the decision. And otherwise it returns navigationdecision.navigate, which means OK, go do your thing. All right. So yeah, we got some guardrails now on our navigation, and we have a menu that we can probably add a whole bunch more choices to over the next few minutes. All right. Let's take a look at JavaScript, evaluating JavaScript. So by default, JavaScript should be restricted pretty heavily on a webview for security reasons. But you can turn off those protections, which we are about to do. So we're going to go in here Mhm. Yep. JavaScript mode unrestricted. There you go. We are flying without a net here. Awesome. OK. So we just enabled unrestricted JavaScript, which means we can now do some things like pass JavaScript into the web view, and that's what we're about to do. So we're going to go back to the menu now and add a choice to it. Let me save this. Oh, accidentally doubled up a line. I'm going to go back into that menu here. We're going to add another one of these, user agents. So we're adding another menu choice to that enum. We're going to have another code snippet that takes action if that one is selected. There we go. And we're going to have another pop up menu item. Awesome. And so now we have two menu choices. This second one is called user agent. We've got it listed in the menu now, and we've got a handler for it when it's called. So if I Save, you should be able to come over here and see, yep, two choices. Show user agent, and if I select this one, there's my user agent string. This is the string that every browser defines to identify itself to the server. And so let's see how we're getting that. So here in the handler we have runJavaScriptReturningResult, and it's just grabbing-- it's the world's simplest JavaScript-- just a navigator.useragent. So it's not even a full on method or anything like that. It's literally just an expression and it's just grabbing the user agent directly out of the JavaScript engine running in the webview and popping it in into a snack bar. Awesome. Let me do that again. Oh, wrong choice. Show user agent. There it is. Yeah, Android 11. Chrome 83. It may be time to update my emulator, get a different one there. I think I'm running Chrome 90 something on my desktop right now. But it works. That's the important thing. All right. So there's some JavaScript that we were just executing. Anything else in this page? Don't think so. All right, now we have JavaScript channels. So you may be familiar with platform channels in the Flutter SDK. The JavaScript channels are something that come out of the webview package. They're a way for you to register a channel that the webview can use when it decides it wants to send you something. So let's take a look at that. We're going to modify webview stack here. We're going to give it some JavaScript channels. Awesome. Let's Go back to webview stack here. Now it's going to yell at me because that createJavaScriptChannels function does not exist. So let's go make it. There we go. So this is creating a JavaScript channel. It's giving it a name, SnackBar, and it has a little callback for onMessageReceived. It just takes the message and pops it into a snack bar. And that JavaScript message includes a string as part of its payload. All right, so when we call this method here, this JavaScript channel, and we give that to the webview, what it does is create an object inside its JavaScript context where inside the web view JavaScript is running in the page, and that JavaScript Object can be used to send messages out. And I imagine we're about to get a little bit-- yep, back to the menu. So we're going to do another one here. There we go. Let's save that. Let's go out to the menu. We're going to do just like we did before. We're going to add something to the enum. We're going to add a big old chunk of JavaScript in a string. There we go. If you haven't seen this, by the way, triple block quote or triple single quotes or triple double quotes, either one, are multi-line strings in Dart. So that's how we can have this enormous string here on multiple lines, but have it all be part of the same string literal. All right. And we need another pop up menu item so that I can select this new one that we just did. A little hot reload here, open up the menu. So look up IP address and-- oh, hot restart. I've already created my webview, I've already got the stuff set up. If I want to give it new JavaScript channels to begin with, I've got to do a restart. So let's do that. That's my webview state had already been set up, right? There we go. Let's try that one more time. Look up IP address, and there we go. There it is. So let's take a look and see what's really going on here. So we have run JavaScript. In this JavaScript it's doing an XML HTTP request, an asynchronous web request, and it's grabbing my external IP address in JSON format. So it's going out to an external service, grabbing my IP address. If it's successful, it's parsing the JSON and then just passing it back. PostMessage here, that's the JavaScript channel part, SnackBar.postMessage, and just giving it a string of IP address and then the IP. There you go. So again, that's an asynchronous call, right? That's the webview calling out to some other service, waiting on a response, getting a response, and then deciding it wants to send a message into the dark code that's executing in my Flutter application. And that's what JavaScript channels allow you to do. They give you a way to give that webview a chance to-- a mechanism that it can use to send data whenever it decides it wants to. All right. Cool. Anything else in this section? I don't think so. All right, we're on to managing cookies. Cool. So we have a lot to add to the menu. In this section. All right, we got five things. Let's get those into place. Back into menu.dart. I'm guessing I have five enum values here, and going to convert the menu to a statefull widget so it can-- ah. So it can keep a reference to a cookie manager. Fortunately, my IDE can do that conversion for me from stateless to statefull. Actually, I say that. It's the SDK. It's a quick assess, technically in the SDK, that the IDE plugs into and then I can add that field for a cookie manager right there and get a list of all cookies. All right. So it looks like we're going to have a bunch of other helper methods here, so let's start grabbing these and putting them into menu.dart. So there's one. There's two. I wish I could always add code this quickly when I was working. That's a-- remove a cookie, I believe, is last. So I've got five helper methods. I need to wire up five menu options. So we're going to add-- grab those here. These are the handlers. They get executed here. Save those, and we have some new pop up menu items. Let me grab those. Those will go right here in the bottom. Going to save them. That's a lot of-- that's a lot of-- OK, good. There's a recommended order of operations here to use these five menu items in. . And I did change the state object for the webview, so I'm going to do a hot restart again just to make sure that all gets done correctly. There we go. All right. In what order shall we mess with these? So list cookies is first here. Let me do that. Whoop. All right. List cookies. So there's the cookie, by default, that flutter.dev gives you. OK. Clear cookies. All right. So there were cookies, it says, now they were gone. Just got that in a snack bar. I'll run clear cookies again. No cookies to clear this time. List cookies, also empty. And then we can add a cookie. So added a custom cookie. We can set a cookie. I'm curious what the difference is between those two. Let me take a look at the code in a second. And then we can list cookies again in there. There's our custom cookie for foo=bar, and remove. All right. So let's go back and take a look at the code and see what's going on in these helper methods. So list cookies is using that controller to run JavaScript. So it's runJavaScriptReturningResult, which we used before on the navigator.useragent to get the user agent. This time it's just doing document.cookie, so straight up vanilla JS using the document APIs. Clear cookie is using the cookie manager. That's that object that we made that also comes out of the webview package, I believe. And that's using-- we're its API to clear out the cookies. And looks like it returns a Boolean, I think. Yep. To indicate whether there were any cookies to clear. So here we go. Here's add cookie and set cookie. So add cookie is using, again, document.cookie. So this is a vanilla JS. I'm going to use the normal APIs just as you would with normal JavaScript, and it's setting up-- creating a cookie that way. Set cookie looks like it's doing the same thing with cookie manager. So it looks like you have two options to set a cookie. If you want to you can use JavaScript directly by calling runJavaScript on the controller, or you can use that cookie manager, which has what looks a more structured way with optional name parameters here to create the cookie. And remove cookie, a little bit of trivia I learned checking out this codelab. One way to clear a cookie is to remake it and give it an expiration date that has already happened. So here you can see it's assigning the cookie using document.cookie, but it's giving it an expiration date of 1970, and I'm pretty sure it is not before 1970. I would probably have a very different hairstyle if it was. All right. So those are our five methods for setting cookies. Awesome. Anything else to do on this step? Nope, doesn't look like it. All right. We got one left, loading Flutter assets, HTML strings, and files into the webview. So yeah, certainly sometimes you might want to use your webview to access something that's stored externally, like I said, a privacy policy or terms of service or something like that. Maybe even open source licenses, although Flutter does have a way to do that. But maybe you have some local HTML that you need to display somehow and you don't want to manually convert it into widgets, right? Maybe you have a library-- maybe you work in an enterprise and you put one of those libraries that does HTML based reports that your company has invested a zillion dollars in and you need to find a way to use. This is maybe a way you could bridge that gap, right? Use a webview, get the HTML from that library, and then pop it in there. Let's take a look at it. So inside pubspec. OK, we're going to use path provider here. So we're going to add path provider. Let's go make that happen real quick. Where's my pubspec? There you are. And again, I'll zoom in a little here. We're going to go down to our dependencies and add path provider right there. And oh, looks like there's even got a version bump. Awesome. And OK, some assets. So we're going to load from assets. We actually need to register those assets. Bunch of comments here in the default pubspec, by the way. Don't be afraid to take those out when you're ready. There we go. So we got assets now, we have an index file, and a styles file-- which do not currently exist, so I'm guessing I'm about to go make those. Looks like it. Yep. And you know what? I added path provider, which means we're going to have to restart this app, so I'm going to go and stop it right now. We'll have to do a full restart. But right now, we can go in and make these other files. Assets, too, we'd need a full restart for, right? Those do not get hot reloaded. So a folder called assets, folder called www, and a folder called styles, I believe it was. And then we have an index.html in that base www folder. There we go. I'm just going to drop in some content, zoom that one in a little bit. So we got just a basic HTML file here, title, link to a style sheet-- which is the other file we're about to make, I think-- and just an H1 header and a paragraph. And then we have the world's simplest CSS file here, one directive for one element. Let me go ahead and grab that. Come here, then we'll make that in here. Awesome. So again, very simple stylesheet. Let me go ahead and close the project explorer. And so we're going to have some helper methods it looks like in the menu, again. So we have onLoadFlutterAssetExample, so let's get that into place. Those are going to go, again, at the bottom. We'll have load a local file, so it looks like we've got some things to work on here. We're going to add a-- go ahead and get dependencies for pubspec. I'm going to have a constant up here. So this is just a straight up string constant with some HTML in it. And I think we need to get these imports if we don't already have them. So I'm missing a couple here. There's dart.io and path provider, right? We added that to the pubspec. We should probably import it somewhere. There we go. And what else do we need? So LoadLocalFileExample and PrepareLocalFiles. So it looks like this is going to make a local file on my emulator and then tell the webview to load that local file into itself. Let's get those in place and load HTML strings. We did that one, that one, prepare local file. There we go. OnLoadHTMLStringExample. Cool. So we got our helper methods. Now we just need to add a few more menu choices. Let's get those going all the way at the top. Well, almost now. We got that string constant there. Let's get those in place. I'm guessing we have some more-- yep. [INAUDIBLE] item handlers. Let's get those going. And you're a lowercase B. There we go. And then we just need to add them to there. All right. So I think we're ready. Let's fire the int back up and see what happens. Let Gradle do its thing here. All right, there we go. So we got our webview firing back up. And of course, we cleared our cookies, so we get the cookie policy pop up again. That's awesome. And let's play with some of these things, these new menu choices. So load a Flutter asset. This is going to load that file and stylesheets-- there we go-- that was being used as-- those were the assets that we added, so those are hardcoded into the app. And then we have load HTML string. This should just be that string constant that's getting put in there. Local demo page. Yep. I'm going to load local file, which I think is the same string. So let's real quick go back to the Flutter asset. There we go. Let me do the local file so we see the color change from the stylesheet. There it is. So you can tell it's a different thing. It did get the data in there correctly. And let's check out that code because I just kind of pasted it in. So we had onLoadFlutterAssetExample. This is just using an API method on the controller, loadFlutterAsset, and it just hands it a Flutter asset path. And that's really interesting that it don't-- you don't have to give it the path for the CSS file, I guess. It just knows how to find it. Interesting. OnLoadLocalFileExample, that's the one-- looks like it's making a local file, PrepareLocalFile, and it's using that string constant that we defined all the way at the top, that a little bit of HTML, and so it's just dumping that into a temporary file and then telling the webview controller, load file, and giving it the path to that temporary file. Even simpler is the load HTML string example here. It's just saying, hey, controller. Here's a string. Please display this string. It doesn't get much simpler than that. It's like the webview basically saying, tell me what you want me to say and I will say it, and there it is. So we got those all wired up and working. Awesome. That is the end, I think, of step 12, which means that's the end of the codelab. We just covered getting the webview Flutter package into the app. We got that into the pubspec. We added the webview widget, and now we've taken a grand tour of the API and everything that it can do. OK, so that's it for webview and this workshop. To be honest, I had no idea the webview widget could do so much stuff until I started working with this codelab. Speaking of codelabs, by the way, if you'd like to do some more of them, head to docs.flutter.dev or dart.dev/docs to find a bunch of additional codelabs on both Dart and Flutter. And of course, if you enjoyed this video, we have new stuff on this channel about every week or so, so consider hitting that Subscribe button, and we'll see you next time. [MUSIC PLAYING]
Info
Channel: Flutter
Views: 68,872
Rating: undefined out of 5
Keywords: Flutter, widgets, webviews, JavaScript, website development, web development, app development, UI, user interface, open source, Google I/O, Google IO, I/O, IO, Google I/O 2022, IO 2022
Id: FrqGGw9DYfs
Channel Id: undefined
Length: 43min 5sec (2585 seconds)
Published: Wed May 11 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.