Flutter docs Navigation

Flutter docs Navigation

·

11 min read

This blog is very long to read, I have summarized every bit made the video of it, and have provided all the code on GitHub, check them out:

GitHub: https://github.com/raman04-byte/flutter_docs

Video: https://youtu.be/FwBSQoKzEkE

In this blog, we are going to make Flutter Docs NavigationBar

In this whole project, I am going to follow MVC architecture

Let's talk about MVC architecture first and get to know what it is:

MVC (Model-View-Controller) is a popular architectural pattern used in software development to separate the concerns of an application into three distinct components: Model, View, and Controller. While Flutter primarily promotes using the "Flutter way" architecture (similar to the MVVM pattern), you can still implement an MVC-like structure if it aligns with your project's requirements and design principles.

Here's a brief explanation of how you can apply the MVC architecture in Flutter:

  1. Model (M):

    • The Model represents the application's data and business logic. It is responsible for data storage, retrieval, and manipulation.

    • You can create Dart classes in a Flutter app to define your data models. These classes represent the data you are working with, such as user profiles, items in a shopping cart, or any other application-specific data.

    • Model classes do not depend on the UI or user interactions. They should be as independent and reusable as possible.

  2. View (V):

    • The View is responsible for rendering the user interface (UI) and displaying data from the Model to the user. In Flutter, this is typically done using widgets.

    • Each screen or page in your Flutter app can have its own View component, which includes the UI layout and design.

    • Widgets like Text, Image, ListView, and custom widgets can be used to visually represent your app's screens.

  3. Controller (C):

    • The Controller acts as an intermediary between the Model and the View. It handles user input, processes requests, and updates the Model or View accordingly.

    • In Flutter, you can use state management techniques like StatefulWidget or third-party state management libraries (e.g., Provider, Bloc, Riverpod) to implement the Controller part of MVC.

    • The Controller can handle user interactions, validate input, make API calls, and update the Model and View as needed.

Let's go to the development state of this navBar application:

First, let's take a look on pubspec.yaml file:

Use the latest version of the package.

Let's look for the folder structure that we are going to follow during this development of the Navigation bar:

Let's look into main.dart file:

import 'package:flutter/material.dart';
import 'app.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(const MyApp());
}

This Flutter code is the entry point for a mobile application. It imports necessary Flutter material design widgets and a custom "app.dart" file. The main function ensures proper initialization of the Flutter framework with WidgetsFlutterBinding.ensureInitialized(). It then launches the app by running an instance of the MyApp widget using runApp(const MyApp()). The MyApp widget, defined in "app.dart," is responsible for constructing the user interface of the application. This code sets the stage for a Flutter app, ensuring framework readiness and establishing the root widget, which is essential for building the app's graphical user interface.

Clone the repo from GitHub and try to understand the architecture

Let's dive into the development of the navigation bar:

homepage.dart:

import 'package:docs_flutter/feature/home/template/home_template.dart';
import 'package:flutter/material.dart';

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: HomeTemplate(),
    );
  }
}
  • The code defines a Flutter widget for a home page.

  • It imports "home_template.dart" for the home page's template and Flutter material design widgets.

  • The HomePage class extends StatefulWidget, representing the home page.

  • The createState method returns an instance of the _HomePageState class to manage state.

  • _HomePageState manages the mutable state of HomePage.

  • The build method returns a Scaffold with a HomeTemplate as the body, structuring the home page's content.

hometemplate.dart :

import 'package:docs_flutter/feature/home/template/home_row.dart';
import 'package:flutter/material.dart';

class HomeTemplate extends StatefulWidget {
  const HomeTemplate({super.key});

  @override
  State<HomeTemplate> createState() => _HomeTemplateState();
}

class _HomeTemplateState extends State<HomeTemplate> {
  @override
  Widget build(BuildContext context) {
    return const Column(
      children: [
        HomeRow(),
      ],
    );
  }
}
  • This Dart code defines a Flutter widget named HomeTemplate for a home page.

  • It imports the "home_row.dart" file and Flutter's material design widgets.

  • The HomeTemplate class extends StatefulWidget, representing the template for the home page.

homerow.dart :

class HomeRow extends StatefulWidget {
  const HomeRow({super.key});

  @override
  State<HomeRow> createState() => _HomeRowState();
}

class _HomeRowState extends State<HomeRow> {
  TextEditingController textController = TextEditingController();
  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: EdgeInsets.symmetric(
        vertical: Dimensions.scaleH(10),
        horizontal: Dimensions.scaleW(5),
      ),
      child: Row(
        children: [
          MouseRegion(
            cursor: SystemMouseCursors.click,
            child: SvgPicture.asset(
              Assets.image1,
              height: Dimensions.scaleH(37),
            ),
          ),
          const Spacer(),
          DropButton(
            items: const [
              'Mobile',
              'Web',
              'Desktop',
              'Embedded',
            ],
            title: 'Multi-Platform',
            height: Dimensions.scaleH(40),
            width: Dimensions.scaleH(135),
            menuHeight: Dimensions.scaleH(35),
          ),
          DropButton(
            items: const [
              'Learn',
              'Flutter Favorites',
              'Packages',
              'Monetization',
              'Games',
              'News'
            ],
            title: 'Development',
            height: Dimensions.scaleH(40),
            width: Dimensions.scaleW(33),
            menuHeight: Dimensions.scaleH(40),
          ),
          DropButton(
            items: const [
              'Community',
              'Events',
              'Culture',
            ],
            title: 'Ecosystem',
            height: Dimensions.scaleH(40),
            width: Dimensions.scaleW(25),
            menuHeight: Dimensions.scaleH(35),
          ),
          Padding(
            padding: EdgeInsets.symmetric(
              horizontal: Dimensions.scaleW(5),
            ),
            child: Text(
              'Showcase',
              style: TextStyle(
                color: Colors.black,
                fontSize: Dimensions.scaleH(15),
              ),
            ),
          ),
          DropButton(
            items: const [
              "What's new",
              'Editor Support',
              'Hot reload',
              'Profiling',
              'Install Flutter',
              'DevTools',
              'Cookbook',
              'Codelabs'
            ],
            title: 'Docs',
            height: Dimensions.scaleH(40),
            width: Dimensions.scaleW(30),
            menuHeight: Dimensions.scaleH(35),
          ),
          AnimSearchBar(
            helpText: 'Search',
            width: Dimensions.scaleW(40),
            textController: textController,
            prefixIcon: const Icon(
              Icons.search,
              color: Color(0xFF6e7274),
            ),
            onSuffixTap: () {
              setState(() {
                textController.clear();
              });
            },
            onSubmitted: (value) {},
          ),
          const AppBarIcon(
            icon: SimpleIcons.twitter,
          ),
          const AppBarIcon(
            icon: SimpleIcons.youtube,
          ),
          const AppBarIcon(
            icon: SimpleIcons.medium,
          ),
          const AppBarIcon(
            icon: SimpleIcons.github,
          ),
          MouseRegion(
            cursor: SystemMouseCursors.click,
            child: Container(
              height: Dimensions.scaleH(38),
              width: Dimensions.scaleW(30),
              alignment: Alignment.center,
              decoration: const BoxDecoration(
                color: Color(0xFF1389fd),
              ),
              child: Text(
                "Get started",
                style: TextStyle(
                    color: Colors.white,
                    fontSize: Dimensions.scaleH(15),
                    fontWeight: FontWeight.w500),
              ),
            ),
          )
        ],
      ),
    );
  }
}

