Let's Learn FlutterFlow
FlutterFlow is a visual builder for Flutter + Firebase apps. In this episode, Abel Mengistu will teach us how it works!
Links & Resources
Full Transcript
Click to toggle the visibility of the transcript
Captions provided by White Coat Captioning (https://whitecoatcaptioning.com/). Communication Access Realtime Translation (CART) is provided in order to facilitate communication accessibility and may not be a totally verbatim record of the proceedings.
Jason: Hello, everyone, and welcome to another episode of Learn With Jason. Today on the show we have Abel Mengistu. Abel, thank you so much for hanging out with us today. How you doing?
Abel: Thanks, Jason. I'm good. How are you?
Jason: I'm doing great. I'm super excited because we're doing something that I don't really know anything about, which is really exciting to me. But before we talk about that, I want to talk about you a little bit. So for anybody who's not familiar with you and your work, do you want to give us a little bit of a background?
Abel: Yeah. So I'm Abel. I'm the founder and CEO of FlutterFlow. My co-founder and I started building this last year after we were working on a different startup where we were building a mobile app and kind of realized, wow, technology around mobile app development has gotten so much better, but it still felt slow. The process of building and launching an app. And that kind of inspired us to work on FlutterFlow. Yeah, that's a little bit about me. My background before that, I was a software engineer at Google, where I worked on primarily machine learning and data science stuff. So, not mobile. That's kind of the surprising part. You know, if you asked me five years ago that I'd be doing this, I would say no, that's very unlikely. But as we started working on it, we kind of saw how frustrating it was. So that's kind of the background.
Jason: Cool, yeah. So this is -- and that kind of round about brings us to what we're talking about, which is today we're going to be dealing with FlutterFlow. So FlutterFlow, if I can repeat this back, is a way to build mobile apps using Flutter and Firebase that doesn't require you to write the code. It's a visual builder.
Abel: That's exactly right.
Jason: Nice. The vast majority of what we do is web content. Could you talk about what Flutter, what it's for, why you would reach for it as a developer.
Abel: Yeah, Flutter is Google's Open Source UI toolkit. It lets you write your application in Dart. They use the Dart language. It allows -- the special thing about Flutter is it allows you to write it once and actually compile it to all these different platforms. So you can write Flutter code and when you want to run it on iOS, it will compile it to that architecture. And it uses -- underneath the layers, it uses the same graphics and engine that Chrome uses. It's all scaled. So you get very performant apps. A lot of the times if you've looked at -- kind of going back to like the early days, almost wrapping web views inside of applications, you get performance issues because it's not running because it has to talk to the native language from JavaScript and it's going to be slower. So the Flutter team -- Flutter gives you an advantage over that because it compiles machine code. So that's what -- when we started building an app after we left Google -- sorry, froze up a little bit.
Jason: No, you're back. You're back. Well, cool, so when you left Google --
Abel: Yeah, and we looked at what's kind of the best thing out there to build a cross-platform mobile app. It was really between React Native and Flutter. I think what happens mostly is because React is so popular, React Native is just easier to pick up for people who already know React. We didn't do React before. So we were trying to see if it was the best thing we can use. You got to start from scratch. This is back in 2019. So when we looked at the fact it compiles beyond mobile, too, that's an advantage. FlutterFlow itself is built in Flutter. It actually compiles and you can build a desktop app as well.
Jason: Nice, so it's not just mobile.
Abel: Correct.
Jason: So as a developer, if I start using this Flutter approach, I can target web and native. So does that mean I can also ship to, like, an Apple device and an Android device?
Abel: Absolutely, yeah.
Jason: Cool, okay.
Abel: For web, I would say they primarily target web apps. So if you're doing a website, it wouldn't make sense to use Flutter. It's just going to add too much complexity. But if you're building a complicated, you know, something like FlutterFlow or a web app, it makes it -- you know, it's comparable, and I think React has a leg up there for sure. It's just been around for longer. But the ability to have that same code base run everywhere is just pretty huge.
Jason: Yeah, that definitely seems like kind of -- we're seeing a few angles of this. Flutter is doing it. Kotlin is trying to do the same thing. I've seen a couple other -- React Native is a long-running attempt to do this. So it's very interesting to see this kind of, like, how do we do the one code base deploy everywhere approach. So I'm really excited to see kind of how this works. So the other side of this is Firebase. We've talked a little about Firebase on the show. Firebase does a bunch of things. Primarily I think most people know it as a database. Specifically, I think a lot of people get excited about the real-time aspects of Firebase as a database. But what kind of attracted you to Firebase in the FlutterFlow context?
Abel: Yeah, so the primary -- so we use Firebase for FlutterFlow itself. When we're deciding to build FlutterFlow, we thought what would be like the fastest way to build an app for developers? We thought that was Flutter plus Firebase. We wanted to build something very quickly, get it in front of users. That's one thing we learned actually from our last startup. One thing we did wrong the first time around is we didn't move as fast as we needed to. Being able to iterate quickly and test your ideas with the market is important. So we thought if we wanted to enable people to do that, what is the best way to do it. We thought it was Flutter and Firebase. So we actually built FlutterFlow with Firebase itself. We wanted to make it easy to -- and the reason Firebase makes it really easy is because you don't have all of the complexity of setting up clusters or manage it. You don't have to go through a lot of process to really just worry about your infrastructure. You just think at the data layer. So I want authentication. I want my, you know, collections to look like this. User collection that has this kind of data. It doesn't even have a schema, so it doesn't enforce it. It's so easy you can just start writing to the database and reading.
Jason: And we can have a debate about, like, the structure versus unstructured data later. But I agree with you. Forgetting something out the door, like being able to just set it up and go, you know, that's a soapbox I've been standing on for a long time. How do we get out of the way of developers so that they're able to just have an idea and then build that idea, instead of having to build eight layers of foundational stuff so they can start on the idea. So it sounds like we're pretty philosophically aligned there.
Abel: And we do use -- like internally, we use schema and enforce that because it makes it easier to work with connecting it to the UI, et cetera. So yeah, we like that Firebase lets you do it in a schema fashion, but we enforce it.
Jason: Sure. Very, very cool. All right. I feel like I could ask you a bunch of questions, but it's probably easier rather than trying to talk about it in the abstract to just like look at this thing. So let's switch over to pair programming mode. What we'll do here is let's go ahead and start by giving a shout out to our captioning. We've got Rachel here today from White Coat Captioning. She's taking everything we say and writing it down so that this show is more accessible, which I appreciate so very much. And that is made possible through the support of our sponsors, Netlify, Fauna, Auth0, and Hasura, all of whom are making this show more accessible to more people. While you're clicking on things on the internet, make sure you go and check out Abel's Twitter. You can get the latest on FlutterFlow. What else are people going to see on your Twitter? Is it all tech all the time? I see a basketball in here. What else are we going to see?
Abel: Yeah, it's mostly tech and FlutterFlow these days, yeah.
Jason: For sure.
Abel: But stay tuned. I might start switching it up.
Jason: All right. So here's the FlutterFlow home page. As we were saying, FlutterFlow is built on top of Flutter and Firebase. So here's a bunch of links. These will all be available in the show notes as well. With that, I think, Abel, today because we're using FlutterFlow, you were going to take over and do a screen share here. So let me give you control over that. You should be able now to share your screen.
Abel: All right, I'm sharing. Can you see my screen?
Jason: We can see it.
Abel: Cool. Yeah, so this is the FlutterFlow. I've already created a project. I wanted to do a quick demo of building a simple app that I had in mind. Also, I haven't built this yet. As demos go, it's going to be interesting how it works out.
Jason: That's my favorite part about this show. We just kind of make stuff up as we go. I also just realized that I broke the -- oh, no. How do I get the speaker? Give me just a second to get you back in the right place on the screen. I broke it. This is a new format for us to do this size of video. Or to do you sharing. I'm just trying to figure out what the right size is so you are actually on the screen. There you are. Perfect. Live debugging. (Laughter) All right, we're ready. We're looking at you. We're looking at FlutterFlow. What did you have in mind? So this is the homepage, right? So if I log in -- so I go to FlutterFlow, I create an account, and this is the first thing I'll see? This kind of empty project.
Abel: Yeah, so if you just create an account and you log in, you'd see this screen, which has all the projects that you have. It lets you create a project. You can go to your account settings and so on. We kept it pretty simple, focusing on building apps. So that's the main thing. You can get started immediately. This is kind of what you get. I have basically a blank project here. To kind of make it easy, I've already created a Firebase project because I didn't want to go through Firebase setup. It's relatively quick, but I didn't want to spend time doing that. I already have my Firebase created here. Doesn't have a fire store, so I'll create that quickly. So FlutterFlow is tightly integrated with Firebase. If you want to do authentication or connect the data, that's kind of -- we allow API access, too. It's really mainly focused on Flutter/Firebase ecosystem. I wanted to build this app just kind of for fun for myself. I thought I might as well get started on this show. It's just a way to find courts, so basketball courts where games might be happening.
Jason: Oh, nice, yeah.
Abel: You can see what people are saying about the games. I'm a basketball fan. I like to play. But sometimes it's hard to find out exactly, you know, when games are happening, which courts, and so on. So I thought it could be fun to do something that lets you add those and be able to search them.
Jason: Absolutely. Also, welcome Ben and friends. Welcome to the stream. And Eco, thank you so much for the sub. We are working on a FlutterFlow app today. So we're actually looking at Abel's screen right now. Abel is the co-founder and CEO. We're going to build, as he just said, an app using Flutter and Firebase visually through FlutterFlow to find basketball courts to go play a game. So I'm pumped about this. This is going to be great.
Abel: Yeah, cool. So let me get started. One thing is I'll set up the Firebase here. I've already created the project and added my API keys here. This is the configuration key and the Android/iOS configuration which is necessary to communicate with Firebase. Let's get started here, just enable email sign-in. All right. So first thing I'll do is just start with a log-in page and a homepage. Then we'll figure out how that looks. Maybe just do a simple map. So I'm thinking log-in, you know, associate data with users. Then at the homepage, ideally you have a map where you can see different courts that have been added. Then a page for adding data to Firestore. So these three pages should be pretty easy to do.
Jason: So this is ambitious, right? Just to recap, as you're setting that page up, we're looking at building an app that's connected to a database that's going to have user log-in, going to have user uploaded data, and it's going to have map, like geo vis. That's ambitious as hell for a 90-minute show. So I'm super excited to see how productive this makes us. Also, thank you Cocasyn for the scrub -- for the sub. Make sure you abuse that boop emote now that you got it. Also, I added a new emote, everybody. Look in your available emotes.
Abel: I didn't talk through this. Let me actually just talk through this. What I did here -- so I can't see the screen, if there are any questions I should address.
Jason: I'll relay questions as they come in. Right now everybody is just playing with the new emote.
Abel: Yeah, when you said emote, I was thinking, man, I wish I could see this new emote.
Jason: I worked with a company called N1 Studios and made an emote that I call compooper. It's a sad computer with smoke coming out of it.
Abel: All right. So I'm on this create page dialogue. I'm going to use a template.
Jason: Oh, this is smooth. Okay.
Abel: There we have it. So just hit create, and all it did is basically gave you the starter components. All this is modifiable. I didn't kind of walk through the UI of FlutterFlow, but we have different page elements here you can add to the UI. We have properties on the right side. There's run, debug, et cetera, or issues. There's different settings for the app. There's Algolia integration, GitHub if you want to directly push to GitHub, Google Maps. I created the Firebase project and added my API keys. The Firebase one is the same. It's bundled with apps.
Jason: The Firebase is a public client-side code, but the Google Maps one should be private.
Abel: Yeah, Google Maps and same with Algolia. I thought it could be found try to do an Algolia geolocation search.
Jason: (Laughter) I love the --
Abel: It's the most overpromise, underdeliver.
Jason: Honestly, any time that anybody is doing anything with map visualizations and stuff, I know it's getting easier, especially with like the Google Maps SDKs and other companies like Map Box are doing a good job. But it feels so ambitious to be like, yeah, I'm going to get information and place it on a map in the world visually. Sounds like so much to do in a short time. But also, you just built a log-in page in one click. So I'm feeling pretty optimistic here.
Abel: So what I'll do here is get rid of the safe area. The safe area is for kind of the space between. But it looks a bit nicer without that. This is already preconfigured. So if you notice here on action, sign up already has an action. That's on authentication. Uses create account, auth provider should be email. So you use that for the email. It's already kind of preconfigured, and you can roll with this directly.
Jason: Wow, yeah, that's slick.
Abel: So log-in page is basically done. That should work. Forgot password is not linked to anything, but we can set up a password flow, which is pretty easy. Now, before we actually do the home page, I'm going to go to the Firestore and show you kind of how to source. I'm going to delete this table because it's already preconfigured. I want to show you how you do that. We already have authentication enabled here. Here you can choose what the log-in page is going to be and what the home page is. The difference here is --
Jason: Oh, nice.
Abel: If you haven't logged in, it takes you to this log-in page. If the user is already logged in, it will automatically move to the homepage.
Jason: Got it, yeah.
Abel: That's basically the only setting we have now. There's tutorials here of how to set up Google sign-in, Apple sign-in, et cetera. That's basically is here in the authentication tab. Now that we have authentication enabled, we want to have the actual user database, right, or collection. So we'll create one and call it users. It doesn't have any data, but you don't have to manually populate that. You can come in here and say I want you to create a user record every time someone signs in.
Jason: Oh, nice. Okay.
Abel: So what that does is it will actually automatically populate all the Firebase fields for you. This comes free with Firestore authentication. So you can do the Google sign-in. It will have the photo URL, display name, email, and will automatically create the UID. Phone number will be populated if you have phone sign-in enabled. So it basically kind of generates all of that for you. I think at this point, we can even pause and quickly look at -- we can go to view code.
Jason: Sure. And I have a question from the chat while you're switching over. Is it possible to use FlutterFlow to build an Amazon style e-commerce app?
Abel: So one integration that we're working on now, we still don't have, is payments. So for that reason, you know, payments is going to be a critical part of e-commerce. So it won't work without. You can build everything except for the payments part. Until we have that feature, it would be blocked.
Jason: But it's on your roadmap.
Abel: It's on our roadmap and should be relatively recent, I'd say under two months for sure.
Jason: Nice. Public day commitments. That's great.
Abel: We've already started to work on it. I will say the -- so we just recently made some changes. V Code is not rendering the code here. So I apologize about that. We won't be able to show -- I believe download code would work. You can download it and open up it in Visual Studio code.
Jason: Sure, absolutely.
Abel: Yeah, okay. So we have login set up. We have the user collection. Now, the only other thing we need is a collection for the courts. Let's see. What do we need here? A name field, just the name of the court. You can select a type here. We'll make it a string. Also, have the location and a lat/long. We can add a created at field just to show that.
Jason: Then do we need to tie it to a user?
Abel: Maybe we'll have a small -- great, yes, we need a user. The way you type that up is do a record reference. So you'd say I want reference type users. So this will be basically a reference that points to -- and the way Firebase works, all the documents have a reference, which is a path to the document. This would end up being a path to the user document. And maybe we'll add a small description.
Jason: Oh, yeah, nice.
Abel: It will be a string. And that should be --
Jason: So there's a question. What's the field type? How is that different from the data type?
Abel: Ah, I think this -- oh, right. So field type is whether it's a single element versus a list. Imagine having comments here.
Jason: Right, right, right.
Abel: You can potentially have a comments collection, which will have the user who added the comment, being a reference to the user, and maybe the actual text of the comment. So if you had a comments collection here on each court, maybe people can come in and add a comment. And the way you'd do that would be comment, it's a record reference of comments, but it's a list. So users can come in and add comments. So on an add comment button, you could essentially say add to this current courts comment feed.
Jason: Perfect. Yeah, that makes sense. And thank you for the cheers, for the bits, Ximena. 17 months. That is a long time. Thanks for sticking with me. Looks like we have a sad little hype train right now, 0% at level two. (Laughter)
Abel: Cool. So I think this should be enough. I'll add an image URL field. So maybe you can grab an image from -- so in this demo, I want to be able to run it maybe on the web.
Jason: Yeah, yeah.
Abel: So we have ability to run, and the web will compile it and put it in an iFrame so you can play with it. But it doesn't have an image picker when it's running in web mode. That's a lot of fields already. I think we're set here. We have the home page. I'll leave that blank for now.
Jason: Thank you. Thank you, Bobby Tables. Sorry, we just got a bunch of gift subs. Thank you, all. Thank you for that. Make sure you use all those emotes you just got access to. Welcome to the boop crew. Okay. So now we have a login screen, a home screen, and you added an add court screen.
Abel: Yes.
Jason: Okay.
Abel: So let's make an app bar.
Jason: So you just drag and drop that? I know you said visual builder, but in my head, the dots didn't connect until you just did that. So you just drag and drop things over to have that all work?
Abel: Correct, yeah.
Jason: That's amazing.
Abel: So I can drag and drop an image and start modifying its size. So in this case, I have an app bar. Maybe I'll change this color, maybe go a little darker here. I can make the background color also. Now we can have text here. That would be the title. So one thing you have with this app settings is you have your theme where you can select the project colors. You can add multiple colors here. You set it up once. Probably would have been nice to have a nice color thing. I'm not a designer, so it's not going to look amazing at the end here. So if you have either from your designer or you know what kind of colors you want to use, set it up once and easily have access to it here. So let's name this add court, title one. We'll make it a white color.
Jason: Thesis500 is saying, Flutter still uses Dart under the hood? Is that correct?
Abel: Correct. It's a framework you write in Dart. It's like React using JavaScript. It's Flutter using Dart.
Jason: That makes sense. Cool. So you've got the page setup here. You have a title in place. And now I assume we're about to see something that's going to blow my mind.
Abel: Well, let's see. We need to set up -- this is a page where you just add a court, right. So we'll do a text field here. Actually, before I add this, let me get a column. You want to lay out your page. These are things like columns, rows. These are Flutter concepts. Pretty intuitive, I think, but it's definitely -- like the first time you use it, it may not lay out the way you want. You can't just drag and drop things around. For example, in this column, you can set alignment to center. Now the row is centered within the column.
Jason: Okay, I gotcha. Okay.
Abel: And this is nice because it allows you to do responsive views by taking this kind of layout approach. So if I change different screen sizes, it will look nice. So add a column, give it padding here on the left and right. There's a little bit of space. Here we're just going to start adding text fields. You have some options here on the right. You can set the password, whether it's a password field or not. We'll hide it. You can have a clear field icon. The hint text, you can say like court name. Can't see the text here. Let me make the color white so you can see what's happening. So that's the hint text. I'll also give it a label text here. I think there's an underline option.
Jason: Oh, I think it needs a border color because the colors are unset. Nice, okay. All right.
Abel: There you go. Cool. So we have the court name here. I can just copy that now and paste it in the column. Actually, let me give it a little padding in the bottom. Maybe the court name. I copied it before I added padding.
Jason: Oh, I got you, okay.
Abel: Cool. Then maybe this could be description. Saturdays or something like that.
Jason: Then we need our location, right?
Abel: Yeah. Okay, so location is going to be --
Jason: Oh, we need a different thing. Yeah.
Abel: I need to do image URL. Location is going to be a different button. We just added this last night. Let's see how it goes. We have a place picker button, basically. When you click this, it brings up a dialogue that lets you choose -- it looks like searching location using Google Maps. So I have it configured here with the API keys. What it should do is you just add this button here. You can still customize the color and so on. But it's already preconfigured with an action. You can't add an action on it. But I can use the value the user has selected here. So we'll leave that as is. We have the location picker. I think that's it, right?
Jason: We need like a save button, right?
Abel: Correct, yeah. So let's make this description multi-line. You can maybe add multiple lines here. And let's add a save button. We're going to do that using a floating action button. That's just like a confirm down here, maybe. Right now it's empty. We'll add an icon button inside of it. There's also a widget tree, which shows you how your app is laid out. Some of our users prefer working in the widget tree here. So I'll do a check mark here. So we have an icon search. Let's make the color white.
Jason: Okay, cool.
Abel: Now we just need to give it an action. I'm going to actions here. I have a shortcut, command A. Here's the interesting part. It should do a back-end call. That's how we group that action and create a record. So we want to create a court. So create record and choose which collection you want to create the record in. This is courts. And now you can set the different fields in this collection. Name, location, created at, user. Perfect.
Jason: Okay. So I'm just going to -- let me just recap. So first, let me say welcome to the New Relic crew. Thank you so much for the raid. So good to see you. Second, you just hooked up a database to this form by like clicking three buttons. I'm still kind of reeling. That happened so fast. So you just clicked on the action button, and for the action, you chose create record. When you did that, it automatically pulled up your available Firebase collections, and you chose courts. Then you just hit the plus field button and it auto filled all of the fields in your courts collection.
Abel: So, yeah, we haven't attached them to anything in the UI yet, but it's ready to go.
Jason: It's so good. I love it. So the next step is we have to connect these two, like connect to court name.
Abel: Right. So the reason you see this issues show up is because these things are not associated. It already tells you location is not set, created at is not set. But as we set these fields, it should go away. So the name field, you can hard code things in the values here if you want. We'll do from variable. It's a widget state. Widget is the Flutter term for basically anything. An app itself is a widget. Then a scaffold is a widget. Here in this widget tree, all of these different pieces are widgets. That's why we use that terminology. So let's go back here. The action on this button, that's where we were. So the name should come from a variable. Widget state, and it's the court name. For example, this place picker value, we do some type checking for you.
Jason: Nice. That's great.
Abel: You can't select that. So court name. The location should be attached to that place picker.
Jason: And I like that it shows the other ones are incompatible because who would ever write a lat/long by hand? You just don't do that. (Laughter)
Abel: How do people build maps back in the day?
Jason: Man, yeah, I'm just imagining when you see maps from the, you know, 16th century or whatever and it's a semi-accurate map, I'm thinking like who drove a boat around the coastline and was like, I'm going to draw this perfectly? (Laughter)
Abel: Yeah. I can't even draw a circle perfectly.
Jason: For real, man. I couldn't draw you an accurate map from here to, like, the grocery store a ten-minute walk away from me without getting it completely wrong. They're like, oh, no, this is more or less to scale.
Abel: Yeah. So we also have some -- like right now current timestamp is only a global property, but you can associate the current timestamp at the time it's created at. Then whoever clicks this button comes up as the user who created the court. Finally, variable image URL, same way.
Jason: So when you just did in three minutes is easily an hour of hand coding. It's not just like, oh, we've got this field name, but it's okay, we have to get the reference to the database and then we have to map each field to the thing and then we got to pull the user reference out of our authentication object and we've got to map that to a field value and all the type checking and coercion there. So we just saved, just in mapping inputs to the record, at least an hour.
Abel: Yeah, we definitely saved a lot of time. That's why we were excited about this. We've actually started using it for -- because we didn't build this for web. But we've started using it in some cases because it's much quicker. Then we copy the code into our editor and work from there.
Jason: Yeah, yeah, yeah.
Abel: That's been our flow, even for FlutterFlow itself. All these components inside FlutterFlow.
Jason: That's t right? We did it. (Laughter)
Abel: Yep. And now you need to navigate back. So this creates the record, but it will automatically pop back to the homepage. So add court should be ready now, unless we have a bug.
Jason: I'm so ready to see this happen.
Abel: I think we set it up correctly.
Jason: Yeah, let's do it. So now I see this run button up here. Can you just click the button and go?
Abel: So if I do run, it will start building. The issue with that is now -- yeah, actually, let's run. It's going to open a new page and send a request to our server, which initiates a build. But it won't be very exciting. It will take two to four minutes to build. We'll do this later also. But the issue is --
Jason: There's nowhere to show the courts or anything like that.
Abel: There's login page and it goes straight to the homepage, which is empty.
Jason: That's right. That's right. We haven't actually built the app.
Abel: Right. Exactly. So here we can add a floating action button. That's just an add button. I think I zoomed in. I'll do icon button here. This will take us to the add court page.
Jason: Got it.
Abel: Okay. Let's make it. I'll go to action on top, go to add court. So now if we ran it again, you could go to add court, and we should see it come up.
Jason: Man, this is so dang cool.
Abel: I'll hit run here. While it builds, I'll continue building from this point. So I'll let this build. Then we'll add the map widget here. Let's find the Google Map, add it there. So we have a Google Map added. Also, just to have it there, I'll also add a text -- so let me take a step back. Currently we have the homepage, which has the floating action button and the map. We can't overlay anything on this map because some items can have something inside it. Like a column can take multiple children. But a map is just the final state. If you want to stack things on top of a map, you need to wrap it in a widget. You can wrap it in a stack widget. Now you can see the map is under the stack, and you can add more things on the stack. So if I want to add a text field, like for my search, I can just add it here. Now it overlays.
Jason: Got it, okay.
Abel: Because of the stack here. And what I'll do is I can set the alignment here. I'll actually wrap the widget itself in a container. That can style it a bit better. Maybe make the height like 50. I can align it a little bit so it looks like a search button there. Okay. So this is now aligned at negative 0.8. This means vertically negative 1 is the parent container. If you change the screen size, you know, that's fixed. So it helps you build responsive apps using alignment. Okay. Let's give it padding. Let's say 20 on each side. I think I gave padding to the text field. I just quickly did undo. Let's give the padding to the container so it doesn't cover them up. Reduce the opacity so you can see through it a little bit.
Jason: Oh, cool. Yeah, I got you.
Abel: Padding for the actual text field. Let's give it a border radius. Why not.
Jason: Slick.
Abel: Small touches there. Our designer would make fun of me for trying to make it look nice. I love our designer. Very talented person. But when it comes to design myself, I'm not great.
Jason: I mean, what I love about this is you're saying you're not a designer, but you're building something that looks like a native app. Like if I opened this, I wouldn't think, oh, there was no design put into this. It looks great out of the box. Also, welcome to Alex and all the raiders. What's up, y'all? Hey, Ben. Good to see you. Thanks for coming over. We're building with FlutterFlow right now. We've spent like, I don't know, outside of the gibber gabber, I think we spent a solid 25 minutes here and we've built an app that's hooked up to Firebase. It's got authentication. You can create an account with your email and log in. Then we built a form where you can create a basketball court and save it to our Firebase database. Now we're building a map display for it. As you can see, we're really just setting the bar low today. Not ambitious at all. (Laughter) But yeah, this is blowing my mind a little bit. Okay. So now you've got the search in place. We've got the -- and I think you said you were doing this as a place holder. The next part I'm excited to see is how are you pulling the courts into the map?
Abel: Cool. Yep, that was exactly going to be my next step. So the way the map works, there's a couple things. There's markers, and this is what I plan to use. You can set the markers to come from Firestore.
Jason: Oh, my goodness.
Abel: I believe we can set this to the current user's location. Okay. I think there's a way of setting this up as the current user. We recently added this, so I don't remember how to do that. We'll just set it to the default here.
Jason: Okay.
Abel: You can say I want the single marker to show up on the map, or I want multiple markers. This is just showing you a demo. It's not loading. Until you run it, it won't. Now you can send multiple markers. This is where the magic happens. You can just say I'm going to do a query here. What I'll do is on the map, the way you get data -- we've seen how to add data from Firestore. The way you query Firestore is through these data icons here on a component. So you can create a request here that says give me -- I believe I can say query collection, courts.
Jason: Right.
Abel: And it should just give me all the courts. Now, I'm hesitating because I'm trying to see -- courts records. I think that's it. So type, list of document. I hit save here. I believe that's it. I don't know 100% how it works. It's kind of magic for me too. The way it knows is it has location. So any record that has a location you can associate with the map. It will just use that record's location to set the marker's lat/long basically. So I've done a query on the map. I said give me all the courts. And if we do Algolia search, then you need to do it differently. You have to get the records from there. We'll start simple, just query all the courts and mark a document, set it from the court records, and you're done. Coding this would take some time. And that's it.
Jason: Okay. So can we go add some courts and see them show up now? We built the whole app?
Abel: Yeah. This is being really slow. I'll actually download the code so I can bring it up in Visual Studio Code and run it in an emulator.
Jason: Yeah, let's do it.
Abel: Then we'll see on Firestore. I'm going to check my Slack real quick. Ah, there we go. I was starting to get worried.
Jason: It's the curse of the demo. Everything goes just a little bit slower than you want it to.
Abel: Yeah, because this not showing up had me worried. But it looks like it downloaded fine. Once we have the downloaded code, we can debug it if we need to. So it's a zip file. Basically a Flutter package. What I'll do is just open this up in VS Code. I'll move this over for folks who can't see my other screen. Yes, I trust the authors. That's wonderful. Cool. So this is the layout of a Flutter package. Basically, the unzipped file contains these different directories here. If there were any assets, they would be in here. This is all the code. Maybe I should have opened it up in Finder, actually, so I can show you what it looks like. So yeah, there's a read me that tells you, you need to run this store if you have a Firestore configured, et cetera. These are all the different pages. A lot of stuff is red here. In Flutter, you need to pull all the packages. For us specifically, when we do a Firestore connection, there's a schema that sends the JSON back. I'll run those commands now.
Jason: Can you give it a couple command pluses to make that just a bit bigger?
Abel: Yeah. Oh, opposite way. How is that?
Jason: Chat, give us a plus one if you need more. Otherwise, we'll roll.
Abel: So this is the command.
Jason: They're asking for one more.
Abel: Okay. There we go. I can do one more.
Jason: So I think we're in good shape here. So now we've got the code.
Abel: Now it's just running the build commands. In the meantime, let me open up -- so this is the entry point to the app. As you can see, there's the Firebase user initially. This is the part that navigates you to the homepage versus login page based on whether you're logged in. If you're logged in, it takes you to the homepage. Otherwise, it takes you to the login page. The thesis to FlutterFlow is we want to make it easy to do a lot of common things inside of FlutterFlow, but we still think code is the most expressive. So we don't want to limit the ability for someone to take over and add any modifications. But to be able to do that, you need to generate really clean, readable code.
Jason: Absolutely.
Abel: So basically, the way we write code internally, that's -- we kind of templatize that and make it ready to ship. So this is how that works, the login flow. The homepage is where we have the search, like the text field. Oh, we never added an action on the floating action button, it looks like.
Jason: Oh, whoops.
Abel: Looks like the floating action button is not going to navigate. Oh, okay. The floating action button doesn't have an action, but the icon button has an action. So when the icon button is pressed, navigator.push. So it actually pushes the court widget to the navigator. This is the very standard UI code for Flutter. You have the scaffold floating action button. The colors, et cetera. It's all in Dart. Hey, I'm on the livestream. That's Alex, my co-founder, who just walked in.
Jason: So we know who to ask if this map thing gives us any trouble.
Abel: Exactly. A little bit of a blooper moment. It looks like everything is set up. I don't see any errors. I think we're ready to go. We can take a second and just look at what's happening here. We had a query on the map, right, to pull all the data in. So you can see where that's happening. Before this Google Map was returned, we have the stream builder, which queries the courts record. You expect the list of courts record. When that data is ready, it will automatically load the map. For each courts record, it maps it as a marker, and there's a location there. When I'm looking at the code, it looks correct to me. We'll try to run it now and see what happens. Let's select a device. I should really upgrade my emulator.
Jason: One thing that I'm liking about looking at this code is it doesn't look too far removed from anything I've seen before. I can probably thank TypeScript for exposing us to the list court records. That type of annotation is familiar. As a JavaScript developer looking at this, it looks different but not so different that I can't figure out what's going on. That's kind of a nice feeling. Looks like you froze a little bit on us. Oh, uh-oh. Oh, dang. Did we -- oh, no. Okay. Let's just hold on while Abel gets reconnected here. While that's happening, I'm just going to drop some links. Everybody go check out -- here we go with the please hold. Nicely done, everybody. I love that everybody did that in concert. That was excellent. Excellent, excellent. Let's see if it's going to play these back to back. That's going to be a lot, if it does it. Is it going to play three in a row? It's not. Oh, good. I was a little worried. Oh, no, it is. Dang it. Yeah, let's see. Checking to see. Right now he's attempting to reconnect. I have him in chat. Don't have any video or audio. Yeah, please don't queue them, though. I thought I set it up to not queue. Apparently I did not. That's what I get. Oh, no. Nobody give Bobby Tables information. That's really what I -- that's the take home from here. Dart Vader. Boo! (Laughter) I think I did rate limit, but I don't know what the limit on that is, like if it's a three-second rate limit or five-second rate limit. Let's see here. So now Abel is still out. Should get him back in a second, hopefully. In the meantime, yeah, let's just recap a little bit because of how freaking cool this is. So this visual builder let us -- so Abel set up a Firebase app but just set it up, didn't create anything. Just got the credentials. Then logged into FlutterFlow and added API keys for his Firebase app and for Google Maps. From there, we drag and dropped the login, drag and dropped the homepage, and we drag and dropped the add a record page, all of which we were able to just connect in a couple clicks to get us a full working app. Like a three-page app. We built the whole dang thing in like 30 minutes or so. It was really, really fantastic how quickly we were able to get that done. I think, if we can get Abel back here, we're going to see it just work, where we'll be able to, you know, open it in the emulator, log into the app. Or we'll create an account, log in, add a court, and we should see that court pop on the map as we do it. If we don't get Abel back, which I still don't see him yet -- so fingers crossed -- then we'll, you know, tweet about it or something. In the meantime, what's up, chat? How y'all doing? If you're in the U.S. or otherwise had a long weekend, how was your long weekend? Mine was good. I played a game that is called, depending on what part of the United States you're from, it's either called cornhole or bags. Let me pull this up. So it's a game that's sort of like horseshoes. You've got a board. There's a hole in the middle. Then you throw bean bags from about 25 feet away and try to get a bean bag either on the board or in the hole. You get one point on the board, three in the hole. It was super fun. Like, we had a lot of fun. My partner Marisa bought an official Cornhole Association of America set. Haven't played until you've seen these. Oh, oh, we got Abel back! What up?
Abel: Oh, man. This is so frustrating. My Wi-Fi dropped. Sorry about that.
Jason: No worries. We were talking about cornhole.
Abel: Oh, perfect.
Jason: Let me link these to the chat, but let's go ahead and consider these. I think bags is an east coast name. I heard that's what people in Philly and Jersey and stuff call it. Oh, Wisconsin calls it bags. But yeah, my family is in like Illinois. They're all bags players. But yeah, Abel, feel free to reshare. I'm just filling time at this point.
Abel: Yeah, no problem. Sorry about that.
Jason: No worries.
Abel: Cool. I'm back here.
Jason: All right. Here we go.
Abel: My computer was being so slow, so I just killed the emulator also. I've closed most of my Chrome tabs. I need to get a machine with more RAM.
Jason: Man, the eternal race of I have a thing I want to do, and then they release an app that uses even more RAM.
Abel: Yeah.
Jason: Okay, I saw the emulator for a second, then it flashed away. Oh, no, is the emulator the problem? Yeah, you're super choppy. I have a feeling the emulator is what's sabotaging us here. Wait, are you back? Okay, you're back, I think. I'm seeing -- Curt in the chat is saying he tried to use the emulator, and it destroyed his setup. It might just be that we can't use an emulator while streaming. Yeah, Eco makes a good point in the chat. If you want to spend me the code, I can attempt to run it in my emulator.
Abel: Yeah, that's a great point. I stopped sharing. Let me continue sharing my screen. I killed the emulator. Let me send you the file. Do you want me to send it as a zip?
Jason: Yeah, if you just want to maybe email it to me.
Abel: I've also started the build on the web.
Jason: Now we'll see a race between how quickly I can figure out an emulator and how quickly FlutterFlow can get the app built. I have a suspicion that FlutterFlow is going to win. Oh, thank you, Curt, for the subs. Everyone who just got a gift sub, welcome to the boop crew. Use the heck out of those new emotes you've got. I did, for anybody who's just joined, add a new one. We've got a little compooper friend. So, welcome. Welcome, everyone. Enjoy dropping those boops. Let's see. I'm looking for an email. I don't see one yet.
Abel: I think I have a setting.
Jason: Oh, like the undo. I do love undo in email. We did lose the bucket emote for that. But I feel like nobody was really using the bucket, which is why I pulled it out. The bucket wasn't getting the screen time that I felt like it deserved. So we're going to see if the little compooper gets a little more. And let's see here. Still not seeing it.
Abel: I sent it to Jason@Lengstorf.
Jason: Yep, that's what I got open here.
Abel: Hopefully it doesn't put it in spam automatically.
Jason: Definitely shouldn't, but let me look in my spam folder. Yeah, it definitely got spammed. Not spam.
Abel: Also, if you check out the screen, there's a small issue with the button here. We had something here. So the build actually finished.
Jason: Perfect.
Abel: Sorry to say, Jason. Not your fault.
Jason: FlutterFlow wins. (Laughter)
Abel: I slowed down the email. So I'm just going to create -- you know, it doesn't necessarily verify the email here, I think, but I'll use my FlutterFlow email here. I'll just create a password. I'll hit sign up, which will create an account. I'll go to Firestore here. Hopefully you see the user show up here when I sign up. Cool. So if I go to Firebase and refresh this page, I should see -- sweet, there we go.
Jason: Cool, okay.
Abel: I have an identifier. Also, the thing Firebase doesn't do for you automatically is it doesn't create the Firestore collection for you automatically. You have the email. My created time is all set up here. So user collection is set up properly, and we have this -- it's actually not going to search anything, but we have a map to interact with.
Jason: That's so cool that you can -- yeah.
Abel: Bridgetown, all right. I like that. Oh, not good. So something broke here. Okay. Unfortunately, because it's in run mode, one thing that happens is -- so I can tell there's some issue with the UI. Maybe the zip file could help us debug what's happening here.
Jason: Sure.
Abel: My guess is one of the components is configured incorrectly. I can also have Visual Studio Code up here to try to debug it live. If something is broken on the screen, it will still load this gray widget and the navigation works. But there's something wrong with our add court page. I suspect -- we just added this button. So I suspect it's this. Of course f you add a button without a location, it's not really going to make sense. But let's look at the code and see what's happening. I've sent you the zip file, if you want to open it up.
Jason: Sure, yeah. Let's peek in here. So let me get into my GitHub folder here. Actually, it's not in my GitHub folder. I'm going to go to download courts, I think it was. So I'm looking in the lib folder. Then there was add court. So we have the add court widget.
Abel: So I can't see your screen. Let me stop sharing.
Jason: Yeah, let me share with you.
Abel: And this is how it typically works.
Jason: Now I just got to put your video back in the right place. Good, there we go. So now we should be looking at -- yes. We're all looking at the right thing. We don't have the never-ending loop. I think we're in good shape here. So let's peek in here. We're up at the top. We have the court widget. We're looking at a stateful widget. If I start looking around, I'm going to find my app bar, background color, floating action button. Here's our safe area. Column, padding, text form field. We've got another text form field. Then we should see one more. Another text form field. Now we should see our Google input, place picker.
Abel: Yeah, and just looking at the code, everything looks fine. So why don't we try to run it. So you need to open up readme.md. There's two commands there you have to run. The flutter pub get.
Jason: Okay. I don't think I have Flutter. So how do I --
Abel: So there's a Flutter getting started. It should be quick to set up.
Jason: Okay. So let's get started. MacOS. System requirements, get the Flutter SDK.
Abel: Yeah, that's the main part, the Flutter SDK.
Jason: This will give me the command, right?
Abel: Yeah.
Jason: Okay. Let's do it.
Abel: I'll also try to debug here. By looking at the code, I couldn't tell what's wrong. I'll do one more shot at bringing up my emulator.
Jason: Okay. So we're opening up Flutter. Almost. There we go. So this just opened over here. Wait, what do I do? I unzip. Oh, I just got to move this somewhere. Drag the file to the desired location.
Abel: Yeah, you can leave it in downloads. That's probably fine. Depends on how organized you are.
Jason: How organized I am? (Laughter)
Abel: How organized you want to be.
Jason: Yeah, all right. So let's do this. Let's add the Flutter tool to my path by doing export path, equals path. Then -- no, that's not right. It's going to be like downloads, flutter, bin, I think. I think I screwed something up. Maybe I can't do -- let's go to downloads. Okay. Now let's try that again. Export, which I think I can do this way now. There it is. Now we should be able to run the command from the readme, which means I run flutter pub get. Nope, got to move into the actual thing. Let's move into courts. Try again. Things are happening.
Abel: Nice.
Jason: Make that a little bit bigger. All right. So that worked.
Abel: Do you happen to have an Android --
Jason: I think so. That's a great question.
Abel: In Visual Studio Code, if you do command shift P, is there a select device there?
Jason: Crap. No, I don't think I have it. No.
Abel: That's fine. You should be able to do Flutter 1 after you've created your emulator. Also, it looks like it's starting to run. I'm trying to build it, too. It hasn't brought down my machine so far. This is kind of why. This is exactly why we're trying to do FlutterFlow. That whole process of having the emulator and all the different emulators you might need and setting them up and that stuff is a lot.
Jason: Yeah, it's a lot. Although, I will say this is more pleasant than, like, some of the other emulator setups I've had to do. Especially when you're working with compiled stuff. Then you have a fake a different operating system. Not having to, like, run a dual boot of another operating system or something and just being able to actually do an emulator like this.
Abel: I think it's actually trying to run it in Chrome. I think you want to control C that. It's built for mobile now. I think you want to control C that and make sure you have an emulator open. It will automatically select it, I believe.
Jason: Is there a command for that? I'm worried that I don't --
Abel: If you do Flutter devices.
Jason: Flutter devices?
Abel: Have you opened the emulator?
Jason: Well, I'm actually wondering if I have it. Let's see fiscal cliff one. I only have Chrome.
Abel: Okay, cool. Here, let me try sharing my screen. I have it up now, and it hasn't brought down my machine.
Jason: All right. Let's see what happens.
Abel: I haven't actually debugged the issue yet, but at least it's there. It's on the login page. I haven't tried to sign in. It's just connected to the service here. I believe I had a very secure password here. Let me see. Yeah, there we go. So it's signed in.
Jason: All right.
Abel: Cool. I think the reason -- when it shares Wi-Fi --
Jason: It gets brutal, yeah.
Abel: There's Google Maps. Now plus here. So I think what happened is we just added this feature, the search. We have to compile it to run --
Jason: Don't you do it. Don't you do it.
Abel: I'm not going to try.
Jason: Here we go. It bound up on us for a second. Let's try it. Let's give it a shot.
Abel: Let's say Palo Alto High School has some games. Saturdays. It's running a little slow. I'm not going to set an image URL. It's not going to show up anyway. Search places. Sweet. Palo Alto High School. Oh, moment of truth here. It's being slow. I'm not seeing any errors.
Jason: I'm honestly going to blame this on the fact we're trying to -- we're on a Zoom screen sharing, streaming, and running an emulator. That's a lot. (Laughter) So I don't think this is going to come down to your tech as much as it's going to come down to the fact we're really pushing the bandwidth limits here.
Abel: Yeah, I will say it's definitely an early product, FlutterFlow itself. We're less than nine months -- we're about nine months old now.
Jason: Oh, dang.
Abel: Yeah, so we definitely -- you know, we're moving fast and trying to improve.
Jason: Absolutely.
Abel: Our users have been pretty patient is what I'll say. We launch features as quickly as possible and improve them. So I think I'm looking at a message here. Error occurred.
Jason: And that's from searching or because we didn't get enough content in there? Like is it a three-character search or something?
Abel: This is what's happening. Places auto complete response. Let's try to get this.
Jason: Oh, it hot reloads? That's nice.
Abel: If I search again, I should see -- let me try to search again. I should see a better message in terms of why it failed to search. I'm just going to hard restart. Maybe it was a failed search in advanced state. I'll have to go to the add button again. I'll go directly to search places. This API project is not authorized to use -- all right. So this is just a configuration error. I think, hopefully -- I know we're running low on time. It's going to be exciting.
Jason: We're going to get this working.
Abel: Yeah, so I'll go into Google console. My computer is on the brink here. It's fighting through. We'll go to Google Maps. Let's make sure it's enabled. Not authorized to use this API. Oh, we haven't been to Places API. Make sure we enable that, I believe.
Jason: This has always been my biggest sticking point with Google's API. Every API has a different name, and it's always kind of confusing to understand what buttons I'm supposed to click. I always feel like I'm adding too much access. But this is -- so, okay, once we've enabled this, theoretically speaking, you should just be able to type again and it should work, right?
Abel: I believe so. Yeah, that's my understanding.
Jason: All right. Let's give it a shot. Let's see what happens.
Abel: Yeah, let's see what happens. I'm excited, actually. This is the first time I'm running through this flow of using this widget. We actually built it yesterday.
Jason: I love it. Test in production.
Abel: Just full transparency here. All right. Let's search for places. There we go. Nice.
Jason: All right. Oh, there's high school. I see it.
Abel: Selected that. All right. So this is a little bit of an overflow happening here. That's fine. Basically what's happening is it uses the name of what you selected. So if I actually go to -- and we can fix it with hot reload here. There's a selected place, it shows that. Otherwise, it says search places. Maybe you can just say place selected or something. So it doesn't overflow. There we go. Instead of showing the name.
Jason: Gotcha.
Abel: Saturdays at noon. Of course, when you click it, nothing is going to happen for now. When I hit okay, ideally this will create a record. Nice, all right.
Jason: Got a record.
Abel: Let's go to console here and refresh. So there should be a courts. Nice. There's a courts. Here we go. Saturdays at noon. And this should match up to Palo Alto high school's lat/long. Since we don't have Algolia set up -- I definitely overpromised. We'll scroll up here.
Jason: Got it, got it, got it. Okay.
Abel: Oh there, we go.
Jason: Look at it go! Oh, my god. Okay. So I've done map stuff before, and every time I've done it, I always leave like a little beatdown by just like how many hoops I feel like I have to jump through. So I feel like this is incredible, that, you know, the biggest debugging we had to do was making sure the right API was enabled. We just built a whole -- I mean, that's a whole ass app. We could ship this. It's maybe not the prettiest thing in the world. We could add more to it, but that does what we want. We can literally say, I'm a user, I want to save a list of basketball courts. I want to see where they are. And it opens up and shows me courts. Like, that is so cool. Also, just in the nick of time because we just ran out of time. So anything you wanted to show before we start doing the offload?
Abel: Yeah, so maybe I'll just kind of quickly say -- I'm not going to do anything, but one thing -- ideally what you want to do is when this is clicked, maybe show something, right. That's also very easy to do. You basically can say -- I believe this is an action here on marker tap. Let's say you want -- this is not going to work. I'll just say navigate to page. I'm not going to build this, but I'll show marker detail page. You can do this as a component that shows up from the bottom as a bottom sheet. If you create a marker detail page, you can define a parameter here. That's a court. So when you tap a record, you want it to load the details. Like maybe comments or whatever people have added. The way you do that would be on top, navigate to page, marker detail page, and you can pass in a parameter. The court should be the marker record. And this is type checked. It knows marker record is a court because that's what you've associated here with marker documents. So now basically you just need to go and set up the view for what a court looks like with the image and all that stuff. Then it will just work. Now when you tap this, it navigates you to a different page that shows you more details about the court. I just wanted to show that. Yeah, thanks so much for bearing through this. We somehow made it work.
Jason: Oh, I mean, like I said, if this is the biggest problem we got to overcome, like, dang. That's really good work that y'all are doing over there. So let me switch over here to show some links on the way out. Make sure that y'all go and check out Abel on Twitter. Let me actually find where I'm supposed to be putting this link. Here it is. This is the chat. There we go. Make sure you go and do that. Follow. We have been using FlutterFlow today. If you are interested in doing some build once, deploy everywhere work visually, you can check out FlutterFlow today. And while you're looking at things on the internet, remember this episode, like all episodes, has been live captioned. We've had Rachel with us all day today from White Coat Captioning. Thank you for being here. That is made possible by our sponsors, Netlify, Fauna, Auth0, and Hasura, all kicking in to make this show more accessible to more people. With that, make sure you go and check out our schedule. We've got some really, really exciting stuff coming up. We are now booked out into October, folks. It's going to be a great time. So later this week we're going to learn Blitz.js. We're going to do a web performance audit with Tim, one of the leading performance experts in the world. That's going to be fun. We're going to learn how to build a CLI in Rust. We're going to do JamStack. We're going to do realizetime with Ably. I'm real excited about that one. We're going to learn about right-to-left support and so much more that hasn't been added yet. Check out that schedule. There's so, so much good stuff going on. You can add the Google Calendar link to make sure that you get updated for what's happening when and with links to watch. So y'all, thank you so much. Abel, thank you so much for hanging out today. I really appreciate you taking the time to teach us and show us FlutterFlow. It's been -- wow, what did I just do? It's been ab absolute blast and a pleasure to learn from you today.
Abel: Yeah, of course. My pleasure. It's been great to be here today. Thanks for having me.
Jason: All right, y'all. With that, chat, stay tuned. We're going to find somebody to raid. We'll see you next time.
Abel: Bye, y'all.
Learn With Jason is made possible by our sponsors: