In the previous tutorial we explored the most basic Widget’s that you will need while developing your Flutter app. In this piece we are going to learn a few more widgets that will help you add some extra flare to your application.

Before we begin !

Breaking our widget into smaller widgets

We don’t want to keep adding all our widgets into a single class. It will become unmaintainable, unreadable, and in general is just a very bad practice. Luckily, creating your own widgets is very easy in Flutter. All you have to do is define a class, extend StatelessWidget or StatefulWidget and override the build method. It’s much easier to understand in code.

In the code below you can see we have everything into one single class called MyApp. Let’s break it into two widgets.

Large Widget class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( theme: ThemeData( primaryColorBrightness: Brightness.light, ), title: 'Hello Flutter', home: Scaffold( appBar: AppBar(), body: Center( child: Text('Hello'), ), ), debugShowCheckedModeBanner: false, ); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class MyApp extends StatelessWidget { @ override Widget build ( BuildContext context ) { return MaterialApp ( theme : ThemeData ( primaryColorBrightness : Brightness . light , ) , title : 'Hello Flutter' , home : Scaffold ( appBar : AppBar ( ) , body : Center ( child : Text ( 'Hello' ) , ) , ) , debugShowCheckedModeBanner : false , ) ; } }

In the code above you can see we have everything into one single class called MyApp. Let’s break it into two widgets.

Broken Widget class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( theme: ThemeData( primaryColorBrightness: Brightness.light, ), title: 'Hello Flutter', home: Home(), debugShowCheckedModeBanner: false, ); } } class Home extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(), body: Center( child: Text('Hello'), ), ); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class MyApp extends StatelessWidget { @ override Widget build ( BuildContext context ) { return MaterialApp ( theme : ThemeData ( primaryColorBrightness : Brightness . light , ) , title : 'Hello Flutter' , home : Home ( ) , debugShowCheckedModeBanner : false , ) ; } } class Home extends StatelessWidget { @ override Widget build ( BuildContext context ) { return Scaffold ( appBar : AppBar ( ) , body : Center ( child : Text ( 'Hello' ) , ) , ) ; } }

As you can see we create a new widget called Home, returned the Scaffold widget in the build method. You can place the Home widget into its own file, which you will eventually do with most of your widgets. So from now I will only be showing you code inside the Home widget and skip the MyApp widget.

Inkwell

Inkwell lets you add material ripple on tap of any widget. Just wrap on widget with Inkwell and tap it to see the effect!

Inkwell class Home extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(), body: Center( child: InkWell( onTap: () {}, child: Text( 'Hello', style: TextStyle( fontSize: 24.0, ), ), ), ), ); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Home extends StatelessWidget { @ override Widget build ( BuildContext context ) { return Scaffold ( appBar : AppBar ( ) , body : Center ( child : InkWell ( onTap : ( ) { } , child : Text ( 'Hello' , style : TextStyle ( fontSize : 24.0 , ) , ) , ) , ) , ) ; } }

Make sure you provide a callback (non-null) in the onTap property of Inkwell, else nothing will happen when you tap on the text.

Inkwell provides a lot of customisation options as well.

Inkwell Customise class Home extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(), body: Center( child: InkWell( splashColor: Colors.green, highlightColor: Colors.red, borderRadius: BorderRadius.circular(4.0), onTap: () {}, child: Text( 'Hello', style: TextStyle( fontSize: 24.0, ), ), ), ), ); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Home extends StatelessWidget { @ override Widget build ( BuildContext context ) { return Scaffold ( appBar : AppBar ( ) , body : Center ( child : InkWell ( splashColor : Colors . green , highlightColor : Colors . red , borderRadius : BorderRadius . circular ( 4.0 ) , onTap : ( ) { } , child : Text ( 'Hello' , style : TextStyle ( fontSize : 24.0 , ) , ) , ) , ) , ) ; } }

We have change the splash and highlight color to green and red respectively, and change add rounded borders of 4.0 pixels.

Material

A lot of widgets in Flutter such as Scaffold, Card, etc use the Material widget under the hood. Material widget provides you with shadows, background color, borders and much more.

Below we have a simple Text inside a Container which is centred with the Center widget.

class Home extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(), body: Center( child: Container( padding: EdgeInsets.all(24.0), child: Text('Material'), ), ), ); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Home extends StatelessWidget { @ override Widget build ( BuildContext context ) { return Scaffold ( appBar : AppBar ( ) , body : Center ( child : Container ( padding : EdgeInsets . all ( 24.0 ) , child : Text ( 'Material' ) , ) , ) , ) ; } }

Now let’s wrap the Container with a Material widget and providing values to its various properties.

class Home extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(), body: Center( child: Material( child: Container( padding: EdgeInsets.all(24.0), child: Text('Material'), ), color: Colors.red, shape: CircleBorder(), elevation: 6.0, shadowColor: Colors.red, ), ), ); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Home extends StatelessWidget { @ override Widget build ( BuildContext context ) { return Scaffold ( appBar : AppBar ( ) , body : Center ( child : Material ( child : Container ( padding : EdgeInsets . all ( 24.0 ) , child : Text ( 'Material' ) , ) , color : Colors . red , shape : CircleBorder ( ) , elevation : 6.0 , shadowColor : Colors . red , ) , ) , ) ; } }