Let's break down the code and explain each part:

  1. class HomeRow extends StatefulWidget: This defines a class called HomeRow, which extends StatefulWidget. This means that HomeRow is a stateful widget and can have mutable state that can change over time.

  2. const HomeRow({super.key});: This is the constructor for the HomeRow class. It takes a named parameter key, which is not required, and sets it to super.key. The super keyword is used to refer to the base class constructor, which is part of the StatefulWidget class.

  3. @override State<HomeRow> createState() => _HomeRowState();: This method is an override of the createState method. It is used to create and return an instance of the state class for this widget, which is _HomeRowState. In Flutter, the state class is where you define the mutable state and the build method to describe the UI for the widget.

  4. class _HomeRowState extends State<HomeRow>: This defines the state class for the HomeRow widget. It extends State<HomeRow> and is responsible for maintaining the mutable state of the widget.

  5. TextEditingController textController = TextEditingController();: This line declares an instance of TextEditingController called textController. This controller is typically used to manage and control text input widgets like text fields.

  6. @override Widget build(BuildContext context) { ... }: This is the build method of the state class. It is called whenever the widget needs to be built or rebuilt. Inside this method, you define the layout and structure of the widget's UI.

    • The UI structure consists of a Padding widget that wraps a Row widget.

    • Inside the Row, there are several child widgets, including MouseRegion, SvgPicture, Spacer, DropButton, Text, AnimSearchBar, AppBarIcon, and Container widgets.

    • These widgets represent various UI elements, such as icons, drop-down menus, search bars, and buttons, and they are arranged in a row layout.

    • Various properties like dimensions, text styles, and colors are customized based on the Dimensions class or other constants defined in the application.

The HomeRow widget is intended to be part of a larger Flutter app's user interface and provides a row of interactive and informational elements for the user to interact with. The specifics of how this widget behaves and interacts with the rest of the app would depend on the rest of the code in the app.

drop_button.dart :

import 'package:dropdown_button2/dropdown_button2.dart';
import 'package:flutter/material.dart';

import '../../../constants/dimes.dart';

class DropButton extends StatefulWidget {
  final String title;
  final List<String> items;
  final double height;
  final double menuHeight;
  final double width;
  const DropButton({
    super.key,
    required this.items,
    required this.title,
    required this.height,
    required this.width,
    required this.menuHeight,
  });

  @override
  State<DropButton> createState() => _DropButtonState();
}

class _DropButtonState extends State<DropButton> {
  String? selectedValue;
  @override
  Widget build(BuildContext context) {
    return DropdownButton2(
      underline: Container(
        color: Colors.white,
      ),
      isExpanded: true,
      hint: Text(
        widget.title,
        style: TextStyle(
          color: const Color(0xff000000),
          fontSize: Dimensions.scaleH(15),
        ),
      ),
      items: widget.items
          .map(
            (String item) => DropdownMenuItem<String>(
              value: item,
              child: Text(
                item,
                style: TextStyle(
                  fontSize: Dimensions.scaleH(13),
                ),
              ),
            ),

          )
          .toList(),
      value: selectedValue,
      onChanged: (String? value) {
        setState(() {});
      },
      buttonStyleData: ButtonStyleData(
        height: widget.height,
        width: widget.width,
      ),
      menuItemStyleData: MenuItemStyleData(
        height: widget.menuHeight,
      ),
    );
  }
}

Let's break down the code and understand how this widget works:

  1. Import Statements:

    • import 'package:dropdown_button2/dropdown_button2.dart' imports the DropdownButton2 widget from the dropdown_button2 package.

    • import 'package:flutter/material.dart' imports Flutter's material design widgets, which are used to create the user interface.

  2. Import Custom Constants:

    • import '../../../constants/dimes.dart' imports constants from a local file, likely containing constants related to dimensions.
  3. DropButton Class:

    • This class is a StatefulWidget, which means it can hold mutable state.

    • Constructor: The constructor of this widget takes several parameters:

      • key: A named parameter for the widget's key. However, there is a syntax issue here, as super.key is not the correct way to set the key. Instead, you can directly pass Key to the constructor and set it as key: key.

      • title: A required string parameter representing the title of the dropdown.

      • items: A required list of strings representing the items to display in the dropdown.

      • height: A required double parameter representing the height of the button part of the dropdown.

      • menuHeight: A required double parameter representing the height of the dropdown menu.

      • width: A required double parameter representing the width of the button part of the dropdown.

    • The constructor is used to configure the initial properties of the DropButton.

  4. _DropButtonState Class:

    • This class represents the state for the DropButton widget and extends State<DropButton>.

    • It has a selectedValue property to store the currently selected item in the dropdown. It's initialized as nullable (String?) because initially, no item is selected.

    • The build the method is overridden to define the UI of the DropButton widget.

  5. build Method:

    • Inside the build method, a DropdownButton2 widget is created. DropdownButton2 is likely a custom dropdown widget from the dropdown_button2 package.

    • The properties of the DropdownButton2 widget is configured as follows:

      • underline: This property defines the style of the underline, and in this case, it's set to a white color.

      • isExpanded: The dropdown should expand to fill the available horizontal space.

      • hint: This is the text displayed when no item is selected in the dropdown. It uses the title property from the widget and a specific text style.

      • items: The list of items to display in the dropdown menu. The items are constructed from the widget.items list and have specific text styles.

      • value: This is set to the selectedValue from the widget's state, indicating the currently selected item.

      • onChanged: This function is called when an item is selected. It is currently empty and doesn't do anything.

      • buttonStyleData: This property defines the height and width of the dropdown button.

      • menuItemStyleData: This property defines the height of each item in the dropdown menu.

