arch-atlas

Change theme

Presentation (Building the UI)

07:55

Summary

This lesson focuses on setting up the global theme of the Flutter application to prevent repetitive, hardcoded styling across the presentation layer. By analyzing the UI design and configuring the default ThemeData to match the predominant dark mode palette, widgets like text automatically adapt to the correct contrasting color. The instructor demonstrates implementing global theme overrides in the application root and explains the mechanics of centering text inside a stretched column on the splash screen.

Key Ideas

  • Text widgets inherit default typography and colors from the application's global ThemeData.
  • Hardcoding colors (e.g., color: Colors.white) on individual text components is a poor practice that breaks dynamic theming and adds boilerplate.
  • ThemeData.dark() configures the application to default to a dark color palette, natively converting default text elements to white.
  • copyWith() allows overriding specific properties of a predefined base theme, such as changing the scaffoldBackgroundColor to a custom variable.
  • CrossAxisAlignment.stretch on a Column forces its children to expand and occupy the full available width of the screen.
  • TextAlign.center centers text within its own bounding box, which is required when the text widget itself has been stretched to full width by its parent layout.
  • SizedBox provides fixed dimensional spacing (e.g., height: 4) between widgets inside a column or row.

Code Snippets

Configuring the global dark theme and custom background color in main.dartdart
class Bookly extends StatelessWidget {
  const Bookly({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData.dark().copyWith(
        scaffoldBackgroundColor: kPrimaryColor,
      ),
      home: const SplashView(),
    );
  }
}
Adding spaced, centered text to a stretched SplashViewBodydart
class SplashViewBody extends StatelessWidget {
  const SplashViewBody({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      crossAxisAlignment: CrossAxisAlignment.stretch,
      children: [
        Image.asset(AssetsData.logo),
        const SizedBox(
          height: 4,
        ),
        const Text(
          'Read Free Books',
          textAlign: TextAlign.center,
        ),
      ],
    );
  }
}

Architecture Insight

Architecture insight

Theming is a cross-cutting concern: the Domain and Data layers must never know about Brightness.dark. By centralizing ThemeData at the app shell (MaterialApp.theme), you keep the layers below it color-blind.

If a use case ever needs to read a theme value, that's a layer leak — the use case shouldn't care about pixels.

Looking ahead — Light/dark switching, when it lands, should ride on the same DI/state layer you'll set up later — a ThemeCubit in presentation, no domain dependency.

My Notes

  • Gotcha on Layout Alignment: The instructor addresses a very common Flutter layout confusion. Because the parent Column has crossAxisAlignment: CrossAxisAlignment.stretch, the Text widget takes up the entire width of the screen. Therefore, wrapping the text in a Center widget wouldn't do anything because the text box itself is already full-width. You *must* use the textAlign: TextAlign.center property to align the characters within that stretched bounding box.
  • Theming Strategy: Always audit your design files (Figma/Adobe XD) before writing layout code. Identifying that the app is predominantly dark allows you to set ThemeData.dark() immediately. This lets Flutter handle the contrast calculations natively, keeping your widget trees clean.
  • [Editor's note] Using a raw integer like 4 for a SizedBox height is fine for drafting, but in a strict Clean Architecture setup, spacing tokens should be extracted to a core design system file (e.g., AppSpacing.sm) to ensure uniformity.