As you can see we have used the Material widget and various of its properties. While almost all of them are self-explanatory, let’s go through them and what they do.

elevation : Elevation gives a shadow to the child Widget.

: Elevation gives a shadow to the child Widget. shadowColor : The color that should be used as when elevation is provided.

: The color that should be used as when elevation is provided. color : Set the background color of the child Widget.

: Set the background color of the child Widget. shape: The shape that Material properties should take. (Take note: It doesn’t change shape of the underlying widget.)

There are many more properties that Material widget provides, take a look at the Material documentation.

Never miss a post from TheTechnoCafe

Scaffold

You might have seen Scaffold being used in the previous examples and might be wondering what exactly does this widget does. Well, Scaffold has a lot to offer, and I mean a lot! Whenever you build a Flutter application, you are going to rely on Scaffold a lot.

For starters, whenever you create a new page, Scaffold provides you something to being with. It allows to add an AppBar, NavigationDrawer, Bottom Navigation Bar, Floating Action Button, Bottom Sheet to name a few with very few lines of code. I won’t be covering all the things that Scaffold is capable, that is complete another article in itself. Let’s check out the most common things that you will use Scaffold for, such as AppBar.

Flutter Scaffold class Home extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Hello Scaffold'), ), body: Center( child: Text('Hello Scaffold'), ), ); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 class Home extends StatelessWidget { @ override Widget build ( BuildContext context ) { return Scaffold ( appBar : AppBar ( title : Text ( 'Hello Scaffold' ) , ) , body : Center ( child : Text ( 'Hello Scaffold' ) , ) , ) ; } }

In the above code snippet you can see that we have added a basic Scaffold with AppBar. Also we have provided a title in the AppBar. Let’s add a FloatingActionButton.

Scaffold Floating Action Button class Home extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Hello Scaffold'), ), floatingActionButton: FloatingActionButton( child: Icon(Icons.add), onPressed: () { print('Floating!'); }, ), body: Center( child: Text('Hello Scaffold'), ), ); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Home extends StatelessWidget { @ override Widget build ( BuildContext context ) { return Scaffold ( appBar : AppBar ( title : Text ( 'Hello Scaffold' ) , ) , floatingActionButton : FloatingActionButton ( child : Icon ( Icons . add ) , onPressed : ( ) { print ( 'Floating!' ) ; } , ) , body : Center ( child : Text ( 'Hello Scaffold' ) , ) , ) ; } }

Add a FloatingActionButton is just as simple as using the floatingActionButton provided by Scaffold and using the inbuilt FloatingActionButton widget. The child of FloatingActionButton is Icon. Flutter provides a lot of inbuilt icons, to use them you have to use Icon widget and pass in one of the icons provided in Icons.

Scaffold has a lot to offer, covering everything here is not the intent of this series. I recommend to give a glance on the Scaffold documentation. I will do a deep dive on Scaffold in one of my future posts so keep checking the website for new content.

Container

In my opinion Container is one of the most used Widget, probably to the extent that sometimes I overuse it in my applications, but that is because it has so much to offer. From adding margins/padding to a Widget to defining your own shadows and gradients, Container can do a lot.

Below is a TextView surrounded by a basic Container with no parameter.

class Home extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Hello Container'), ), body: Center( child: Container( child: Text('Hello Container'), ), ), ); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Home extends StatelessWidget { @ override Widget build ( BuildContext context ) { return Scaffold ( appBar : AppBar ( title : Text ( 'Hello Container' ) , ) , body : Center ( child : Container ( child : Text ( 'Hello Container' ) , ) , ) , ) ; } }

Now lets add padding of 16.0, and set the background color to red.

Container with padding and color class Home extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Hello Container'), ), body: Center( child: Container( child: Text('Hello Container'), padding: EdgeInsets.all(16.0), color: Colors.red, ), ), ); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Home extends StatelessWidget { @ override Widget build ( BuildContext context ) { return Scaffold ( appBar : AppBar ( title : Text ( 'Hello Container' ) , ) , body : Center ( child : Container ( child : Text ( 'Hello Container' ) , padding : EdgeInsets . all ( 16.0 ) , color : Colors . red , ) , ) , ) ; } }

You can even set width and height (although it is not recommend all the time, as you layout should be flexible and hardcoding values all the time is a bad idea) and give an alignment to the child inside.

Container with width and height and alignment class Home extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Hello Container'), ), body: Center( child: Container( width: 200.0, height: 100.0, alignment: Alignment.bottomRight, child: Text('Hello Container'), padding: EdgeInsets.all(16.0), color: Colors.red, ), ), ); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Home extends StatelessWidget { @ override Widget build ( BuildContext context ) { return Scaffold ( appBar : AppBar ( title : Text ( 'Hello Container' ) , ) , body : Center ( child : Container ( width : 200.0 , height : 100.0 , alignment : Alignment . bottomRight , child : Text ( 'Hello Container' ) , padding : EdgeInsets . all ( 16.0 ) , color : Colors . red , ) , ) , ) ; } }