This DropButton widget allows you to create customized dropdown buttons with various configuration options, making it easier to integrate dropdowns with specific styles and behaviors into your Flutter app.

appbar_icon.dart :

import 'package:docs_flutter/constants/dimes.dart';
import 'package:flutter/material.dart';

class AppBarIcon extends StatefulWidget {
  final IconData icon;
  const AppBarIcon({super.key, required this.icon});

  @override
  State<AppBarIcon> createState() => _AppBarIconState();
}

class _AppBarIconState extends State<AppBarIcon> {
  bool isHovered = false;
  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: EdgeInsets.symmetric(
        horizontal: Dimensions.scaleW(2),
      ),
      child: MouseRegion(
        cursor: SystemMouseCursors.click,
        onEnter: (event) {
          setState(() {
            isHovered = true;
          });
        },
        onExit: (event) {
          setState(() {
            isHovered = false;
          });
        },
        child: Icon(
          widget.icon,
          color: isHovered ? Colors.black : const Color(0xFF6e7274),
        ),
      ),
    );
  }
}

Let's break down the code to understand how this widget works:

  1. Import Statements:

    • import 'package:docs_flutter/constants/dimes.dart' imports constants related to dimensions from a local file.

    • import 'package:flutter/material.dart' imports Flutter's material design widgets.

  2. AppBarIcon Class:

    • This class is a StatefulWidget, indicating that it can have mutable state.

    • Constructor: The constructor of this widget takes two parameters:

      • key: A named parameter for the widget's key.

      • icon: A required parameter of type IconData, representing the icon to be displayed.

  3. _AppBarIconState Class:

    • This class represents the state for the AppBarIcon widget and extends State<AppBarIcon>.

    • It has a isHovered property, which is a boolean to keep track of whether the mouse pointer is hovering over the icon.

  4. build Method:

    • Inside the build method, the widget is defined.

    • It consists of a Padding widget, which provides padding around its child.

    • Inside the Padding, there is a MouseRegion widget. The MouseRegion widget is used to capture mouse events and change the state when the mouse pointer enters or exits the region.

    • The properties of the MouseRegion widget include:

      • cursor: It changes the cursor appearance to a click cursor when hovering over the region.

      • onEnter: This is a callback function that gets executed when the mouse pointer enters the region. In this callback, the isHovered state is set to true.

      • onExit: This callback gets executed when the mouse pointer exits the region, and it sets the isHovered state to false.

    • Inside the MouseRegion, there is an Icon widget. The icon's color is conditionally set based on the isHovered state:

      • If the mouse pointer is hovering (isHovered is true), the icon color is set to Colors.black.

      • If not hovering, the icon color is set to a specific gray color (Color(0xFF6e7274)).

This AppBarIcon widget is designed to display an icon that changes color when hovered over. It's useful for creating interactive and visually responsive icons in an app's app bar or similar navigation areas. The color change provides a visual cue to the user when interacting with the icon.

Hence our NavigationBar is completed