Detecting Tap Gestures

In this section, we will create the navigation bar for our home page by using the widgets RawMaterialButton, TextField, Icon, and CircleAvatar. We'll also learn how the GestureDetector widget can be used to detect taps and run code when gestures are recognized.

Get Started

We'll begin by making some modifications to the Scaffold widget.

  • First, wrap the RecentCourseCard widget in a Column widget. Since our UI components will be arranged vertically, the Column widget is best for this.
Column(
  children: [
    RecentCourseCard(
      course: recentCourses[2],
    ),
  ],
),
  • Next, add a color to the parent Container widget. This will serve as the background color for our screen
Container(
  color: kBackgroundColor,
  child: Column(
		children: [
			RecentCourseCard(
				course: recentCourses[2],
			),
		],
	),
),
  • Finally, we need to switch the Safe Area and Container widgets since the Safe Area widget is not letting the color extend behind the notch.
Scaffold(
  body: Container(
    color: kBackgroundColor,
    child: SafeArea(
      child: Column(
        children: [
          HomeScreenNavBar(),
          RecentCourseCard(
            course: recentCourses[2],
          ),
        ],
      ),
    ),
  ),
),
Flutter_07_01

Create the Home Screen Navigation Bar

At the bottom of main.dart, type the shortcut stless to create a new stateless widget.

  • Name this new widget HomeScreenNavBar
class HomeScreenNavBar extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container();
  }
}
  • Back inside the MyApp class, add the new HomeScreenNavBar widget right before the RecentCourseCard widget
Column(
  children: [
    HomeScreenNavBar(),
    RecentCourseCard(
      course: recentCourses[2],
    ),
  ],
),
Flutter_07_02

Configure the HomeScreenNavBar Constraints

Inside our HomeScreenNavBar widget, we need to arrange our widgets horizontally. The best widget for this type of layout is the Row widget.

  • Replace Container with Row
return Row();
  • Set the main axis (horizontal) alignment for the Row widget
return Row(
	mainAxisAlignment: MainAxisAlignment.spaceBetween
);
  • Wrap the Row widget in a Padding widget with a padding of 20.0 pixels
return Padding(
  padding: EdgeInsets.all(20.0),
  child: Row(
    mainAxisAlignment: MainAxisAlignment.spaceBetween,
  ),
);
Flutter_07_03

Build the Sidebar Button

The first widget we will build in our navigation bar is the sidebar button. Add the children argument to the Row widget and inside the list, we'll be using the RawMaterialButton widget.

Row(
  mainAxisAlignment: MainAxisAlignment.spaceBetween,
  children: [
    RawMaterialButton(
      onPressed: () {
        print("Sidebar button pressed");
      },
      materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
      highlightColor: Colors.transparent,
      splashColor: Colors.transparent,
      constraints: BoxConstraints(
        maxWidth: 40.0,
        maxHeight: 40.0,
      ),
      child: Container(
        decoration: BoxDecoration(
          color: Colors.white,
          borderRadius: BorderRadius.circular(14.0),
          boxShadow: [
            BoxShadow(
              color: kShadowColor,
              offset: Offset(0, 12),
              blurRadius: 16.0,
            )
          ],
        ),
        child: Image.asset(
          'asset/icons/icon-sidebar.png',
          color: kPrimaryLabelColor,
        ),
        padding: EdgeInsets.symmetric(
          horizontal: 12.0,
          vertical: 14.0,
        ),
      ),
    ),
  ],
),
Flutter_07_04

Extract the SidebarButton Widget

Before we add any more widgets to our navigation bar, it will be helpful to refactor a little bit by extracting the entire RawMaterialButton widget into its own separate widget.

  • First, place your cursor on the RawMaterialButton keyword
  • Select Flutter Outline in the right side pane
  • CONTROL+Select on the RawMaterialButton keyword and select Extract Widget
  • Name this new widget SidebarButton
Flutter_S07_05

Creating the Notification Button

The notification button will be created in the same Row widget after the SidebarButton widget. We'll use a simply Icon widget to build this. Place the following code after the SidebarButton inside the list of row children.

Icon(
  Icons.notifications,
  color: kPrimaryLabelColor,
),
Flutter_S07_06

Creating the Profile Button

The profile button will be built using the CircleAvatar widget. It will be created right after the notification Icon widget that was just created. However, to avoid both the notification button and the profile button from being grouped so close together, we'll need to add a SizedBox widget to provide some spacing between the two widgets. So inside the same list, type the following right after Icon.

SizedBox(
  width: 16.0,
),
CircleAvatar(
  radius: 18.0,
  backgroundImage: AssetImage('asset/images/profile.jpg'),
),
Flutter_07_07

Create the SearchFieldWidget

At the bottom of main.dart, create a new stateless widget. We'll call this SearchFieldWidget. You can do this by using the shortcut stless.

class SearchFieldWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

We'll also need to add this widget to the Row widget right before the Icon widget and after the SidebarButton widget.

Row(
  mainAxisAlignment: MainAxisAlignment.spaceBetween,
  children: [
    SidebarButton(),
    SearchFieldWidget(),
    Icon(
      Icons.notifications,
      color: kPrimaryLabelColor,
    ),
    SizedBox(
      width: 16.0,
    ),
    CircleAvatar(
      radius: 18.0,
      backgroundImage: AssetImage('asset/images/profile.jpg'),
    ),
  ],
),
Flutter_S07_08

Designing the SearchFieldWidget

The SearchFieldWidget is a creation of multiple widgets nested amongst each other. Some items to note here are such.

  • The Expanded widget will be used to stretch its children to the maximum space available
  • The BoxShadow inside BoxDecoration takes three properties: color, offset, and blur radius. The Offset will take a dx (change in the x-axis) value of 0 pixels and dy (change in the y-axis) value of 16 pixels.
  • The TextField widget automatically takes in a user's text input. Most of the code inside this widget is to customize its style
  • Just like how Container widgets are decorated with BoxDecorationTextField widgets are decorated using InputDecoration widgets. This will include the text field icon, the border, and the placeholder
  • The onChanged argument will be run every time the text inside the text field changes. As of now, we are simply printing the contents to the console.
return Expanded(
  child: Padding(
    padding: EdgeInsets.only(left: 12.0, right: 33.0),
    child: Container(
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(14.0),
        boxShadow: [
          BoxShadow(
            color: kShadowColor,
            offset: Offset(0, 12),
            blurRadius: 16.0,
          )
        ],
      ),
      child: Padding(
        padding: EdgeInsets.symmetric(horizontal: 16.0),
        child: TextField(
          cursorColor: kPrimaryLabelColor,
          decoration: InputDecoration(
            icon: Icon(
              Icons.search,
              color: kPrimaryLabelColor,
              size: 20.0,
            ),
            border: InputBorder.none,
            hintText: "Search for courses",
            hintStyle: kSearchPlaceholderStyle,
          ),
          style: kSearchTextStyle,
          onChanged: (newText) {
            print(newText);
          },
        ),
      ),
    ),
  ),
);
Flutter_07_09