Don’t do drugs, do Flutter! (talk #2)
This post was originally posted on blog.juliobitencourt.com and to a better experience is advised to go there, since medium is a little limited.
This post is a sequel from #1, so in any case just give it a look there.
In the previous one we dealt with a presentation ‘talk’ about Flutter SDK, and on this one we’ll see a detailed explanation of a live coding made to show a tech demo. The complete code is on my GitHub.
The app we’ll create is purposely very simple, although we’ll see some fun stuffs like http requests and screen navigation, besides getting the hands dirty and looking at some working Material Widgets. I’ll try to go step by step, as well as show the code and result.
But before getting the hands dirty it is important to understand as we already argued, in Flutter everything are Widgets and bla bla bla. However, there are two kinds of Widgets, Stateless and Statefull.
1> Stateless, are the ones who literally does not have state, they’re ‘static’, basically show info, as a Button, Text or Icon.
2> As for the Stateful, they have state, are dynamics, the user can somehow interact and change its state, as a checkbox wich is selected/not selected, or a slider.
Now back to business, we’ll finally see the first code snippet.
This is as simple as a Flutter app can be, the main()
method is responsible of starting the app, in our example it returns a function runApp()
(yeah, for those who comes from java, how stunning is that?), from the material.dart
package, that is responsible of receiving a Widget and basically throw it on the screen. Text is our first Widget, Stateless as mentioned before, basically receives a String and in this case a direction in wich we want to render the text. Flutter is completely compatible with LTR (Left to Right) and RTL(right to left), as the Arabic language.
The modifier =>
replaces the famous return {}
. Running our app the result is:
The text is rendered in LTR with a black background, yet very difficult to read at the corner of the screen, let’s use a simple widget Center
:
The simple snippet above can show we don’t need a previous knowledge to understand that will be rendered a centered text. Flutter + Dart is a great combination to turn the code reading very intuitive. Even more after Dart 2 where new
is no longer required. To give it a better looking we’ll extract what is inside runApp()
.
MyApp
is our new Widget, Stateless because we don’t need to keep a state. Every widget has the build()
method, very important to its lifecycle, called automatically by the framework every time it’s necessary to rebuild itself. Inside build()
we return a MaterialApp
, we can say it is a one-size-fits-all widget, having many functionalities to make it easy our life, as themes, navigation routes, among other things like the app title and the home parameter, where we defined our homepage as the text, only that this time without textDirection
, since the MaterialApp
already defines all it’s children to be LTR by default. But only that won’t change a thing in what our app looks like, that’s why we’ll introduce a new Widget.
We changed the homepage to a Scaffold
, this one now is our one-size-fits-all widget for material design and one of its parameters is appBar
, where we use an AppBar
, who guess what? Defines an implementation of a Material Design AppBar.
With the TextStyle
we’ll define a better look to the text, letting it blue with a font size 30.
In this app we’ll fetch a people list with an http request that returns a json as response, this json is hospedated at the same repo on GitHub, here. The structure from json is very simple, essentially an object list with idPerson
, name
and urlImage
.
Now in Dart, we can map to an object that represents our json.
Some key points on the code above.
- The class
Person
has its attributes with it’s respective types as defined in json. - If we stop and think, any json structure is basically a key/value pair map. That’s why it is exactly this way that is represented in Dart. At line 8 we create a constructor
fromJson
responsible of deserializing the json, in other words, transforming it to an Object, that’s why it receives aMap
ofString
( the key will always be a String) anddynamic
. Dynamic for Dart is like a generic type, any other type can be on a dynamic var. - We won’t use the
toJson
method, but is counts as knowledge as it’s responsible of ‘serializing’ the Object, in other words, transforming it to a Map/Json.
Now we’ll create a class called PeopleApi
, responsible of making an http request, parse it and return a people list. And for that it will be necessary the http
package from Dart. The Dart libs are hosted on pub.dartlang.com and in Flutter are managed through ubspec.yaml
, so let’s add this dependency on it:
That will add the http
package on our project.
The method _get()
is the one who makes the request, and it’s with it we’ll learn a very important concept to Flutter. Dart is single thread, so whenever we want to execute a task that can take some time, like access a database or an http request, we need to do it asynchronously. Otherwise if we use the main thread the app would freeze and the user wouldn’t be able to interact with it anymore.
So, adding async
on the method, we tell Dart that at any point in time our method will execute some task asynchronously, but when? Well, that’s why our friend await
is for, as soon as Dart finds this keyword it thinks ‘Ok, from now on this will execute asynchronously, so I’ll just get back to my main thread’. And at this exact moment it will already return a result to whoever called the method at the first moment. What will it return? You may ask me.
A Future
! Future
is basically a promise, that is, some moment in the future (when the async method really finish the execution), this Future
will have a concrete result.
On our get we use the _get()
from http, imported from the package:http/http.dart
package, in addition we define the await
to make a request on the URL. After that, json
from dart:convert
to decode the body of the response, that defines as return a List
of Map
. That’s it! WITH TWO LINESSS OF CODE! We’ve made an http request and decoded the response.
In Dart, the character
_
in front of the modifiers means that this var or method will be private.
The next step now is take the response of the request and transform it into our list of people, so let’s change PeopleApi
.
Our method loadJsonFromApi()
, async
either, will basically use the _get()
created previously, and for each Map
of this list we’ll transform into a Person
through the fromJson
constructor, adding all into a list and returning it.
Let’s get back now to the fun part:
On the main file we extracted the Text to a new Widget PeopleWidget
, this one is a little different from the previous one we created, it is a StatefulWidget
, after all we’ll keep the state from our people on screen. Every Stateful widget must extend State
, wich is the class responsible of literally manage the state. So let’s play with _PeopleWidgetState
.
As we know, build()
is called always that is necessary for the widget to rebuild itself, the truth is, in it’s lifecycle the build can be called many times, so it wouldn’t make sense to do a json request in it (imagine any time the user scrolls the list, a request is triggered). By the other hand, initState()
is called only once on the widget creation, a very good candidate to make an API request. So when the widget is created, getPessoas()
will be executed, making a call to the API, it is also async, so at any moment in the future it’ll fill our _people
list. After this return, the setState()
method will be called, this one is a specific method from Stateful widgets as is tells the widget to rebuild itself. (build()
will be executed again). On the Text inside build()
we have a ternary operator, if the people list is null (remember the json request is async, do the first time build is executed, _people will be null) we show ‘LOADING’ otherwise we print the people list. The result bellow:
Adding toString()
on Person
:
Still far from the final result, so let’s move on:
In case the request isn’t done yet, instead of ‘LOADING’ we change to CircularProgressIndicator
that simply shows a circular loading. We also changed the print list to a ListView
, widget that makes it easy the creation of horizontal and vertical lists, it has a builder where we pass the amount of widgets that will belong to the list, and with that, each will call on demand the function we defined on itemBuilder
to return the widget corresponding to the index. By now we’ll only return a Text with the name of each person.
Now we have a list of names, but not with a very good look, so:
The ListTile
already implements the Material Design specifications to sizes and spacing, and is an excelent candidate to use on our list, we’ll put the Text
on its parameter title
and the result will look much better:
About the avatar image, we would also need to make a request, with all that async
story and everything else, however, Flutter already gives us a ready to go implementation, all we need is an image URL and NetworkImage
, then we use together with CircleAvatar
to get the desired result, we also define in backgroundColor
that while the image request are not done, it’ll show a blue background. Then we put all this inside leading
ofListTile
:
The ideia now is to click in one of these people, navigate to a new details screen, for that we can create a file details.dart
with a very new widget called DetailsPeople
(Stateless, after all we won’t keep any state, only show person informations).
In this widget, we’ll receive the selected person via the constructor, on build we’ll use Scaffold
again together with AppBar
to have a MaterialDesign look and feel. On body
we’ll introduce a new widget called Column
, wich basically render its children Widgets one above the other, in our case, the avatar and name.
Now with this created, we can change on main.dart
to call it.
We envelop the ListTile
inside a GestureDetector
, this will allow us to identify when the user will make a tap on it, and as a parameter of onTap
we define the function that will be executed. With Navigator
and MaterialPageRoute
we create a navigation route to the details page, push
will basically put our new page on top, like a stack. The result will be a working navigation between the two pages.
The widget Row
works exactly as the Column
, rendering the children widgets side by side. If we put our Column
inside a Row
we can align the widgets at horizontal with the help of MainAxisAlignment.center
. And with that, if we put the Row
inside a Container
, it is possible to define a background color with Color
.
The result was not quite the expected, because we changed the background of everything, and we want to change only the background of the space occupied by the image and name. It happens that the Row
by default occupies all the horizontal space available, allowing us to align everything at the center, and the Column
does the same on vertical space, and one the Column
is inside the Container
, it ends up expanding too. To correct it, we can use the parameter mainAxisSize
from Column
with the mainAxisSize: MainAxisSize.min
, now it will occupy only the minimum space required. We can take the chance and envelop our image inside Padding
to a better spacing of our person’s space header, as well as increase the avatar radius
and change the name with TextStyle
.
With that, we finally get the expected result done, although very simple we saw a lot of concepts and widgets that are useful and necessary to a real Flutter development, yet I will introduce a final modification, the frosting on the cake.
We added a new widget Perspective
and the objective is not to see it in details (It is detailed explained on this article I used as base), but yet to show the power that Flutter gives to us developers on customizing (remember part #1?) what is rendered on canvas, after all, with a few lines of code changed, Isn’t it AWESOME?
That’s it, if you came to here and saw all the process, congratulations, hope I at least aroused your interest on the SDK, and you know, don’t do drugs, do Flutter!