Flutter Food Delivery App – part 1 (setup)

We start off with the basic template here

Its UI for food delivery.

Notice that it has the main.dart, and the models.

Let’s make the basic changes of changing some colors. We then remove the default MyHomePage class and change the instance to HomeScreen.

We then create a new folder screens under lib, with home_screen.dart

In the home_screen.dart file, stf + tab and type in HomeScreen. Remember to import Material.dart.

Instead of the default Container, we use Scaffold to take advantage of Material design.
We create an instance of the AppBar and assign it to property appBar. Then under title, put ‘food delivery’.

Hot reload should show something like:

We then use property actions. It is a List of Widgets. Thus, we create an array of type Widget.
We then insert a TextButton in there. It will represent the Cart button.We then specify the leading button as an IconButton.

home_screen.dart

Flutter Dev Portfolio – part 5 (navigation bar)

dev portfolio part 5 download

Go to the file and type stl + tab. Code generation will give you a class that extends a StatelessWidget. Let’s type NavigationBarView as the name of the class

Now, we want to wrap the default Container with the ResponsiveBuilder. That way, our navigation bar will be responsive. Highlight Container and cmd + dot. Then select Wrap with Widget.

You will now see errors. ResponsiveBuilder constructor has property builder. There is no property child. In order to remedy this,
click on child and then cmd + dot. Select Change to ‘builder’

We need to provide a function with parameters context and size. We also want to put some code in there so we need the code to be a block body.

Then we fill in the code. We basically return a Container with static height and width, give it some padding and then add the widgets such as logo, spacer to space them out, and use InkWell to specify clicking dynamics.

Now, if you put the cursor over ‘About Me’, it’ll turn into a hand pointer and all the other click colors would be transparent.

We can extract text as local variable:

Let’s extract our Inkwell as a widget and name it NavigationBarItem

Now each item looks fine, but we need to put some padding in between them. Let’s give each menu item a certain padding.

Refactor It

So in NavigationBarView, we use NavigationBarItem like so:

it would be much better if we used a for loop.

But in order do this, let’s set things up first. We first create a class where it contains the navigationItem’s data. Specifically, text and Icon.

Once these data is set, we then use an array to denote all the menu item istances we want. Hence, using NavigationItem as data holder.

Finally, we instantiate NavigationBarItem and initialize it with the texts:

Now, the only thing left is to make sure our menu is responsive. If you try to shorten the width, you’ll see that we have an overflow.

In apps where it uses Material Design, where there is insufficient space to support tabs, drawers provide a handy alternative.

Material Drawer

We first go to flutter’s page and find drawer. We copy the code.

We then create class DrawerView and paste the drawer code in there. Let’s wrap the Drawer with a widget of ResponsiveBuilder like we’ve done before.

Remove the child property and put builder property, where it takes on a function. This function passes in context, which we do not use, and size object.
We return the Drawer in this ResponsiveBuilder.

If the passed in size IS NOT mobile, then we simply return a SizedBox, which is basically an empty box. Because we’re on standard web size and don’t want to return anything as a DrawerView.

Else, we’ll be on mobile view, which in our case, we DO want to return a Drawer.

main.dart

If you were to analyze Drawer, we added some features.

1) A nice gradient, which points to an instance of BoxDecoration
2) For ListTile, we need to put text for the item. We use a for loop to display the Widget ListTile.
ListTile’s title then gets assigned to item.text.

Now, in main.dart, we see PortfolioView as our main display. We also see a SingleChildScrollView which houses all of our UI. Let’s add a endDrawer to it like so:

main.dart

We then assign it to DrawerView().

So what this means is that we always have an endDrawer, represented by our DrawerView. If our app is NOT in mobile view, our DrawerView will return an empty box (SizedBox) as mentioned above. If its in mobile view, then we literally return a Drawer with its data and UI.
This is done when the drawer button of the navigation bar is pressed and it executes the openEndDrawer like so:

navigation_bar_view.dart

Putting a logo and icon button in Navigation bar when its in Mobile

navigation_bar_view.dart
We need to modify our navigation bar a bit.

