arch-atlas

Create custom app bar

Presentation (Building the UI)

14:35

Summary

This lesson focuses on building the custom app bar for the home screen by breaking the UI into smaller, manageable widgets to keep the presentation layer clean. It demonstrates how to create a CustomAppBar using a Row with a logo image and a search icon separated by a Spacer. A key part of the lesson involves matching the design specifications closely, which leads to replacing the default Material search icon with a more visually accurate FontAwesome alternative and adjusting padding and sizes to align perfectly with the provided Adobe XD mockup.

Key Ideas

  • HomeView delegates its body to a custom HomeViewBody widget to keep the main view file uncluttered.
  • CustomAppBar is extracted into its own stateless widget to maintain modular UI code and avoid deeply nested widget trees.
  • Row aligns the logo image and search button horizontally across the screen.
  • Spacer widget dynamically fills the empty space between the logo and the search icon, pushing them to opposite ends of the app bar.
  • IconButton provides out-of-the-box touch feedback (splash colors) and padding, making it preferable to wrapping a standard Icon in a GestureDetector.
  • Default Material icons may not always match the design mockup perfectly; third-party packages like font_awesome_flutter provide necessary alternatives.
  • FontAwesomeIcons.magnifyingGlass is utilized to match the specific stroke thickness and handle length of the search icon from the design.
  • Padding is applied symmetrically (horizontal and vertical) to ensure the app bar sits correctly within the screen margins.

Code Snippets

Final state of home_view.dartdart
import 'package:bookly/features/home/presentation/views/widgets/home_view_body.dart';
import 'package:flutter/material.dart';

class HomeView extends StatelessWidget {
  const HomeView({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: HomeViewBody(),
    );
  }
}
Final state of home_view_body.dartdart
import 'package:bookly/features/home/presentation/views/widgets/custom_app_bar.dart';
import 'package:flutter/material.dart';

class HomeViewBody extends StatelessWidget {
  const HomeViewBody({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Column(
      children: const [
        CustomAppBar(),
      ],
    );
  }
}
Final state of custom_app_bar.dartdart
import 'package:bookly/core/utils/assets.dart';
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';

class CustomAppBar extends StatelessWidget {
  const CustomAppBar({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 40),
      child: Row(
        children: [
          Image.asset(
            AssetsData.logo,
            height: 18,
          ),
          const Spacer(),
          IconButton(
            onPressed: () {},
            icon: const Icon(
              FontAwesomeIcons.magnifyingGlass,
              size: 24,
            ),
          )
        ],
      ),
    );
  }
}

Architecture Insight

Architecture insight

core/widgets/ is the CA-correct home for components reused across features. The test: if you can imagine two unrelated features rendering this widget, it doesn't belong inside one feature folder.

If only one feature uses it today, keep it local. Don't promote on speculation — premature reuse is harder to undo than late reuse.

My Notes

The instructor heavily emphasizes matching the UI perfectly to the designer's intent. He actively searches for a specific search icon (using FontAwesome's magnifying-glass instead of Material's Icons.search) to match the exact stroke width and handle length from the Adobe XD file.

  • `Spacer` vs `MainAxisAlignment`: The instructor uses a Spacer() widget to push the logo and icon apart. An alternative approach would be omitting the Spacer and adding mainAxisAlignment: MainAxisAlignment.spaceBetween to the Row itself. Both work well, but spaceBetween avoids adding an extra widget node to the tree.
  • `[Editor's note]` UI vs Logic priority: The instructor suggests UI errors are worse than logic errors because clients notice them immediately. In the context of Clean Architecture, this is somewhat contradictory. Clean Architecture exists precisely because complex business logic is fragile and hard to maintain over time. While UI is what the client *sees*, deep logic flaws can break the application entirely.