Hey guys, happy Tuesday.
Welcome to another weekly training
that we're doing with our internal team
here at our dev agency.
We have been working to put these out every Tuesday.
We've been training our team every Tuesday,
and now we're just starting to stream the call.
So if you're interested in learning about how we work
and how we build applications, this
is a great thing to tune into.
So we've been particularly focusing
on how to take the shape up concept from 37signals,
how to take all the AI vibe coding,
and kind of put all that together in a way to have
our team, where we have a single person developing
an application and working with clients,
and how we just organize and run our agency.
So today, we actually have one of our team members,
Janata, who's been working with us, I think,
a little over a year.
And Janata's located in Brazil, and has
been a phenomenal asset to our team,
just has fixed so many things in our previous Flutter flow
applications, and now helped us move out of Flutter flow
into a Flutter app, where we can now use AI and clone
everything to code.
So I'm going to turn this over to him,
and he's going to be doing an introduction to Flutter.
So this is the first time we're working on a Flutter app.
Especially if you're working on it with our team,
this is something that would be required learning,
just to kind of get up to speed on how Flutter works
and how it all connects.
So I'm going to turn it over to Janata.
And there you go.
What's up?
Hey, man.
Thank you, Bruce.
Very happy to share a little bit of my experience.
For this presentation, I prepared
a kind of practical introduction to Flutter and Dart.
And also, we'll be touching a little bit
of how we use it on Tribe Social Mobile.
But the idea is that this presentation
will lay the foundation for future talks
as well, so we can go deeper.
So this will be a really nice overview
over the Flutter structure, the Flutter framework,
and the main concepts that I think
are important to understand in order
to be able to create applications that perform
better and that have a great user experience overall.
So I'm looking at the chat here.
So if anyone has any questions, you can also send on the chat.
I'll be looking at it.
And also, feel free to jump at any time to ask questions.
So let's start.
So let's start with a very, I would
Flutter.
So Flutter is a Google open source.
And this part, it's open source.
I think it's very interesting, because it
allows us to move things around as we need
and really dive deep into the code
if we need to do something custom.
But it's an open source UI toolkit
that enables developers to build natively compiled applications
for mobile and web and desktop.
From a single code base, it's powered by Dart.
And it's three lines development by eliminating,
and I said, like, most of code duplication.
Because as we see, there's still some native coding necessary
for some specific features, especially the ones that
are very hardware dependent or OS dependent.
So it's still needed to do some work on Swift or Kotlin
if we are doing some very specific things.
And also, Flutter was designed to have a native performance
that's achieved through direct compilation.
And I will show how this handling system for Flutter
works and why it's different from other hybrid platforms,
hybrid frameworks.
And I will showcase a little bit of the difference
between web and mobile development for Flutter
and how the performance-wise works for both platforms.
So I added here on the web on the end
that web version still has some handling and scheduling issues.
Mobile is a lot better in terms of performance.
But we'll look through this in detail on the next slide.
So really quick here, doing a kind of reality check
for the cross-platform idea for Flutter.
So the idea is that you write once and you run anywhere.
And this promise does come with a few exceptions,
especially that developers need to tweak the platform's
specific behaviors or accommodate
or add or remove.
OS or hardware level integration separately.
There are tools that we need to use, for instance,
if you are going to depend on a lot of work with camera,
or with the gyroscope from the phone,
from these hardware specific things,
there's a chance that you won't find a Flutter component or a pub,
as we call the extra package we can add on top of the Flutter framework.
You may not find this already done,
like this integration in the hardware level,
so you may need to write native code.
But the interesting part is that Flutter is very open to it,
and it allows for us to have native breed.
You can write your code in Kotlin,
and also you can write in Swift,
and you can use this code on your Flutter application,
which is a very interesting ability that we have.
So that's it in the hardware level integration.
Also, Flutter web apps tend to have larger bundle size,
which impacts initial load times and browser performance.
And SEO remains a challenge,
because as we'll see on the next slide,
Flutter uses a very specific way of rendering the interface.
We basically use painters to draw the interface,
so we are not using the usual HTML or material
or Cupertino components provided by Apple, Google.
So we kind of, the Flutter framework
is drawing everything from zero, let's say,
which is a challenge for having a proper SEO for these pages.
And we also have some browsers compatibility issues,
especially for advanced device features
requiring fallback logic or native implementations.
Flutter web excels at internal tools, dashboards,
complex UIs, and cross-device user experience
where SEO is less important.
These are the ideal use cases for Flutter web.
And also it's great for happy prototyping
and reducing the development cycle
by reusing the logic across different platforms.
And also Flutter web is doing a great job on the PWA side,
on the PWA support side.
So if you have a Flutter web application
that you'll be running only as a PWA,
it usually has a better performance
than if you treat it as a normal web application.
Also, I wanted to kind of bring a little bit of a comparison
across other cross-platform frameworks.
So as you can see here,
the usage of Flutter is growing across the years
and Reactive Native is actually slowing down.
The growth is actually slowing down for this platform
and the user is actually getting lower volume
as the years passes.
And I think a lot of this is because
Flutter has a very interesting ability
in how it's generating, in how it's handling the interface.
So one last piece of information regarding this comparison,
because I think like Reactive Native and Flutter
are the main players
when we talk about hybrid development right now.
So one interesting data is that the Flutter repository
has a little bit more stars than Reactive Native,
showing how the Flutter community is still active.
And one fun fact is that I've started working with Flutter,
I believe like six years ago, six or seven years ago.
And since then, almost every year,
it comes around some form of news that Flutter is dying
or Flutter is dead,
and only to continue living for the next year.
So this is one thing we need to take with a grain of salt
when looking at this type of hybrid platforms development.
So moving forward,
one very important piece of Flutter
is how it works with rendering.
So Flutter delivers a...
native-like apps by skipping the device's built-in UI components, the OEM widgets,
and instead drawing everything itself. So it uses the Skia or Impeller in the
newer version on Flutter 3.29 forward. I think the default is Impeller
handling engine or CanvasKit for the web to handle directly on the device's GPU.
So the OEM widgets in mobile developments refer to the built-in UI
components provided by the device operating system, such as buttons,
sliders, lists, and text fields created by platforms like Android that has the
material guidelines and the iOS with the Cupertino components. So Flutter doesn't
use it directly. If you see the Flutter widget catalog that you'll see a little
bit further on the presentation, you'll see that we have the material widgets and
the Cupertino widgets, but they are being drawn from zero using these rendering
systems instead of relying on converting the code to Swift or to Kotlin to use the
material or Cupertino libraries. We draw it from zero. So these widgets use system
level code and styles, ensuring the app looks and behaves in a way that matches
the native experience of that device, and they're called native because they are
maintained and handled by the operating system, not by the app developer. Flutter
skips all that and draws the interface directly. So this is very important to
be aware because Flutter is very dependent on handling the components, and
this changes a lot on how we think about it in terms of performance of the system.
And so in a lot of cases, we would be avoiding drawing things directly, but in
Flutter click case, it's actually better. So sometimes if we need a very
custom component, sometimes it's just better to just get a custom panther and
draw it on the interface. And since we have this very optimized hander, the
skia or the impaler, it's usually very performance oriented to do this kind of
performance directly using the handlers. So now moving forward about the Flutter
philosophy itself, you see that as we go over the next pages of this
presentation, we will be diving deep into the very initial Flutter concepts that
are necessary to build our knowledge blocks to be able to add more complex
things on top. So first of all, the very basic thing about Flutter is that
everything is a widget. So you hear it often if you read the documentation, if
you're watching tutorial, that widgets are the building blocks of Flutter app's
user interface. And each widget is an immutable declaration of a part of the
user interface. So widgets are used to describe all aspects of a user interface,
including physical aspects such as text, buttons, and even like paddings or
centering something. You don't have like a parameter that you center. Instead, you
have a widget that's called center and all the widgets that are added as child
from this center widget, then will be centered. So it's a very hierarchical
based composition and each widget nests inside its parent and can receive context
from the parent. This structure carries all the way up to the root widget, as we
can see here. So this is basically the hello word from Flutter. So here, as you
can see, we have a material app, which is a widget. So it's the basis where
everything is built on top. It's the material app. And then you have a scaffold,
which is basically a structure that allows you to have an app bar and to have
a body that is the basis for building the rest of the application. And as you
can see here, it's all is very like HTML-like. We'll be nesting these
components.
and when they are indented a little bit further,
this means that this part is a child from this one,
and if they are on the same line,
as you can see here,
this means that they are on the same level of hierarchy.
I have an image below here that makes it easier for us to see.
This is the visual result from
this code would be something like this.
We have a app bar here on the top,
you have the Hello World text that is right here,
and you have a sized box.
Even this sized box is a widget,
which basically a widget that has a specific space to add
between components and a button.
As you can see here on the right,
we can see the hierarchy more easily.
We have the center and the column that's inside the center,
because the column is centered, as you can see here.
It's a child from the center widget,
and then we have the text,
sized box, and button,
all of them as children of the column,
all on the same level.
Moving to the next part,
which is also one of the core concepts of Flutter,
is that we have a declarative UI.
Basically, when you code with Flutter,
you write what the UI should look like.
Rather than imperatively specifying draw steps,
Flutter redraws automatically on when data changes,
similar to React.
For instance, if we look at Swift,
now Swift has a bit more of reactive touches to it,
so it's a little bit more declarative,
especially with the Swift UI.
But on the early days of Swift,
we had a structure very similar to this imperative style.
Imagine that we want to turn this view into this view.
We have a yellow background with two components.
In the imperative style,
we would have to do something like,
let's take the B view and set the color to red,
let's clear all its children,
and let's create the C tree view and add as a child for the B view.
This would be the process in the imperative style
to move this screen on the left to the state on the right.
It's the same screen, different state.
On the declarative style,
this is a little bit different.
We just return a view B with the color,
the child, and the information that we
have as its own component, as its own widget.
Here we have a small image to make it easier to understand.
Basically, in the declarative style,
you describe the UI structure and you return a composite widget,
so it's a widget composite of other widgets.
In the imperative, you have a mutable objective state,
and you call methods in sequence in order to update this object.
Also, please just jump on if you have any questions.
If I'm going too fast,
I can slow down and go back to any of the items.
Awesome. The next big thing,
and I really love this little formula right here,
because it basically says that
the UI is always a function of the current state.
That's how you think about the interface on Flutter.
It's a function of the states that we have.
When the state of your app changes, for example,
the user flips a switch in the setting screen,
then you change the state,
and that triggers a redraw of the user interface.
Here is the power of the Flutter handler.
Since it's very fast and very optimized,
the user never notices or almost never notices this redraw.
You can be redrawing all the screen to change just one component,
and sometimes the user won't even notice if this is done right.
you can rebuild the whole screen
and the user will only see that,
oh, that little component,
that little button changed the state,
but behind the scenes,
you could be rebuilding all the screen.
So there is no way of changing the UI itself,
like widget set text.
You change the state and the UI rebuilds from scratch.
So that's how we think in terms of building interfaces
and the main, let's say,
philosophy behind the Flutter structure.
It's the reactive model,
it's the declarative UI,
and it's the everything is a widget.
So it's these three basis concepts
that serves as the foundation
for us to lay the rest of the interface
and the rest of the structure of the framework
on top of the three main core concepts.
So let's move further.
Now, diving a little bit deeper
into the widget fundamentals itself.
Like I said, everything is a widget inside Flutter,
but we have mainly two types of widgets,
the stateless widgets and the stateful widgets.
Let's start with the stateless,
which is the most used generally.
So the stateless widgets are used
when the UI doesn't depend on dynamic data.
So widgets that have no mutable state,
they have no class properties that change over time,
they subclass the stateless widget.
So they declare themselves
by extending the stateless widget.
Like on this example below,
we are creating a class that's called,
in this example, it's nice to mention,
it's actually both examples are from our tribe app.
And in this specific case,
we are looking at the chat feature,
the chat feature that we have.
When we draw the interface and we say,
hey, this is a text message,
and this is the text that needs to be inside this bubble
of text inside the chat,
we won't be changing it anymore.
So that's why the text message itself is a stateless widget.
Once we say, hey, the user said hi,
and they send the message,
there's no going back, they can delete it,
but they will be removing,
they won't be updating the content of that text message.
So as you can see here,
also just to mention,
many built-in widgets are stateless,
like padding, text, and icons,
things that don't change very frequently.
And when you create your own widgets,
you create stateless widgets most of the time.
So here on the left,
we have this small example for the text message class
that extends the stateless widget and only has the text.
If it's a reply to someone,
the text color that should be used for this bubble,
and if it's on the mobile view or not.
And that's all we need for building this component.
And since it's not dependent on dynamic data,
then it can be a stateless widget,
meaning it doesn't have a state.
For the stateable widget,
then it's our widgets that hold local state,
like a counter or a toggle switch.
For example, if a widget has a counter
that increments whenever the user taps a button,
then the value of the counter is the state for that widget.
So if something changes
and the interface needs to change
because that piece of data changed,
then we are probably talking about a stateful widget.
The widget needs to be rebuilt to update its part of the UI.
And these widgets then will subclass the stateful widget
and they store multiple states in a separated class.
So this is very interesting, I'll show here below.
We declare first the chat page,
extending the stateful widget,
and we define the structure for that chat page.
So on this specific chat page,
we have the group that this chat is inside.
We have a reference to a conversation
that contains all the data of message
and users involved on this conversation.
And we also have a static root name
and a static root path that's using for a user.
the location of the app on the web page.
We have the chat page state,
and it extends a state.
As you can see here, it's a state for the chat page itself.
We only can do this extension if we
declare the chat page as a stateful widget.
If we try to do the same thing for
a one that's here on the left,
it won't work because it won't have a state associated with it.
Then we have some cool abilities like we have
this overwrite method for the init state so we can
decide what we'll do when
we initiate the state for this widget that we are building.
In this case, we create the model for it,
and we'll talk more about the model on the next field.
These slides, but basically the model is
the data structure behind the widget.
We'll log a Firebase event,
and we then move on with the initialization of the component.
Just to sum things up,
a stateful widget holds mutable states and it builds on state change.
A stateless widget, it's mutable and there's no local state.
So the stateless widget is not rebuilt
when there's a change on the state of the page.
So the stateless widget is static.
Now, moving to a little bit of a more,
let's say, the fun part,
let's say, is the layout system itself because personally,
I really like how Flutter deals with UI building because it's
very easy to read the UI components itself.
As we can see, very,
very dense library and
very complete library of widgets that are already available on Flutter.
As I said before,
we have here two design system,
Cupertino, that's the Apple design system,
and Material Component, the Google design system.
Both of them, and that's very interesting because they look exactly
like the iOS and the Material Component,
but they're actually being drawn by the render.
So they use painters behind the scenes to draw the square,
add text, and make it feel and look like the components we have familiarity with,
but they are not using the components behind the scenes.
That can create some form of lagging.
As you can see, for instance,
all the Cupertino components that we have here are old now,
that we have all these new iOS versions.
So if you look at the iOS 26, for instance,
we have a different design for a few of these components,
but the Flutter library didn't catch up yet because we need to redraw everything from zero.
But if you, as a Flutter developer,
really wants to have the look and feel of iOS 26,
for instance,
then you can basically create these components yourself using custom painters,
which is basically what these components are using behind the scenes to design
and to draw the components on the interface.
So let me go back to the presentation.
So here are the main components, row and column,
stack.
Stack is for putting things on top of each other using the z-axis.
Row and column are pretty self-explanatory.
Container, it's a container.
Padding, it's the spacing.
So this is a very interesting thing about Flutter.
When you want to add space,
you literally add a widget that's called padding,
and then you inform if it's going to be a top padding,
if it's going to be a bottom padding.
left, or if it's going to be a top, left, right, and bottom. So it's a widget as everything else
we have. Just like the flexible and expanded widgets, for instance. If you place something
inside the expanded widget, it will take all the available space. And this, it's a kind of
different way of looking at things, because on some other cases, this would be like a property
of the component. So you have like a column, and then you set that that column has the expanded
property. In Flutter, you'll be declaring the expanded widget, and you'll be adding a column
as a child for that expanded widget, and then the column will take all the
available space on the screen for that component.
Now, we'll be moving from the Flutter framework itself to the Dart, which is the language that
Flutter uses. And I will not spend a lot of time on this topic for now. I just wanted to
give the three things that I think are useful here. So basically, Dart language is strongly
typed with inference, which is a very interesting thing. So we can do something like final data
and pass a string, and then from now on, since you declared as final, the data is expected to be
a string. And if you try to parse it to something else, it can cause an error.
We also have no safety built-in, so variables are non-nullable by default. So if you declare a
string name, the Flutter framework, the Dart language actually expects for it to not be null
unless you explicitly say that that string can be null. So this prevents null reference and errors
that compile time. Finally, the soundness. So Dart's type system, like the type systems for Java
or C Sharp, is sound. So it enforces that soundness by using a combination of static
checking, compile time errors, and runtime checks. For example, if we assign a string to an int,
it's a compile error. So if you declare a string A equals to one, we'll get a compile error.
You won't be able to build the project. But if we try to cast an object to a string using
as a string, but we are trying to cast the object that is a double, for instance,
then it will fail at the runtime, throwing an error that you can't do that casting.
And one useful thing here is that you can do a nullable casting. So we can try to cast
something, and if it fails, it will be null, and you can check for it, and then you can
treat this error according to prevent failing it on runtime.
Now, regarding the asynchronous management on Dart, we basically have two keywords that
handle all asynchronous programming here. So it's the async and await keywords. So they provide
declarative ways to define asynchronous functions and use the results. So basically,
to define an async function, you need to add async before the functional body,
and the await keyword works only if you are declaring an async function. So this is very
basic, but I think it's interesting to bring this up because this is something we'll be using a lot
in basically, I think, almost every page that we'll be building. There will be some form of
asynchronous call that you need to be making, and you need to wait for stuff. So this is used a lot.
So there is a simple example here that we are declaring a future. So we are declaring, hey,
on the future, we will be receiving a string for this create order message. We declare it as
synchronous function, and we say, hey, we need to get the order variable, but
To get it, we need to await for the FetchUserOrder function
to complete.
And when we add a await inside async function,
the return will only be called once the await is complete.
So this return right here only will
be called when we have the FetchUserOrder response.
On this case, the FetchUserOrder is just
a delayed return with a two seconds delay.
But you can imagine how this can be any FavAp call or anything
that we need to await the response from.
And then to call the function we just created,
we just need to use the await as well.
So this will be automatically handled by Flutter.
So when we run the main, it will print the FetchUserOrder
instantaneously.
And since this is on the await, it
will wait on the two seconds.
And then it will print the URL there is and the data,
the large late, large late, that we are passing here
on the FetchUserOrder.
So just to nail this down, the key
terms we need to think about in asynchronous programming
for Flutter is basically the async and await.
You can use the async keyword before a function's body
to mark it as synchronous.
And you can use the await keyword
to get the completed result of an asynchronous expression.
And the await keyword only works with the async function.
So that's it regarding the asynchronous part.
I see Ira had a question, like, is a future
the same as a promise?
Yeah, perfect, perfect, perfect analogy.
It's exactly the same thing.
And future type is always, yeah, exactly.
If you declare a synchronous function,
you always need to have a future as a return for it.
And you can define which type of future you'll be receiving.
And this can be even a generic.
So you can have, like, a generic future.
So you don't need to specify what you'll be receiving.
And you can have, like, custom classes here.
For instance, if we have, like, a user record,
we can receive a future that's a user record
and that has all the data mapping to the name property,
the age property, and all the user information
that we may need.
Let me take a look in the chat.
Perfect, I think I covered it all.
So moving forward, the state management part.
So we basically have two kind of state management.
We have the ephemeral state,
meaning the state that's UI-related,
or it's the local state,
meaning things you can neatly contain a single widget.
So this is intentionally left as a vague definition
from the documentation, and you'll see below,
because you don't necessarily need to choose
one over the other, and the line is not so clear-cut.
But some examples are the current page in a page view,
the current progress of a complex animation,
or the current selected tab in a bottom navigation bar,
all are examples of ephemeral states.
And probably we won't be needing this
on the next page of the app,
or we don't need to keep this for the whole user session.
So this is what you call the ephemeral state.
The app state is basically everything that's not ephemeral,
so it's things that you want to share across all the app.
So if you want to keep between user sessions,
we have user preference, login info, notifications,
the shopping cart in an e-commerce app,
and the read or unread state of articles
in a new app, for instance.
So this is what we call the app state.
And for managing the app state,
this more complex state,
then your choice depends on the complex
and the nature of your app,
and your team's previous experience,
and many other aspects.
So we have basically three built-in approaches in Flutter,
like three native approaches.
just for Flutter and how we handle states.
The first and probably the most we use it
because it's also the simplest is the set state.
So it's the first time I was learning about Flutter,
I was like, hey, this is funny because it's like,
it's literally like a function that you call
and everything that you use inside means
that you are updating a state that you need
to rebuild the application.
So it's only works with a stateful widget
and with only a single stateful widget.
This stateful widget can be like a whole page
or even a set of pages that are built over time,
but in the code, it will be declared
as a single stateful widget.
And that's the limitation of the set state also.
You can't share the state with other widgets,
which can lead to duplicate logic
if multiple widgets use the same data.
So it's the best use case for it is
for simple widget specific UI updates.
And this directly tells the framework
to rebuild the widget.
So it's like kind of the hard code way of saying,
hey, right now I need to rebuild this
because I've increased the counter
and this will cause all the interface to rebuild
for that specific stateful widget.
So the set state is a function of the stateful widget.
Now getting a little bit more nuanced here,
we have the value notifier and the inherited notifier.
So this built-in Flutter APIs that you organize
and share state and trigger UI rebuilds when it changes.
So we have the value notifier for a single value
and the change notifier for complex state.
Both have listeners that you can subscribe to.
So you can use a inherited notifier
to allow child widgets to listen efficiently
for changing a parent state.
The scope for it is that you can share
different from the set state.
You can share state across multiple widgets,
not just locally.
And the use cases for simple values,
you use the value notifier.
And for more complex use cases,
then you have to use the change notifiers,
the change notifier.
And this triggers a build in listeners
that are the value listener builder.
So one example here,
as you can see, we are the same way we have a count here.
We are trying to keep like,
this is the example of a counter.
So we have a value notifier that's an integer,
that's the counter and declared the initial state as zero.
And then we instantiate a listenable builder
that basically says,
hey, the value I'm listing is the counter.
And every time it's changed,
I want to run this builder right here.
This builder has the context, the value,
the new value from that was received
from the value notifier.
And we have the child itself.
On this case, we are returning a text widget
with only the value that we are receiving.
The advantages for this method is that it's the state,
the couple of the state change, then UI updates.
And it's useful for shared values
and more dynamic scenarios rather than the basic set state.
The limitation is that we have some kind
of manual listener management,
and it's not always ideal for deeply shared state,
which is kind of resolved by the last guy on our list,
which is the inherited widget and the inherited model.
The inherited widget basically is a way
to provide shared data to child widgets
with minimal boilerplates, avoiding manual data passing,
what you call prop drilling.
So child's widgets use a static method
to access the shared state,
and rebuilds are triggered only when needed.
Many, and this is very interesting.
So basically, we will see below,
we have a lot of community-based adoptions
that the community has viewed for providers on how.
how we can manage the states for more complex use cases,
but most of them are using this inherited widget
and inherited model as the basis to build their providers
and to build their infrastructure as well.
So the scope is API-wide and can be used in any sub-tree
and can share data with any descendant.
So when you want to avoid passing data down
to every construction, so every constructor,
we can use this inherited widget,
which makes the code cleaner,
so you don't have to pass the kind of creating
a lot of boilerplate by repeating that,
hey, this widget has a name
and its child inherits its name
and its child inherits its name and so on.
So instead of like having to pass to every constructor,
we can use this inherited widget.
So here we have a small example.
So let's say you have a class called MyState
that extends the inherited widget
with a count as a property
and a callback that's called the increment.
So we declare the state and we say,
we depend on the inherited widget of exact type
and we pass the state we are creating,
where we assert that the value is not new
and we return the result.
And here we override the update should notify.
So on this line right here is where we are stating
when we want the state,
or actually when we want the widget to be reviewed.
So in this case, we only be notifying when the count,
the count value is different from the old count value.
And for usage, we can just call the class we just created,
passing the context of the application
and calling the increment.
This will update the count
and will notify the inherited widgets once this is changed.
The advantage is that it's cleaner,
it's more scalable with many shared data
and many state management solutions like providers
are built on top of it.
Limitations, more boilerplate compared to basic options
and not as flexible for fine grained state control,
unless paired with the notifiers.
So here I have a small table to compare
for make it easier to understand.
So the method set state is for a single widget,
it's for local and very simple ephemeral states,
updates the whole widget and doesn't have a sharing,
it doesn't have the ability to share its state
with other widgets.
The value notifier, we can scope to multiple widgets,
it's best for shared, simple and complex use cases.
The listener reviews the interface
and allow us to share the state.
Finally, the inherited, the scope is three wide
because we can inherit it at any point of the tree.
So we can use it for a global shared in that state
and all the descendants will have viewed
when the parent is updated.
If the update returns as true for this update
should notify that we are overriding
for the inherited widget.
And to finish this part, there is no clear cut rule.
So to be clear, you can use state,
you could build the most complex application
using only the set state to manage all the states of that.
In fact, the Flutter team actually does it
in many of their examples,
including the starter app we saw before.
But we can decide that for our specific context
or our particular app,
the selected tab bar is actually not a femoral state.
For instance, if we want to remember that the last tab
the user selected in our application was the community tab
and the user closed the application
and when you open again, we want to be on the community.
unit step, then we probably can't rely only on the state
and sub-state management for it.
We need to make it into a app state management.
So key takeaways.
Build the class cross-platform power of Flutter.
So you build once, and you deploy everywhere,
even if you have some performance
issues on some places.
I think, in general, the trade-off is usually worth it,
especially because you cut a lot of costs
by having just one code base that you can
run on mobile, web, and desktop.
This is very good for quick MVP and prototyping and getting
a faster time to market.
We have the widget first thingy.
So everything is a widget.
You can compose complex UIs from simple reusable building blocks
using the declarative syntax.
We have access to the Dart modern features
like strong typing, no safety, and elegant async await
patterns that make your code safer and more maintainable.
And we have scalable state management,
meaning we can start with very simple things
like the set state and then graduate
to more complex things like the provider as we want.
One thing I forgot to mention here
is that we have, right now, I'm showing inside the PubDev,
which is basically the library for all pubs related
to Flutter, which are the packages that are built
that community builds to help other devs.
So it should be state management.
So we have Flutter block.
We have RiverPod.
We have Hooks.
We have a lot of variations of blocks.
And as you can see, there are ones
that have 3 million downloads.
Others have a little bit less.
And it's really up to you to take a look
into what you like, what you think
it fits best to your project.
My recommendation is to usually don't
start by adding a external dependency.
You can try to, oh, sorry.
Let me share my screen again.
One second, I think.
So my suggestion will be to always start
with the built-in options.
So try to use head state, value modifier,
or the inherited widget.
If, for some reason, you think this
is adding too much boilerplate, or this
is being too much of a pain to manage,
then look into the pubs.
Because if you start by refusing a provider,
you probably won't understand why the provider is
needed in the first place.
So it's good to have this.
Yeah, I think it's borrowed from React solution.
I think it's borrowed from React.
I don't think it's like a fledged solution.
We may be losing him a little bit.
I don't know if that's me.
You guys can hear me?
OK, yeah, I think it's Janata.
This is awesome.
I know we're coming up on a couple more minutes.
I'll give him a second to hop back in.
But let me clear this.
There we go.
We'll wait for you to unfreeze.
I thought it was you.
OK, this is great.
If he comes back, we'll let him wrap it up.
But this is good.
We'll probably do some more deep dives like this.
Oh, there's Janata.
You're back.
We lost you for a minute.
Maybe we lost you again.
You lost me?
Can you hear me?
Can you see my screen?
Do you have any final thoughts?
A couple of minutes.
I know we have a couple of minutes left.
We may have to go to a part two next week.
Yeah, I was thinking about that because I still
have some stuff to talk regarding
like how we are applying this to the tribe itself.
But I think we can kind of cut it for now
because actually the next slide, it
will be like the Tribe Act title review.
I'll just share a small spoiler right here.
So it's basically we'll be looking at the key directory.
how the folders are structured,
and how all these foundational concepts
actually apply to Stripe.
But I think it's better for us to leave this for a second.
Yeah, we'll do that in part two.
So like and subscribe if you wanna see part two.
We'll be back here next week.
But thanks, Gennaro.
That was great.
If you guys have comments or questions,
you can always drop them in the comments
if you're watching the replay.
And always, if you're interested in joining our team,
there's always an application in the description.
So thanks, Gennaro.
We'll be back for a part two.
And we'll hang out for a second with the team,
but I'll go ahead and end the stream.
So thanks, guys.