In our Responsive Builder of our NavigationBarView, we get a size object and we differentiate it by its isMobile property.
If it is mobile, we return a Container that is of 60 height. In other words, we return a navigation bar that is solely for mobile mode.
Its width is infinity. It has a row where we have a SizedBox of width 20 for padding, then a logo, then spacer to that it separates other stuff from the logo, then an Icon Button that represents a drawer.

The icon on this IconButton is a menu, and when pressed, it will open up the EndDrawer of our context.

navigation_bar_view.dart

Notice that the context is PortfolioView. So it’s saying that we need to open up the endDrawer property.
Hence, in our PortfolioView, we specify DrawerView() for the endDrawer.

Flutter Dev Portfolio – part 4 (using extensions)

ref – https://medium.com/flutter-community/using-extension-methods-in-flutter-612b8db532ae

We extend all classes that derives from Widget with the ability of having:

1) a black colored border of width 5
2) Background color of red
3) circular border radius of 8
4) padding and margin of 16

We do so by returning a Container where we specify the decoration property.
We assign it to an instance of BoxDecoration.

From there, we simply fill in the properties of BoxDecoration.
this refers to the original Widget we are applying these attributes to. So in our case it is
AutoSizeText. When we call addContainer extension, it would return a Container where its child references
the AutoSizeText. Thus wrapping our AutoSizeText with a Container.

Then, just use like so:

Flutter Dev Portfolio – part 3 (Header Body)

Developer Portfolio Header Section

In the header_view.dart, under headerView, let’s extract the content. So the text, images, logo, etc.
All of that content is the Expanded child of the Row. let’s click on ‘Column’ of the Expanded child and Extract Widget

Then you’ll see that it has been extracted as the HeaderBody, and we instantiate it where our old code was. Notice that the logo is outside of our HeaderBody.

Now let’s put it inside our HeaderMobileView.

Now run the app, make it into mobile size and we see our header’s contents. Notice that our FlutterLogo is NOT shown because we did not include it in our HeaderBody.

We need to add some padding, logo and what not. So we need to wrap our HeaderBody inside of a column. That way, we can add padding, put a logos, and what not as children.

The code should look like this:

And now we see the logo in mobile view.

Now let’s add some padding.

Adding Auto Size Text

ref – https://pub.dev/packages/auto_size_text/install

Let’s add auto size text wdiget to our pubspec.yaml file. It is a Flutter widget that automatically resizes text to fit perfectly within its bounds. So say we specify that we want want our sentence to fit in two lines. Normally, when we start typing out the sentence, if it doesn’t fit, it’ll appear on line 2, then line 3. But with auto size text, the font size of the text will decrease so that it fits within two lines. And if we keep typing, and the widget sees that the text will start to overflow to line 3, it’ll decrease the font size some more so that our sentence will only fit in those two lines.

Installing auto size text

Flutter will then install it.

Once installed, let’s change Text to AutoSizeText like so:

isMobile property

In HeaderBody, add property isMobile by cmd + dot.

Then let’s specify that whenever we’re on mobile, we give it a smaller font or size.

In HeaderView, find the HeaderBody and pass in isMobile boolean from size object.

In HeaderMobileView, since its a mobile view, let’s always give it a true value for HeaderBody

Flutter Dev Portfolio – part 2 (Header)

Let’s download Responsive Builder.

Open up pubspec.yaml and put it right under dependencies like so:

Flutter will then attempt to install it. Once done, go to header_view.dart and double click on the Container in build function.
Wrap with widget, and type ResponsiveBuilder.

Once you typed ResponsiveBuilder, it’ll say that the property is erroneous. Naturally, there is no child property.
If you cmd + click on ResponsiveBuilder, it’ll give us this:

We see that there is a property called builder that is of type Function. And we instantly know that when we initialize a constructor for ResponsiveBuilder, we need to pass in a function for property builder. It has a named constructor property which takes in a function like so:

We need to provide a function to draw for when size is a certain height or width. Notably, if its isMobile, then we return a certain Container with its text, buttons, boxes, etc. And if its web, then we return a certain container with other laid out text, buttons, boxes, etc.

