 Welcome to the presentation of the one with the braid. You might know her from doing stuff with Jungtakt and trains and also Flutter and Matrix. And she's going to give you a small presentation about Flutter and how you can do stuff with it. Yeah, exactly. That's what I intended to do for the next 45 to 50 minutes. Yeah, welcome to my talk about performing cross-platform development using Flutter. A brief introduction of me. I'm the one with the braid. Jeb Den Miet-Fletning in Danish and I'm a software developer doing Dart and Flutter in healthcare industries, especially with the focus on Matrix. I don't mean the film, but the chat protocol. Apart from that, I'm interested in previous D-Linux risk computers. And yeah, I started using Flutter back in 2018 when it was publicly released and here in 2019 I started doing it professionally. And yeah, that's what I still do. Had a Flutter. What is Flutter? It's a bit complicated. Flutter is lots and nothing. Flutter is a software development kit based on the Dart platform. Dart is a programming language. I will get you to a quick introduction into the Dart bit later. Moreover, Flutter can be the Flutter engine. The engine is what finally renders stuff on mobile devices. Flutter can be the foundation library containing lots of classes and APIs implemented in Dart. Flutter features lots of design-specific UI components for easy software development. And Flutter features lots of DevTools for easy development. This is everything Flutter can be. Now I'm trying to convince you to use it in case you don't use it yet. Why Dart and Flutter? Flutter compiles to, unlike JavaScript, where we often compare Flutter with JavaScript since these are the most popular cross-platform mobile frameworks. Flutter compiles to native code. If you compile any Dart, you get a native Linux binary. You get your native Windows binary. You get your native code on iOS. You get your native code on Android. And you get your JavaScript if you compile it as a web app. One advantage, of course, but that's common in any kind of cross-platform development. You have your single code base. That saves you half of the number of mobile developers. Also lots of development time. And Flutter is super good at adopting to the platform. If you write a single application, it basically looks same on any device. But it adapts in small UI enhancements. For example, in order to meet the habits of the platform it runs on, which is, in my opinion, a very good approach. So a very basic example if you have an empty app, material design on Android, it uses another app bar than on iOS in order to meet the habits of what users are known from their platforms. And Flutter, and that's the most important thing. It was designed about around development experience. Not main user experience. Well, that's what the developers at the end do. But the framework itself was developed for developer experience. That means they have a team about the developers of Flutter, they have a team about caring about developer experience. So asking nothing but the question, how can we make development more intuitive, easier? Yeah, and that's a very good idea. And it finally helps you out getting rid of legacy development patterns. Brief history of Dart and Flutter. Dart was initially released already in 2011. So it's a pretty modern language. It's aimed to replace JavaScript with a type safe language. And let me just summarize, it absolutely failed. They aimed to bundle Dart as language in Chrome, but since, hey, a language supported by a single browser, well, back in these times, Chrome did not have a market share of 95% yet. So it absolutely failed. But some approaches were good. Having a typed language. But it was only semi-ready. They forgot many APIs to implement. Back in these times, Dart did not have a proper type system yet. So it absolutely failed. So the developers of Flutter, which is Google, looked for a new use for this language, which is actually a very nice language. And they found a use, mobile development. That's why they announced Flutter in 2015. And since 2015, back in these times, Flutter was an Android only SDK aiming to render at 120 frames per second. But with time going on, Flutter was adding more and more platforms from Android, iOS, than later. First preview of Linux desktop, web support, Mac desktop, and Windows desktop, was released. All of them are counted as stable nowadays, though it's discussable, in my opinion. And yes, so nowadays Flutter is cross-platform development kit, which is really cross-platform. The only common, okay, common is discussable, but the only common platform it does not run on is BSD, which is a pity because I'm a huge free BSD fan. Yeah, I'm not sure whether you are already familiar with Flutter or not, whether you are experts in Flutter or beginners. So a quick heads up about Flutter. Yeah, well, we already mentioned Flutter is a UI framework. A cross-platform UI framework aiming to help you creating simple and easy, or having a simple and easy way to create mobile platforms. Most important term if you use Flutter is widget. Any UI component is called widget, at least anything the end developers use. And as most UI declarations, whether it may be a programming language or a market language, you have a nested tree. You know it from HTML, you know it from XML, you know it from anything. You have your element nested into elements, having children, and you have a tree structure where you nest your elements. But unlike, for example, an HTML where it stacks, you have widgets in Flutter. And yeah, yeah. And what makes Flutter pretty special is Flutterships, both super basic layouting widgets, doing nothing but layouting, up to super high level widgets for end users, which are kind of, yeah, here's our search bar for, yeah, so you can either use super highly customized development with low-level widgets or super high-level widgets where you basically drop in a few widgets and you have a final app, or at least final MVP. So let's look at some Flutter code. It's the simplest Flutter application I could find. This is an entire app. It says, hello world in Klingon. It's geeks here. I thought Klingon might be liable for you. Yeah, so very important part of Flutter is a run app because that's basically what tells the Flutter entering to start rendering an app. And everything starting from that point is a widget. A text is a widget. We will later look at other widgets on more complex samples. Everything which is provided to Flutter is widgets and they are nested into each other. And how to add interactivity to these simple widgets nested into each other. That's the next point. We have Flutter highly separates into two major kinds of widgets, which is state-less and state-full widgets, similar approaches from likely most other modern UI frameworks. Stateless widgets are immutable rendering objects. They are once created according to their parameters, once laid out, built, and that's it. And they don't have their own state, so nothing can change about them. It's always the parenting element deciding on whether this part of the UI is supposed to change or not. And state-full widget is the exact opposite. It's a mutable modifiable rendering object, which has control flow about anything it contains. As that one decides whether about its children, for example, that one decides whether a button is green, whether it's red, because that one has control logic and can trigger state changes. And unlike, it always consists of two parts, which is once the surrounding class, so which represents the element, so the widget. And once the state itself, the state is mutable, it can trigger changes of the state and is always preserved across rendering, while the widget itself, the state is part of this technically being rebuilt on every build. Yeah, what makes me liking Flutter? And how can we make it performant? Because one of my aims when doing mobile development is don't exhaust resources. I know many people in IT environments have very powerful devices, but they always like to support the most basic devices possible. So if we look at performance on Flutter, use stateless widgets wherever possible. If there's no need to half a state, if it's possible to inherit state, because the last computations we do during build, the more performant it gets, when using stateful widgets, always try to check whether the parts of the widget we want to rebuild, actually rebuild, that's what we have, for example, the update widget method, have dedicated controllers, so try not to nest many states into each other, but try to have a single controlling architecture that might be a pattern like block, providing streams that might be something like hive, which is a database back end, that might be a simple stateful class serving as controller for a particular part of the screen in order to always reduce components having their own states in order to render more efficiently and use keys. Keys is a part of Flutter where you can simply pass over keys, which tells the rendering framework to not rebuild a widget as long as the key is the same, so always use keys whenever possible if you want to prevent parts of the UI from unnecessarily re-rendering. Now I was talking about rendering a lot. Let's get deeper into it. So in order to get deeper into how rendering works in Flutter, we need to have a look at of how Flutter is composed. And here we have more or less visible graphic explaining how the Flutter framework is built. At the top we have the material and Cupertino libraries, if you have ever used Flutter, you likely or if you have ever did mobile development, you've heard about both. Cupertino is simply the UI style used by Apple. Material is the UI style used by Google because these are the most popular mobile platforms to develop for. These are kind of the top-level rendering parts containing high-level widgets with UI components used in these design patterns. And all of them are of course widgets. And widgets itself are rendering objects. And all of this is part of the Flutter framework under the rendering object, so there's lots of noise outside. We have animation, painting gestures. Of course, well, we need to capture gestures. We need to capture a tap event. We need to capture a mouse move. And all of this is shipped together in the Flutter framework written in Dart. As the Flutter foundation, all of this makes the framework serve to what I as a developer see. And all of this builds on the Flutter engine. Flutter engine is developed in C++ plus, not the third plus in the language, but plus JavaScript for use of Flutter web because while C++ nowadays it runs in web using WebAssembly, but back in times Flutter web was developed. That was not that stable yet. So we have an additional engine implementation in JavaScript just to be mentioned for Flutter web. And the engine consists of Skiya, of course, the Dart programming language, and everything which is text because we need accessibility. And in order to be accessible, the underlying operating system needs to know about what's on the screen for screen readers and so on. So that's why text processing is part of the engine which uses operating system APIs. Yeah, Skiya, maybe you know that one. It's a modern rendering library developed by a particular company from the United States having a GS logo, also known as the same developers as Flutter. Yeah, and that's basically the engine. So if we render something, this is the GPU task tree where the engine checks what to rebuild. Spoiler, Flutter tries to reduce the framework itself already tries to reduce unnecessary builds and rendering of parts of the screen as much as possible. If we have, for example, imagine we have a button or a spinner or something that's moving on the screen. So we have our empty amp, a big white scape, and in the center there's this progress indicator, for example, so it's spinning around. Of course, we don't want to repaint the white area all the time. So the Flutter framework checks, okay, which parts did change? That's the part called animation. So when the GPU tells, no, once the GPU tells us, okay, ready for the next frame to be rendered, we check every vSync. The vSync is what the GPU provides to the Flutter engine when it's ready to render a new frame. Then the engine is giving over a vSync indicator to widgets. And according to this indicator, widgets can decide to rebuild once there was a new frame when it's ready to be rendered by GPU. So in case we get that one, all animations, for example, the spinner going from position zero to position one and so on, in order to move, we move that one on. So animation is done. We know the new position of our spinner. Then we build the widget because now, well, we know the spinner is at position one, but what does it mean? We need to, afterwards, we do the build. The widgets indicate there's the build method in case you ever used Flutter before. Well, it's simply that one which is executed. So we build a new widget in order to have available what changes in the widget tree act we actually had afterwards. And that's the next stage. We have the layouting stage. Now that we know which widgets we have, we can now look at how to layout them. Because, well, if you look at constrained boxes, sometimes widgets request more size. Sometimes widgets request less size. Sometimes we have widgets requesting a particular size. Sometimes they just tell us, oh, give me all the space we have. The layouting stages, what's executed afterwards to keep with the example of the spinner, nothing will change that much because the size of the spinner, which it will be the same size, but in case it should change its size because we have a fancy spinner zooming in and zooming out, for example, then we would indicate to the engine, okay, layouting change. We need a new area. Please assign us an area. And afterwards, once the engine computed which part of, or which widget to render where, it's basically doing the paint. And then we're done, and we're skipping over to the next vision and doing this again. So that was a very detailed explanation of how GPU tick works with Flutter. Yeah. And what's the difference between stateful and stateless here? Yeah. A stateless widget simply has its constructor, it's once created and then build. When we have a new v-sync, unless the constructor was modified, we don't build. But once the constructor was modified, we rebuild, independent, not checking anything, we just rebuild it. If the constructor changes, we rebuild it. That's a bit, yeah. Sometimes it's useful. Sometimes it's not. A stateful widget in opposite is once created, and it's then given a state. It's creating a state, and the state decides on everything else. Yeah. And what does it mean to have this state? That's this graphic. We once initialize a state, then we have a dirty build because we have a state that changed, but we did not render it for the end users yet. Because that's why we then try to build it. Then our widget is clean. So then we're more or less done. Then a stateful widget, as that one is not immutable, it's a mutable thing, it could, for example, call a set state. For example, if we have a listener on some button and we tell it, okay, once I press it, the button should change its color, this tab event would trigger a set state. It changes its state. It sets the variable color to light pink, for example, then we have a pink button, but we did not render again. So we are in dirty state again. Once up to the time, the next v-sync is provided by the GPU, and we build again. Other option is we got a config update or a widget update. That's, for example, if the layout constraints surrounding the widgets changed. For example, if the neighboring widget just told me, oh, I now consume twice the size, we need to shrink our widget. That's nothing the widget itself decides. So there's also the update config handler, also the did update widget handler, which basically calls that one afterwards, also resulting in, okay, well, we did not change anything about our widget state, but the surrounding changed. So, yeah, now we have these and those constraints we need to rebuild. And the last part of the widget life cycle is dispose. Once the Flutter engine decides, okay, this widget is now no longer used. It's off the screen or whatever the dispose method is called and everything is garbage collected. And for example, if you have streams listening to, you can dispose them to, yeah, if you want to still optimize the performance now that we know how this rendering works in particular, we have, when you mentioned them, we have some key features. The most important key feature are keys. Keys prevent stateful widgets from randomly rebuilding. Same for stateless widgets, but they're less relevant. For example, if we have a list updated with, or if we have a list and we have a search bar in it. Now the search input changes. So, of course, if we have a list containing 20 items and we look for item number five, we enter a five in the search bar and we get back to item five. Then technically, the library, the UI library would no longer know, okay, which of these previous list ties has it been? Which part do I need to re-render? When we provided a key, hey, you are list tile number five. And once this key, list tile number five, is provided anywhere else, you know, you can reuse what you had at the previous appearance of key number five. So keys assign or basically tell widgets, okay, you have previously been the last one having this key, and so we prevent rebuilds and that highly improves performance. Yeah, especially this example is, when we search, it's usually a stream builder, and now we often have cases where we accidentally rebuild the entire UI because we don't properly refactor already rendered widgets. Especially useful, by the way, for animations because many high level animations, animation libraries rely on keys to be same or different, indicating on whether to rebuild animations or not just as a note aside. And, well, let me grab some water, I was talking for too long. Flutter uses, generally, not uses lots of streams. We often build your eyes against streams. Streams may be provided by the state management, which might be blocked, that might be provided by the database solution, which might be, for example, Hive. We generally often build things against streams. So something changing and triggering UI rebuilds. Very common way of displaying stuff is we get a list of anything from some server, from some database backend, from whatsoever, and yeah, we want to list it. That might be a chat list. That might be whatever. The list of the upcoming talks. So we have our stream providing basically the rendering library. Here, these are the items. Once anything changes, rebuild. Please never do that. If you have a list view building against a stream, believe me, everything will rebuild once there's a new item in the stream, which is awful. Use keys, then it only rebuilds the particular changing parts of the list, or use animated lists or animated sliver delegates where you don't indicate, okay, these are my new 100 items where it was 99 items before, but you just indicate, okay, please insert that item at position number one, which is obviously way more performant than rendering 100 items. Animations, yeah. How to animate? That's one of my favorite topics about Flutter Flutter's genius at animations. Often when we talk about mobile development, we have animations indicating some things, indicating some corporate identity of something, maybe the mascot jumping around or whatsoever. Most easiest way is always, yeah, we have our video or our GIF or our animated PNG and we just render it. That's awfully unperformant because it does not sync with the v-sync we talked about earlier. It simply tries to rebuild and that's awful shit. We can either use shaders, native in Flutter. Shaders are pretty available, especially if you have more or less not jumping in figure, but more or less a background or something like that. Shader is way more efficient. Use animations package. There's a package provided by the Dart language called animations that has high level implementations of many animations, many common patterns of animations which might be page transitions or whatsoever. Again, use keys because animations are super heavy and we do not want to initialize and analyze the keyframes of our GIF if we need to render a GIF every time we change anything on the screen. Also, especially as a replacement for GIFs, just doing some basic animation. There's a super fancy animation format called Lottie doing high level rendering. Yeah, it's super efficient in Flutter. I recently had implemented it somewhere where I sometimes had 50, 60 Lottie animations running on the screen and it was still super-performant and even not that powerful devices. Now these following things only apply if you deploy something for web. Yeah, Flutter supports the referred libraries. So imagine if you compile your application with everything, all the logic you have in, well, first of all, it's just your executable, but we can defer it. Deferred libraries are available in Dart since ages and enable you to split up the compile on the transpiled JavaScript. Again, if you run as a web application into several parts, computing based on which parts are needed first. You can, for example, first have your splash screen, then defer the most other parts of the application. And yeah, that's obviously way more, it reduces the initial load and increases the initial rendering, the first colorful paint you get if you only load a few kilobytes instead of your five megabytes over bloated web application because it contains that much stuff. Also use browser optimized CanvasKit, Flutter renders in web using CanvasKit, which is the web version of Skiya. Since the recent release of CanvasKit, I forgot the version number, it was 0.30 something, they have browser optimized CanvasKit. So they have dedicated versions compiled for Firefox and for Chromium because there are not many other browser engines left. And by providing browser specifically compiled versions of CanvasKit, I don't know how, but they reach at reducing the size of the CanvasKit library by several megabytes. Yeah, also a very, it's unfortunately a very uncommon pattern in Dart web development is web workers. Web workers are always your solution in general in web development. If you have heavy task to compute, JavaScript itself is simply not made for computation and multi-threading, the only solution to do this are workers. Especially web workers are useful in that case. There are also some other types of workers, but these ones are the ones relevant if you have a heavy computation because otherwise you will simply see your eye freezing. While you, for example, encrypt your five megabytes file, it will slow down your application if you encrypt your 200 megabytes file. The user might be a bit disturbed because the web app won't render anything for the next five minutes about storage. Flutter has super fancy storage solutions and let me take one thing first, JSON is not a storage. At least not if you want to process a bit more data than my preferences, whether I'm in light mode or in dark mode. JSON is not a storage. Let me present some nice, Flutter-typic storage solutions. The first one, the boring one, is SQLite. You know that one from anywhere. SQLite is a database solution as SQL might indicate it's SQL-based. It's very performant if we use it on IO platforms, so anything apart from web, because I don't know whether any one of you ever tried to use SQLite on the web. It works using WebAssembly, but please don't do it because it tries to store stuff in indexed DB. It should not store an indexed DB. That's why SQLite is super efficient and performant via foreign function interface if we run a mobile application or desktop application, but it's awful if we use it on the web. Two very Flutter-typic data-based solutions are Hive and ESA, by the way, written by the same friendly person, a developer from Munich. Hive is more or less the Flutter-native one, while ESA is the rusty one and not rusty meaning of old, but rusty in the meaning of it's written in Rust. Someone said, hey, let's rewrite Hive in Rust or something. I don't know. About Hive, Hive is a key value store, and it highly integrates into Flutter. It's listenable in UI, so you have storage boxes of Hive are value-listenables you can listen on in UI. You have type adapters enabling you to store runtime instances of your Dart code directly in the database, which is very convenient for developer experience, especially. With more developer experience, you hopefully also can create better user experience. ESA is aiming to be a replacement for Hive because Hive used to have performance issues in web. That became now way better, but ESA project started way longer time before and has a bit different approach. It's query-based, written in Rust by foreign functions. Since the backend is written in Rust, it's super performant, especially if we run on native platforms, so everything apart from web again. On web it's also a pretty performant. It's loaded via Wasm, but if you process lots of data in web, it turns out it's a bit less performant than Hive. These, as ESA and Hive, are the ways to go with Flutter. One thing to be mentioned, Hive enables you to fully encrypt the database using either predefined AES ciphers or fancy ciphers you may develop yourself. Please don't do that. Please use existing ciphers for some obvious reasons. If you don't need a huge database to store your 500 megabytes of user data, okay, we still have JSON. We have shared preferences. You can drop your JSON in in order to know whether the user wants light or dark mode. Yeah, now a bit short overview about the ways of animation we have in Flutter. First of all, the question, why should we animate? I was talking about animations a lot because I like animations. Why animations? Okay, it's not the only reason I like animations. Here you saw some fancy videos which played very rapidly. Animations are useful for user experience in many ways. Let me try to, well, let me try to replay these videos. That can be clicked on the other one, but fine. Yeah, it can be used for user education. That's that one. For example, here we tap. It does nothing. And instead of just doing nothing, we provide an animation telling the user, okay, this is what you should actually do. That's one way of animation. The other way of animation might be that one. We click on a message in a mail folder, and it opens a pop-up. And instead of just navigating to a new page, we transition the message into the pop-up so that we know, okay, what we now see is the detail view of this message. Over there, you can see it. I don't think I need to replay it. It's a loading spinner, also useful. Show a loading spinner instead of a white screen. That tells the user, okay, something is happening. Please wait. Also, last but not least, this one indicates success or failure. If we start dragging this thing, we might like to know whether this is actually possible. And that's why in this example, the other cards move around in order to confirm the user, yeah, what the action you performed is possible, is allowed in this case. Yeah, and all of this is super easily doable with Flutter. I will show you the Flutter native-native-native low-level way of animating, and then I will show you the more convenient high-level animations. Implicit animation, that's what the native-native-native-flutter way is called. So an animation, wherever we do it, always consists of some progress value we have, some duration, and some curve to animate against. It might be a box resizing according to some pretty fine stuff. It might be a super complex rabbit running around on the screen, according to some value we have. That would be pretty complex to animate, but it's possible. So using these three values in Flutter, we have, and anywhere actually, we can perform UI animations about curves. Flutter has some, no, not some many pretty fine curves, but also you can find any mathematical function to use curves. This very short example of an implicit animation, we only provide it a given value, depending on some if statement, depending on whether whatsoever is currently selected, or not the width or the height should be 200 and 100. We give it a duration, we give it a curve, and that's everything it does. Flutter is very good at high-level way of animating. Once this selected variable changes from false to true, this container, it's an animated container, the Flutter framework will render and animate to the next state. So Flutter is very user-friendly at this point. Oops, sorry, screen is a bit small. Also very common way of animating is using animated builders. It's for more complex stuff like animating than animating just a box, but it's still super easy and convenient to use. This is a very tiny sample of how the use of an animation controller and an animated builder might look like. We give the animation, we initialize an animation controller, give it a duration to animate, and here you see the v-sync again, we were talking about earlier. The v-sync is used there to indicate the animation controller went to reanimate, went to trigger the next frame, and later we use this animation controller in an animated builder where we pass it a child. I will talk about that one later. In the builder we use the value of the controller again and the child. The child you see here is what we define here, because I already told you, we always want to reduce rendering. If we pre-define a constant child, which does not change at any part of the animation, we can just provide this child and instead of building everything again in this build method, we define the child here, it's passed to our build function later on, and the only thing rebuilding is that in this case rotation transformation, and that saves us lots of GPU power because the child may be a super computation intensive widget. No worries, we're almost done. Last part of Flattery Native Animations, to be mentioned, are hero animations. In case you ever used Flattery, believe me, you already used it, but you did not know. Heroes animations are given a tag and it's super fancy. We give a particular widget on the screen a tag, we navigate to a new screen, we provide the same tag to some other widget, and whatever widgets they are, they transform into each other, which is super fancy for screen navigations, for example, where you have a button moving around, a small card being big afterwards, it's super convenient, and it's the most low-code way to use animations because you only surround any widget by a hero widget, give it a tag, for example, my card, and then some counter, and we do not need to modify anything else, we don't have a controller, we need to start, we don't need to listen to some voicing, Flatter Engine does it. How does it look like? Very easy. Here we have a container containing whatsoever with a hero tag set, afterwards on the next page we navigate to, we have some other thing surrounded by a hero tag, and it will magically animate into each other. If you are not motivated to implement animations on your own, there's the animations package, which provides high-level implementations of all the fancy animations, transitions you might use, I won't get into details, I will just show the samples. This is one you have container transformations, where things might transform into other shapes, especially the open container class here. The next type of animation provided there is access transitions, you often have cases where you transition on accesses, for example, during a log-in process, during a stepper or something like that. These are super common animation samples, and they are provided as a high-level implementation over there. Now let's talk about performance once again. I used to sample around. I was doing some animations in Flatter and React Native, that was still back in times where Flare was more popular than Lottie and Flatter. Some basic scrolling Flatter does at 60 fps, if your device is 60 fps, believe me. Flatter is super performant at scrolling, mostly because of its super interesting list view. I would likely go deeper into that, but that would not fit into 45 minutes. It consumes less RAM and less CPU. When scrolling around a list view, for example, if we have the same in React Native, if we use a Flare animation, that was two years ago, Flatter is super unperformant, because there it was rendering, it was directly using rendering objects instead of widgets, which shows us one of the future disadvantages of Flatter. It's not meant for 3D stuff, it's not meant for GL stuff. It's mostly meant for applications displaying stuff, and not for, for example, games and stuff. So there, Flatter is way less efficient, but if we look at Flatter native animations, they're super performant. The sample I had here was a screen with 10,000 GIFs being rendered on a grid and basically some user input was given to the device it was rendered on where it was scrolling around, and that resulted in React Native to 7 frames per second, and Flatter was still doing almost 20 frames per second. It had a high CPU load, but almost a third of the RAM React Native was consuming, and that shows that Flatter is pretty efficient at its rendering. One very important thing to still be mentioned at the end, I don't want to give you too much of enthusiasm, Flatter the bad and the ugly. Flatter itself is open source, it's a framework under mid-license developed by Google. I already mentioned that one, but try to build it yourself and fail. The build of the Flatter framework itself unfortunately relies on blobs falling out of Chromium CI. That's a bit, if we download the Flatter framework, for example, it does not even include the engine, but by default downloads the engine from the Chromium CI and we can technically not know whether that's actually what's compiled out of the source code of Flatter. Also, we can compile the Flatter engine on our own, no problem, but still it downloads all its dependency as blobs from the Chromium CI and you don't know what. That's also, by the way, the reason why Flatter has not been ported to other platforms, Google does not want it to be ported to, because you always need pre-compiled blobs from the Chromium CI. That's the main reason why Flatter was not ported to 3DSD yet, for example. There were many developers aiming to do that, but the Flatter engine does not allow you to compile it if Google does not want it and that's, sorry, that's shit for an open source project. Yeah, also there used to be privacy regressions and there still are privacy regressions. Let me explain them. Yeah, Flatter often connects to Google Fonts. Why? It sounds very evil. It is evil, but let me explain it in a way I can understand it as web dev and not because Google Fonts are easy to use, but Flatter renders in CanvasKit. CanvasKit uses WebGL and WebGL cannot access the fallback fonts of your operating system and Flatter's way to do that was, okay, if we have, for example, that's often the common, if you have foreign symbols or emojis, if they are not available in the fonts bundled with your Flatter application, the Flatter engine decides to connect to Google Fonts and to download some Note 2 fonts. That's a very evil pattern because it's nowhere documented. We were recently stumbling about that over that one at work and found, as a privacy regression, it was super hard to work around because Flatter doesn't intend users to prevent you from that. It simply connects to Google Fonts. It's hard-coded. It downloads these font files, unless you override them by providing a font with exactly that name, whatever it contains, just as a hint if you want to prevent it from that. It's a bit weird. The second huge privacy regression, especially in past, was Flatter experimented with the Fiat libraries on mobile platforms, too, also known as Split APKs, if we talk about Android. Instead of using an open source implementation of it, it was simply shipping the Google service framework implementation of APK Split up without telling anyone. We had huge problems with apps included into FDroid because, on the sudden, they included some proprietary Google stuff and we did not even know from where. It took us some time to investigate where it came from. We could work around it, but also, that's the evil part about Flatter. It's a Google project and use it with caution. Yeah, that's it as of now. Time for questions. Thank you for your great talk about Flatter and how to use it. We have about 15 minutes left for questions. If you want to ask a question, just put your hand in the air. Hello. Okay, thank you for your great talk first. It was a lot of information. So I took some notes and I have to process that later. It was very interesting. At the end, the one with the ugly is just like a real downer and also it sounds like, can you actually use your applications offline because if it loads your fonts? Yeah, I mentioned it's only if you use CanvasKit and that only applies if you deploy it as a web app. And only if you use CanvasKit as render and the web app Flatter contains two ways of rendering in the web, which is the basic HTML renderer. That one has huge issues with performance if you do animations because HTML is not meant for animations and complex rendering like shadows, dropouts, and so on. They are a bit buggy on HTML because HTML is not meant for that. Therefore, OpenGL is used on high-end devices and there it only fallbacks on these. Otherwise, if you prevent from that and you don't have a font shipping, for example, a new Unicode 15 emoji, you will simply see the common square of, hey, I don't know what glyph it is. So it does not prevent you from deploying offline. Also, you can work around these by simply bundling the Noto Color Emoji Compat font when you are gone. Then other questions. Hi, interesting talk. If Flatter renders using WebGL on Canvas, how does it deal with accessibility for example, screen readers and stuff? Yeah, it's super interesting. It uses WebGL and CanvasKit when rendering on web. Also, again, we are only talking about web, but it deploys everything as HTML element to everything which is relevant for screen readers or has area text set and all privilege widgets in Flatter have area text set or at least area representations translated into HTML area text are rendered at exactly their position in background, overlaid by the WebGL as text or whatever it is to in order to have accessibility with screen readers. That's again this part. Let me scroll to that slide. Here. Text is part of the Flatter engine. Everything which is text is not processed in Dart as high-level part, but as low-level component of the app integrating to the native platform in web. That means it's rendered as regular span, as regular paragraph element. In Android, it means your screen reader will read it out. If you use this, I forgot how it is called, this thingy where you tell your screen where to tap on it recognizes all the stuff because it's rendered as native text provided to the operating system. Are there any other questions? Okay, so that means if I want to use Flatter, there's only one implementation by Google. There are no different implementations of the same framework by other vendors. No, Flatter is a framework developed by Google as much as React Native has only React Native. There's one implementation for Flatter. There's also only one single runtime implementation for Dart at least yet. But yes, you're exposed to whatever Google developers of the open source project Flatter does with Flatter. Okay, and how stable is it? I mean, Google may change it at any time. So Google uses Flatter in their own projects at many places. So it might be a cutoff because their entire Google advertisement, everything which is UI around Google advertisements is written in Flatter. So that would be a huge cutoff for Google itself, believe me. And yeah, Flatter is stable, absolutely. You mentioned it downloads dependencies from Google during build. If Google ever drops this project in five years, can you still deploy and change old projects? No, it does not download anything during build. No way. It downloads blobs from the Chromium CI if you build the engine. And as an end user, you would actually never download the engine. The engine is shipped with your installation of Flatter locally, and then used to build the applications. And yes, if you install Flatter, it's more, it's basically a shell script you download and it installs stuff. But once it's installed, you can use it as long as you want. And it's only a complaint about the build process, how to build the Flatter as the project itself, not an application with Flatter. That one does not connect to more services than any other mobile application kit where you download your applications, your dependencies from MPM or whatever. Of course, it downloads dependencies, but only those you want, and you can provide anything locally too. It's only about when you want to build the Flatter engine itself, then it relies on this awful Chromium CI. Other further questions? If there's no questions left, thank you for your great talk. And I think you can go and grab food in about an hour.