Usually, I don’t push tutorials on Tuesday but this UI made me really excited.

Writing content requires a lot of energy, caffeine helps a lot in this process so go ahead and make a coffee for yourself and buy one for me at https://www.buymeacoffee.com/bi3cp0Zk5

Now that you’ve got your drink go ahead and grab the code from Github.

Home Page:

I wonder how someone new to flutter sees such a UI. For me, it’s just a few widgets arranged inside a column!

Search bar:

The search field is a TextField widget for which I gave some rounded corners. Initially, I wanted to go with the OutlineInputBorder and give it some nice borderSide but I decided to set it to BorderSide.none and wrap the TextField inside a container and give the container a BoxShadow property.

The TextField also has a SuffixIcon property I set it to Icon(Icons.search).

To the right, we have a menu button that has some ugly padding but the other solution was to wrap an Icon inside a GestureDetector but then we wouldn’t have the feedback. (Using an InkWell is also as ugly).

Category Menu:

We have seen this so much that I actually just copied the widget from my other projects.

Single Recipe PageView:

Since we are already inside a Column and using a PageView widget requires setting the height I created a Widget called PageCarousel and wrapped it inside a container for which I set the height in order to take nearly half the screen.

Container( height: MediaQuery.of(context).size.height / 1.7, child: PageCarousel(), ),

Now the PageCarousel itself, as you can see we have left and right buttons to change pages, meaning the widget has to be stateful, and second we need a Stack.

In a positioned.fill we have our PageView.Builder that has our controller and our itemsCount.

The actual page content is a Container with some decorations such as a bordeRadius, boxShadow, and an Image for the background.

When setting a background image for a container I like to add a BoxFit.cover to ensure my image takes the full size of the container.

Another thing is that since we will have text show on top of this image we have to add a black filter to add some contrast to the text.

image: DecorationImage( fit: BoxFit.cover, image: NetworkImage("${recipes[id]['img']}"), colorFilter: new ColorFilter.mode( Colors.black.withOpacity(.3), BlendMode.srcOver), ),

Bottom Container:

After struggling to find a name for this section I went with Bottom Container even though it doesn’t really make a lot of sense. Thankfully I’ve got an image that will make it clear.

Note that we are still inside our Column’s children.

...List.generate(dish_cat.length, (id) { return Container( margin: EdgeInsets.symmetric(vertical: 15.0), decoration: BoxDecoration( borderRadius: BorderRadius.circular(15.0), boxShadow: [ BoxShadow(color: Colors.black38, blurRadius: 5.0) ], image: DecorationImage( fit: BoxFit.cover, colorFilter: new ColorFilter.mode( Colors.black.withOpacity(.3), BlendMode.srcOver), image: NetworkImage( dish_cat[id]['img'], ), ), ), height: 150, child: Text( "${dish_cat[id]['name']}", style: TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 17 ), ), alignment: Alignment.center, ); }).toList(),

Again I wonder what a beginner thinks about this code. (Am sure an expert would think it sucks but hey, it works!).

Creates a list with length positions and fills it with values created by calling generator for each index in the range 0 .. length - 1 in increasing order. https://api.dartlang.org/stable/2.5.0/dart-core/List/List.generate.html

In this case, we have a list of dish categories and we want to generate a container of each of them.

Our container has some vertical spacing, as well as fixed height. (It might be a bad idea if the text inside is too big we might want to add a FittedBox).

Details Page:

Thankfully this page is much “easier”, or not.

Here we’ve got 3 elements inside a Stack. First is a Positioned.Fill NetworkImage which represents the background. Second is an IconButton positioned at 15 from the top and right corner. The third is a Container positioned at 15 from left, right, and bottom.

Our focus goes to this Container.

height: MediaQuery.of(context).size.height/1.35,

I found out that setting the height to a “fixed” value works a bit better. Because we aren’t sure if this container is tall enough I gave it a SingleChildScrollView.

Other than that it’s mostly easy stuff, a few rows, a horizontal list for the ingredients, a ratingbar widget that I wrote in 18 lines, and a GridView.count which is probably interesting to go through.

GridView.count( crossAxisCount: 2, shrinkWrap: true, childAspectRatio: 5, children: List.generate( recipes[id]['ingredients'].length, (f) { return Row( children: <Widget>[ Text( "${recipes[id]['ingredients'][f]}: ", style: TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 17, ), ), Text( "$f kg", style: TextStyle( color: Colors.white, fontSize: 17, ), ) ], ); }, ), ),

Since I want to have 2 columns grid I’ve set the crossAxisCount: 2. To avoid it taking too much space I enabled shrinkWrap.

Children are generated using the same List.generate function. The children here are Rows of Text, the first part is the ingredient and the second is its weight.

Finish!

Overall that’s it, there are things I judged to be too simple to go through. Probably out of laziness. However! if you have any questions please leave them in the comment section or send a DM on Twitter.