The reason why we need to provide it is because ResponsiveBuilder overrides a build function. Inside this build function, it accesses the builder property and then execute it and passes the context and sizingInformation data in. Hence, we’re providing a function for property builder. Builder then executes our provided function and passes in data for us to use.

Let’s change the property to builder by using cmd + dot.

Once it becomes builder property, let’s change the provided parameter to a function by providing two parameters. We’re not going to use context, but we will use sizingInformation. Thus, we just give ‘_’ as the context.

Its an arrow function so it returns the container automatically. We do not want that. We want to put code before returning so that we can use the size information and figure out whether we’re on mobile or web.

Thus, highlight the arrow and cmd + dot.

We analyze size object to see if it isMobile. If it is. We want to return a Container instance.

We then cmd + dot and select extract Widget. Give it the name HeaderMobileView().

Let’s specify the size of the Container for the mobile header view by saying we want it to be 90% of the mobile height. Remember that in MediaQuery, it will return width and height for when the app is resized to mobile sizes. Height will then be 9.0 of that height, and width stays the same.

Flutter Dev Portfolio – part 1 (setup)

Developer Portfolio Setup

Setup the Project

cmd + shift + p

Create a new folder on your desktop called DevPortfolio

Select the newly created folder to create your project in

Use lowercase to enter a project name.

Finally, you should see your project being created.

Delete the iOS and Android folders

The project should have been created successfully. If you see that creation process hangs because its still downloading, cancel it. Open the terminal and in the project directory type:
flutter pub get

Open up lib/main.dart.

Delete everything below class MyApp.

type stl, then tab. Let’s create a Stateless Widget called DesktopView.

should give you code generation like so:

replace Container with Scaffold, which is a Material Design Layout.

Let’s instantiate our DesktopView class for property home

Media Query returns us the size of the current media. i.e the window containing our app.

Put this at the beginning of the over-ride build function.

Run > Start Debugging

Make sure you use Chrome as your browser. Wait a while. A browser will pop up. Try to resize it. You will see that our output will be updated with the new window size.

build method will only refresh when size is changed.

Make it Scrollable

Let’s add two containers with different colors. But since our body Column is not scrollable, our 2nd container will overflow. In order to solve this, we simply our Column in SingleChildScrollView.

So now, our body is the SingleChildScrollView, which has a child of Column. Within the column, we have two containers.

Now, when you save, the project will hot save, and we’re able to see the two containers and scroll up and down.

Under lib, create folder called desktop. Then create desktop_view.dart file.

When you copy and pasted the desktop view code into our desktop_view.dart, we’ll see a bunch of errors. This is because we need to import material.dart from the flutter package.

go ahead and do: cmd + .

Now the errors will be gone.
Do the same in main.dart and import our desktop_view.dart file. So that DesktopView() can be used.

Layout of the Site

Let’s layout the text and logo for the site.
So for the first Container on top, let’s give it a static height and width. That way we control it to be smaller than the bottom (blue) region.

Then we put a Row, and specify its children to be a Column and a logo.
In the column, we have children of texts. And in order to align them, we use axis alignments.

Google Fonts

Let’s download google_fonts using our pubspec.yaml file.

Open it up and underneath dependencies, let’s download google_fonts 2.0.0

Save and in your project directory: flutter pub get
Your google fonts are now available as dependencies, instead of resources.

In our text, let’s use the following to apply montserrat font style to our text:

then put some spaces between our texts:

Create rounded red button

There is a named property in TextButton called style. We assign a ButtonStyle instance to it. Within the ButtonStyle class, there is a shape property.

We call MaterialSTateProperty.all because its just a convenience method for creating a [MaterialStateProperty] that resolves to a single value for all states. For every state of this button, we use this shape, which is 18.0 border Radius, and side of color Red.

Refactor our code

So we have two Containers. The top Container contains our text and logo that introduces this site. We want to extract this code and create a class out of it. Let’s make it into a header. Highlight the Container keyword, then cmd + period. Select extract widget.

Type in the name of this extraction, say HeaderView.

Then we see that class HeaderView is created and that our Container has been replaced by an instantiation of that HeaderView class.

Now let’s replace our DesktopView name with PortfolioView. We use rename symbol. That way, any usage of this symbol will also be changed.

