Flutter Reference
Notes on the Flutter framework
Updated: 03 September 2023
Notes from the The Net Ninja Youtube Series
What is it
Flutter is a way to build native, cross-platoform applications using the dart
language
- Easy to build respoonsive applications
- Smooth and responsive applications
- Material design and iOS Cupertino pre-implemented
Install Flutter
To install Flutter you need to:
- Download the Flutter SDK
- Extract into your ‘C:\flutter’ directory. Any directory that does not require elevated permissions should work
- Add the
flutter\bin
directory to your SystemPATH
(possibly also User Path, I had some issues with the VSCode Extension so maybe both?) - Close and reopen any Powershell Windows
You should then be able to run the flutter doctor
command to verify the installation, you may see something like this:
You can address any of the issus that flutter identified during the previous
Setting Things Up
General Setup
Install the Flutter SDK as described above
- You will need to install Android Studio (so that you have an android debugger)
- Once installed, on the Android Studio start screen (bottom right) click on
configure > AVD Manager > Create Virtual Device
and then download thePie
system image and once that’s done select it - Set a name for your virtual device (you can leave this as is)
- Click Finish and the virtual device will be created
Android Studio
Next, install the following plugins:
configure > Plugins > Marketplace
and search forFlutter
- Install the
Flutter
plugin and ask if it should install theDart
plugin as well - Restart Android Studio, you should now have a
Start a new Flutter project
option on the start menu
VSCode
- Install the Flutter and Dart Extensions
Create an Application
The most straightforward way to create a new flutter application is to use the flutter cli
. To create an app like this simply run:
Which will create a directory with the name of your app containing the necessary application files
You can then open the project with VSCode or Android studio
We will need to launch an emulator before we can start the application, to view all available emulators run:
To create an emulator you can use:
If we’ve created an emulator called FlutterCLIEmulator
we should see something like this when we run flutter emulator
To launch the emulator we previously installed from your terminal you can run:
The emulator will remain active so long as the terminal is open. Closing the window will close the emulator
Note that to delete emulators you will need to use the
AVD Manager
from Android studio
Now that your emulator is running you can use the following to start the application
Flutter is also able to connect to a device for remote debugging if one is connected to your computer (instead of an emulator)
Once flutter is running the applciation we can see menu which allows us to use keys to control some stuff:
The App
Basic App
The entrypoint for our application is the lib/main.dart
in which we can see the main
function for our app. We also have an ios
and android
directory for any platform specific code and a test
folder for writing unit tests
For now delete the
test
directory otherwise you’re going to get errors when your tests start failing
Overall our application is made of Widgets
, a Widget in flutter is simply a Class that renders something
The start portion of our application looks like this:
main.dart
MyApp
is a class which extends StatelessWidget
which is our root for the application
For the sake of simplicity we’ll replace all of the code in the main.dart
file with the following which will simply display the text Hello World
on the sreen. We are also using the MaterialApp
which will use the material design for the app:
main.dart
To ensure that the reload happens fully use
R
and notr
Laying Things Out
We’ll use a Scaffold
to allow us to lay out a whole bunch of things, and inside of this we can use the different parts of the layout with some stuff in it:
main.dart
Styling
Using the method above we can add a lot more layout elements to our application layout. We can also make use of fonts. To do so just add the .tff
files into a folder in our project. For our purpose we’ll just add the font files to the assets/fonts
directory
Next, we add the fonts to our pubspec.yml
file in the fonts
property (this is already in the file but is just commmented out)
pubspec.yml
Ensure that the spacing in your yml
file is correct otherwise this will not work as you expect
Our app with the styles and fonts now applied looks like this:
Stateless Widget
A stateless widget is a widget that’s data does not change over the course of the widget’s lifetime. These can contain data but the data cannot change over time
The following snippet creates a basic Stateless Widget:
One of the benefits of creating custom widgets is that we are able to re use certain components. In this widget we need to return a Widget
from the build
function. We can simply move our application’s Scaffold
as the return of this widget
We can also have data for a stateless widget but this will be data that does not change. We can define a widget like this:
The use of final
says that this property will not be reassigned after the first assignment
To setup automatic hot reloading we need to make use of a
StatelessWidget
which is a widget that can be hot reloaded, this only works when running the application inDebug
mode via an IDE and not from the terminal. For VSCode this can be done with the Flutter Extensions and same for Android Studio
Our component now looks something like this:
Images
We can use either Network Images or Local Images. To use an image we use the Image
widget with a NetworkImage
widget as the image
property, an example of the NetworkImage would look like so:
If we want to use a locally stored image we need to do a few more things
- Download the image and save in the
assets/images
folder - Update the
pubspec.yml
file to contain the asset listing:
We can alternatively just declare the folder so that we can include all images in a folder:
- And we’ll still be able to use the image the same way. To use the images use an
AssetImage
like this:
Because using images like this is a common task, flutter also provides us with the following two methods:
- Using
Image.network
- Using
Image.asset
The above methods are pretty much just shorthands for the initial methods
Icons
An Icon Widget looks like this:
And a Button looks like this
There are of course more options for both of these but those are the basics
We can also make use of an RaisedButton with an Icon which works like so
Or an IconButton:
Containers
Container Widgets are general containers. When we don’t have anything in them they will fill the available space, when they do have children they will take up the space that the children take up.
Containers also allow us to add padding and margin to it’s children
- Padding is controlled using
EdgeInsets
- Margin is controlled using
EdgeInsets
If we don’t need the colour and margin properties and only want padding, we can use a Paddint
widget
Layouts
We have a few Widgets we can use to make layouts, some of these are:
The Row
Widget which can have children as an array of Widgets:
Out layout widgets also have the concept of a main
and cross
axis and we can control the layout along these axes. This works almost like FlexBox:
We can do the same with a Column
layout:
Refactor Menus
You can click on a widget and then click on the light bulb/refactor button, or the Flutter Overview Panel (VSCode or Android Studio) and that will allow you to do some things like:
- Move widgets around
- Add or remove wrapper Widgets
- Extract widget to function/new widget
Expanded Widgets
Expanded widgets allow us to make a Widget use up all the extra available space, this is a bit like setting a Flex grow value for a widget
This is a Wrapper widget that we can use to make a child expand into the available space in its main-axis like so:
We can also set a flex
value which defines the portion of space we want a Widget to use
These are also useful for containing an image, for example:
Sized Box Widget
The Sized Box widget allows us to simply add a spacer, we can set the height and width
The height
and width
properties are both optional
CircleAvatar Widget
Flutter also provides a widget for rendering circle shaped avatars:
Center Widget
A Center
Widget can be used to just center things:
Divider Widget
Flutter provides us with a Divider
widget which will draw a simple horizontal line. We provide a height
for the bounding box and a color
for the line.
Stateful Widgets
A Widget that contains state usually consists of two classes, one for the widget itself that extends StatefilWidget
and another for the type of the state itself which extends State<T>
. This looks something like:
We can also convert a current StatelessWidget
to a StatefulWidget
by using the recfctorings available for the class
Updating State
To update state in our component we use the inherited setState
function and use it to perform state updates. This function takes a function as a parameter and we handle state updates in this
A function with a state property _level
and a function for incrementing it’s state can be defined in our class like so:
We can then use call the _incrementLevel
from the onPressed
handler from a button, and simply render the _level
variable anywhere we want to use it
The state of the component can be updated independendently of the
setState
call, but this will not trigger a re-render of the component
Card Widget
We can use the Card
widget to render a card:
SafeArea
If we are not using a layout that is designed to keep the content in the screen we can use a SafeArea
widget which will ensure that whatever content we have on the screen does not end up under the time area, etc. See the usage below:
Routing
Routes in flutter make use of a Map
and a function that makes use of the current app context
which is used so that Widgets can get information about the app state and returns a Widget for the given route
To configure our application to use some routes we can do the following:
By default when our application loads it will start at the /
route but we can specify a different route using the initialRoute
property
To navigate to a new route we can use the Navigator
class and the methods available on that.
To push a new page on top of our existing page we can do:
To replace the current route instead of add a new page above it we can do:
And to go back to the previouse route we can do:
We can use the above to make out /home
route navigate to the /select-location
route like so:
Where clicking the button will cause a navigation.
Additionally, in our /select-location
route we use a layout with an AppBar
, what we get with this is a button that will automatically do the Navigator.pop
functioning for navigating backwards
The routing process works by adding screens on top of one another, routing allows us to navigate up and down this route. The problem with is that we may end up with a large stack of routes and we need to be careful about how we manage the stack
We can also pass data through to routes as well as use Static routenames that are associated with the widgets for the screen they use, for example if we have a Home
widget defined like so:
And the class for the data we would like to pass to the screen like this:
We can call load this screen with the relevant arguments like this:
Or just using a map. Note that if we use a map then the class itself needs to be adapted to accept the Map
type input:
We can then access the data that was passed using the context
object in our build function
Using Route Changes to Get User Data
We can make use of a Navigator push action to open a screen to allow for user input/load some data, and then we can pop back to the initial screen witht the data that was recieved. By doing this we can view the process of this input (from the intial route’s perspective) as an async
action that spans multipls routes
For example, consider something like the following flow:
- User clicks on a button like “update data” from the Home page which routes to a Data updating component
- Data component loads data/does whatever it does
- When the Data component is completed it pops back to the Home page passing the data
- The Home page uses that data to update it’s state, using
setState
Widget Lifecycles
In Flutter we have two kinds of widgets:
- Stateless
- Does not have state that changes over time
Build
function only runs once
- Stateful
- Can have state which changes over time
setState
is used to change the stateBuild
function is called after the state has been changesinitState
lifecycle method is called when the widget is created- Useful for subscribing to streams that can change our data
Dispose
is triggered when the widget is removed
In a StatefulWidger
we can override the initState
function like this:
Packages
Flutter packages can be found at pub,dev. The http
package can be used to make HTTP requests
To use the package we need to do the following:
- Add it to our
pubspec.yml
file
pubspec.yml
- Install it with:
Fetching Data (http package)
We can make use of the http
package to fetch some data from an API. for now we’ll use the JSONPlaceholder
API. To get data we can use the get
function of the http
package
The data that comes from the API is a JSON Map, we need to parse this into a Dart object manually, we can do this from the class that we want to parse the object into:
Note that it is also possible for us to make use of more automatic means of doing this (such as with Code Generation), more information on this can be found in the Flutter documentation
ListViews
We can build list views using a ListView.builder
which allows us to output a template for each item in a list automatically instead of us doing the data-template conversion on our own
The ListView.builder
widget takes an itemCount
which is the number of items in the list, and a itemBuilder
which takes the context
and index
and uses it to then render a given element