arch-atlas

Best seller item part 3

Presentation (Building the UI)

06:28

Summary

This lesson continues building the UI for the best seller list item by adding the author's name and the book's price. The instructor focuses on layout alignment using Column and Row widgets and emphasizes the importance of managing global text styles. A key refactoring step involves adjusting a shared TextStyle to prevent an unwanted custom font family from overriding the default font, demonstrating how to use copyWith for localized style overrides. Finally, the widget is extracted into its own file to maintain a clean presentation layer structure.

Key Ideas

  • Use SizedBox to create precise vertical spacing between text elements based on design specifications.
  • CrossAxisAlignment.start aligns children of a Column to the leading edge.
  • A Row widget is used to horizontally align the price and the upcoming rating widget.
  • Global TextStyle definitions should ideally use the app's default font family unless explicitly defined for special cases like titles.
  • copyWith allows extending a base TextStyle to add specific traits like fontWeight or fontFamily without creating entirely new global styles.
  • Extracting complex widgets into their own dedicated files keeps the presentation layer organized and manageable.

Code Snippets

Removing the hardcoded font family from textStyle20 so it defaults to the app's primary font.dart
abstract class Styles {
  // ...
  static const textStyle20 = TextStyle(
    fontSize: 20,
    fontWeight: FontWeight.normal,
    // fontFamily: kGtSectraFine, // Removed to allow default Montserrat font
  );
  // ...
}
Final state of the BestSellerListViewItem widget, extracted to its own file.dart
import 'package:flutter/material.dart';
import '../../../../../core/utils/assets.dart';
import '../../../../../core/utils/styles.dart';

class BestSellerListViewItem extends StatelessWidget {
  const BestSellerListViewItem({super.key});

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      height: 125,
      child: Row(
        children: [
          AspectRatio(
            aspectRatio: 2.5 / 4,
            child: Container(
              decoration: BoxDecoration(
                borderRadius: BorderRadius.circular(8),
                color: Colors.red,
                image: const DecorationImage(
                  fit: BoxFit.fill,
                  image: AssetImage(
                    AssetsData.testImage,
                  ),
                ),
              ),
            ),
          ),
          const SizedBox(width: 30),
          Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              SizedBox(
                width: MediaQuery.of(context).size.width * .5,
                child: Text(
                  'Harry Potter and the Goblet of Fire',
                  maxLines: 2,
                  overflow: TextOverflow.ellipsis,
                  style: Styles.textStyle20.copyWith(
                    fontFamily: kGtSectraFine,
                  ),
                ),
              ),
              const SizedBox(height: 3),
              const Text(
                'J.K. Rowling',
                style: Styles.textStyle14,
              ),
              const SizedBox(height: 3),
              Row(
                children: [
                  Text(
                    '19.99 €',
                    style: Styles.textStyle20.copyWith(
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                ],
              ),
            ],
          ),
        ],
      ),
    );
  }
}

My Notes

The instructor's decision to strip fontFamily from textStyle20 is a classic design system adjustment. It's better to keep your base typography tokens (like textStyle14, textStyle20) generic and tied to the default app font, and only apply specialized fonts via .copyWith() when rendering specific semantic elements like book titles.

  • `[Editor's note]`: The instructor uses a SizedBox with width: MediaQuery.of(context).size.width * .5 to constrain the title text. While this works on a standard phone screen, it is an anti-pattern in responsive Flutter layouts. If the screen rotates or runs on a tablet, the fixed 50% width might look awkward or cause the trailing elements to overflow. A more robust layout approach is to wrap the Column in an Expanded widget, allowing it to dynamically consume the remaining horizontal space in the Row.