Finally, let’s cut the PortfolioView code and place it into main.dart. We do this because it is the main view that we will see. The SingleChildScrollView will house the HeaderView, BodyView, sidebars and what not. We will continuously create Containers and then refactor them into different sections of the site.

main.dart

Now let’s rename the folder to header, and the file that holds the header code to header_view.dart

The event loop, async operation, and Isolate in Dart

ref –

  • https://medium.com/dartlang/dart-asynchronous-programming-isolates-and-event-loops-bffc3e296a6a
  • https://stackoverflow.com/questions/63707220/how-to-pass-arguments-besides-sendport-to-a-spawned-isolate-in-dart
  • https://medium.com/@lelandzach/dart-isolate-2-way-communication-89e75d973f34
  • https://www.javatpoint.com/dart-isolates

The Event Loop

Event loops is what makes async code possible.

In the lifetime of an app, we can’t predict when I/O, clicks, etc will happen. We don’t know when or in what order these events will happen.

Hence, a Dart or JS app needs to handle them with a single thread.
It uses an event loop. It grabs the oldest event from the event queue, and process it. Then it processes the next one..until the event queue is empty.

Taps, downloading, timer goes off…event loop processes these events one at a time. When there’s nothing left, the thread waits for the next event, or triggers the Garbage collector…etc.

In order for developers to work with the event loop, we use a Future. A Future represents a potential value, or error, that will be available at some time in the future.

Common asynchronous operations include:

– fetching data over network
– reading data from a database
– reading data from a file

Let’s take a look at a simple example.

We run down the program synchronously. W first print Begin Program. We create a Future that will return a value of 14. This Future is incomplete and is queued onto the event loop.
When this Future completes later on, it will take the then callback and process it.
Then we naturally come to print End Program.

At this point, our Future value of 14 is being processed on the event loop. It finishes and returns it to the then. We then print 14.


output:
Begin Program
Instance of ‘_Future
End Program
14

So we synchronously run the main program. If there is a Future, it is put on the event loop. The event loop starts processing after the main app. That’s why we get 14 after End Program.

In order to make sure we wait for 14 value to arrive, before hitting the end, we can use await. Remember that all awaits must be inside of an async function. Thus we add async to main.


output:
Begin Program
14
End Program

Async calls are divided into multiple parts. What comes after an await is not executed immediately because we need to wait for the Future to finish. What it happens is that the main function gets split. “Splitting Function” calls is fundamental because it avoids events on the queue to wait for the futures to finish.

i.e IF the main were executed entirely, the event loop would have been blocked until Future completed.

Let’s describe the situation with another piece of code:

We see that we have getRequest that returns a Future. This Future will return a string. Thing is, we need o wait for this Future to complete. Essentially this is whats happening:

output:
Begin Program
printName() start

When we call printName(), we print the start msg. It is an async operation so we put it on the event loop. However, because there is an await, we only place the code up to the await onto the event loop like so:

It will take the code below await and save it for later.

We then continue our execution:


output:
Begin Program
printName() start
time is 18472847387
End Program

Our program finishes executing and now we’re just waiting around while nothing is happening. However, we’re not blocking the event queue. If others have an event and gets added to the event queue, they will be processed. Our program is just waiting on getRequest to finish.

So in this way, we don’t block, and give other events a chance to be executed. Once getRequest returns, we then add the saved code (that was below await in printName()) onto the event loop. It receives the name ‘Hadoken!’, prints it and then prints that printName function has ended.


output:
Begin Program
printName() start
time is 18472847387
End Program
Hadoken!
printName() end

Future in loops

As you can see from the output, our index.dart gets processed first with all the prints. The longRunningOperation(int i) for i 0, 1, 2 gets placed onto the event loop.
Then the event loop senses that there are item on the loop, will start processing. It executes i 0, 1, and finally 2.