As you can see our Container is now 200.0 wide and 100.0 in height, and the content inside is aligned to the bottom right using the alignment property.

Container allows you to provide border, border-radius, shadow, gradient background to your child Widget. All of this will be covered in a separate article. Meanwhile do check out the documentation on Container widget.

GestureDetector

Building a good looking UI is awesome, but a UI isn’t complete without interactivity. This is where GestureDetector comes in, as the name suggests, GestureDetector allows you to detect gestures of different kind. Let’s see an example.

GestureDetector class Home extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( body: Center( child: GestureDetector( onTap: () { print('Tapped!'); }, child: Container( child: Text( 'Tap Me!', style: TextStyle( fontSize: 28.0, ), ), padding: EdgeInsets.all(16.0), ), ), ), ); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Home extends StatelessWidget { @ override Widget build ( BuildContext context ) { return Scaffold ( body : Center ( child : GestureDetector ( onTap : ( ) { print ( 'Tapped!' ) ; } , child : Container ( child : Text ( 'Tap Me!' , style : TextStyle ( fontSize : 28.0 , ) , ) , padding : EdgeInsets . all ( 16.0 ) , ) , ) , ) , ) ; } }

You can see above we have a Text inside a Container with padding of 16.0, the Container is surrounded by a GestureDetector that has its onTap property set. Whenever you tap on the Text, ‘Tapped!’ will is printed in the console. Let’s add two more properties in GestureDetector.

GestureDetector onTap onDoubleTap onLongPress class Home extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( body: Center( child: GestureDetector( onTap: () { print('Tapped!'); }, onDoubleTap: () { print('Double Tapped!'); }, onLongPress: () { print('Long Pressed!'); }, child: Container( child: Text( 'Tap Me!', style: TextStyle( fontSize: 28.0, ), ), padding: EdgeInsets.all(16.0), ), ), ), ); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class Home extends StatelessWidget { @ override Widget build ( BuildContext context ) { return Scaffold ( body : Center ( child : GestureDetector ( onTap : ( ) { print ( 'Tapped!' ) ; } , onDoubleTap : ( ) { print ( 'Double Tapped!' ) ; } , onLongPress : ( ) { print ( 'Long Pressed!' ) ; } , child : Container ( child : Text ( 'Tap Me!' , style : TextStyle ( fontSize : 28.0 , ) , ) , padding : EdgeInsets . all ( 16.0 ) , ) , ) , ) , ) ; } }

We have provided callbacks for onDoubleTap and onLongPress, now if you tap, double tap and long press on the Text you will see the following output in console.

Progress Indicators

While you can create your own loading indicators in several different ways. Flutter provides some out of the box progress loading widgets.

CircularProgressIndicator

CircularProgressIndicator is probably the simplest loading indicator you can use, as the name suggests its a progress indicator that is circular. It’s a very easy to use widget.

CircularProgressIndicator class Home extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( body: Center( child: CircularProgressIndicator(), ), ); } } 1 2 3 4 5 6 7 8 9 10 class Home extends StatelessWidget { @ override Widget build ( BuildContext context ) { return Scaffold ( body : Center ( child : CircularProgressIndicator ( ) , ) , ) ; } }

LinearProgressIndicator

LinearProgressIndicator is the cousin of CircularProgressIndicator that is horizontal in shape.

LinearProgressIndicator class Home extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( body: Center( child: LinearProgressIndicator(), ), ); } } 1 2 3 4 5 6 7 8 9 10 class Home extends StatelessWidget { @ override Widget build ( BuildContext context ) { return Scaffold ( body : Center ( child : LinearProgressIndicator ( ) , ) , ) ; } }

You can give a value to the LinearProgressIndicator it will only fill the percentage of bar. You can fill 40% of the bar like this:

class Home extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( body: Center( child: LinearProgressIndicator( value: 0.40, ), ), ); } } 1 2 3 4 5 6 7 8 9 10 11 12 class Home extends StatelessWidget { @ override Widget build ( BuildContext context ) { return Scaffold ( body : Center ( child : LinearProgressIndicator ( value : 0.40 , ) , ) , ) ; } }

Tons and tons of Widgets!

Everything in Flutter is a Widget!

That statement is pretty true as you learn more and more about the framework. There are so many Widgets in Flutter that it isn’t possible to cover them all in a handful of articles, and you don’t need to learn all of them.

I have shown you the most common widgets that you will use, regardless the type of application you are building. I will try my best to cover as many widgets possible in future articles, but the best way to learn about widgets is to explore on your own!

Next Tutorial – Flutter Crash Course – 5 – Building Lists and Pages >>

<< Previous Tutorial – Flutter Crash Course – 3 – Dive into Flutter Widgets