output:
— start main —
— procesing 0 —–
Instance of ‘Future
— procesing 1 —–
Instance of ‘Future
— procesing 2 —–
Instance of ‘Future
— end program —
longRunningOperation 0: 0
longRunningOperation 0: 10000000
longRunningOperation 0: 20000000
longRunningOperation 0: 30000000
longRunningOperation 0: 40000000
longRunningOperation 0: 50000000
longRunningOperation 0: 60000000
longRunningOperation 0: 70000000
longRunningOperation 0: 80000000
longRunningOperation 0: 90000000
done with operation 0
longRunningOperation 1: 0
longRunningOperation 1: 10000000
longRunningOperation 1: 20000000
longRunningOperation 1: 30000000
longRunningOperation 1: 40000000
longRunningOperation 1: 50000000
longRunningOperation 1: 60000000
longRunningOperation 1: 70000000
longRunningOperation 1: 80000000
longRunningOperation 1: 90000000
done with operation 1
longRunningOperation 2: 0
longRunningOperation 2: 10000000
longRunningOperation 2: 20000000
longRunningOperation 2: 30000000
longRunningOperation 2: 40000000
longRunningOperation 2: 50000000
longRunningOperation 2: 60000000
longRunningOperation 2: 70000000
longRunningOperation 2: 80000000
longRunningOperation 2: 90000000
done with operation 2

Long Running Tasks

But! Here’s a problem. A long running task on the event loop will block all the events. On a Flutter app, you can try press the other controls for an event. They won’t be processed.

In this case, we should use Isolate. The Isolate works differently in comparison of Thread. The isolates are independent workers that do not share memory, but instead interconnect by passing message over channels. Since isolates completes its task by passing message thus it need a way to serialize a message. The communication between the isolates is done by the message passing as a client and server. It helps the program to take advantage of multicore microprocessor out of the box.

In other words, Dart allows a programmer to execute a function asynchronously in a separate thread! This can be done using the Isolate class, which is in a native Dart library. A Dart Isolate is an object that executes a specific function in a newly spawned thread. The second thread can communicate with the main thread continuously.

Isolate

An Isolate have its own event loop, and memory area.
Dart Concurrent programming uses Isolates: independent workers that are similar to threads but don’t share memory, communicating only via messages.

The Isolate class was designed for message passing between threads using a non-broadcast streaming object called a ReceivePort. You first declare ReceivePort like so:

Then we create an Isolate we make use of .spawn() method in Dart. In our ReceivePort object, there is a property called sendPort. We give it to the long running function so that it can signal to us when it is done. We give the reference of sendPort inside the param array:

We use our ReceivePort instance to listen for messages. ReceivePort can be listened to just once.

When the long running function is done, it uses the reference to sendPort to send whatever data and a message to the listening receivePort that we are done.

Example

function downloadAndCompressTheInternet

main.dart

So when we start executing and executes synchronously. We spawn an Isolate that processes downloadAndCompressTheInternet. As you can see we then print ‘Future is Bright’. So this proves that our Isolate is executing already. After ‘Future is Bright’, our main Isolate will process longRunningOperation2. Then longRunningOperation1, then longRunningOperation2. So our main Isolate and spawned Isolate are running in parallel. We have a long running task that is being executed in parallel with a long running task on main Isolate.

When our spawned Isolate is done, we then send a message to main Isolate, where we created a ReceivePort, and listened for events.


output:

— start main —
— downloadAndCompressTheInternet —
longRunningOperation1: 0
Future is Bright
longRunningOperation2: 0
longRunningOperation1: 10000000
longRunningOperation2: 10000000
longRunningOperation1: 20000000
longRunningOperation1: 30000000
longRunningOperation2: 20000000
longRunningOperation1: 40000000
longRunningOperation2: 30000000
longRunningOperation1: 50000000
longRunningOperation2: 40000000
longRunningOperation1: 60000000
longRunningOperation1: 70000000
longRunningOperation2: 50000000
longRunningOperation1: 80000000
longRunningOperation2: 60000000
longRunningOperation1: 90000000
done with operation 1
[SendPort, 3]
longRunningOperation2: 70000000
longRunningOperation2: 80000000
longRunningOperation2: 90000000
done with operation 2
— end program —
— received message —
45

Async and Await

  • Use await to wait until a Future completes
  • Use multiple awaits to run Futures in sequences
  • await is only allowed inside async functions
  • use try/catch to handle exceptions
  • async/await + try/catch is a great way of working with Futures in Dart

Another Example