[TIL] Theme 불완전 정복기 - 1

김영광·2025년 12월 29일

팀프로젝트 디자인을 마치고, 본격적인 코딩에 들어갔다.
이런저런 우여곡절도 있었지만, 구현 과정에서는 다들 열심히 임해주었다!

🔎 flutter Theme에 대해 공부하던 중..

flutter Theme에 대해 궁금증이 생겨 Google 크롤링, Gemini, 네이버, youtube 등 여러 사이트를 뒤져보았다.
youtube를 최신순으로 뒤지던 중, "Debug To Discover" 채널을 알게 되었다.
flutter를 중점으로 올리는 채널인데 Theme Tutorial 영상이 있길래 따라가보기로 했다.

물론 외국인이라.. 자막은 키도록 하겠다.

📝 Theme 파일 분리

ThemeData를 단순히 class 하나 안에 lightTheme, darkTheme 두가지를 static으로 몰아넣어 관리했었다.
하지만, 영상에서는 ComponentsTheme, ContentsTheme, Theme Tokens 등 여러 디렉토리에 걸쳐 나눠서 관리하고 있었다.
모든 Theme은 함수로 관리되고 있었는데, 유지보수와 최상단 위젯의 rebuild에 따른 용이함때문인듯 하다.

ComponentsTheme은 ContentsTheme, ButtonTheme, DialogTheme으로 구성되어 있다.
ContentsTheme은 Container, Card, Divider 등 전체적인 Components의 Theme을 관리하며, Dialog는 Modal을 전적으로 관리한다.
ButtonTheme은 당연히 Button을 관리한다.

그럼 어디서 값을 가져와 관리하냐?
Tokens에는 color, text, shadow 등 자주 사용되는 속성값을 담아둔다.
이 Design Token들을 이용해 ColorScheme를 만들고, 이를 통해 Theme을 관리하게 된다.

이제 좀 개념이 잡히는 것 같다.

📝 Extension?

앱을 더 이쁘게 꾸미게 위해서는 Extension이 필요하다.
말 그대로 테마의 확장 버전이라고 생각하면 된다.
흔히 보이는 그라데이션이 여기에 포함된다.
이 부분은 조금 더 학습이 필요하기에 팀 프로젝트 이후, riverpod과 다시 한번 다루기로 한다.

📝 그럼 이번 팀 프로젝트 테마는?

이번 팀 프로젝트 AppleMarket은 은은한 살구색 느낌의 앱으로 개발하고 싶다.
앱에서 필요로 하는 Widget은 피그마를 통해 정리해두었기에 그에 맞춰 테마를 구현하고자 한다.

class AppColors {
  // Light Theme
  static const Color lightPrimary = Colors.black;
  static const Color lightSecondary = Color(0xFFFF968A);
  static const Color lightBackground = Color(0xFFFFC5BF);
  static const Color lightSurface = Colors.white;
  static const Color lightSurfaceVariant = Color(0xFFF3F4F6);
  static const Color lightText = Colors.black;
  static const Color lightError = Colors.black54;

  // Dark Theme
  static const Color darkPrimary = Color(0xFFFFD8BE);
  static const Color darkSecondary = Colors.grey;
  static const Color darkBackground = Colors.black;
  static const Color darkSurface = Color(0xFF736571);
  static const Color darkSurfaceVariant = Color(0xFFF3F4F6);
  static const Color darkText = Colors.white;
  static const Color darkError = Colors.black54;
}

따라서, 주요 컬러는 다음과 같이 설정했다.
Primary는 주요 위젯의 색상, Secondary는 가끔 등장하는 위젯 정도로 생각하면 될 것 같다.
Background와 Surface에 대해 헷갈렸는데, 전자는 앱 전체의 바탕색, 후자는 특정 위젯에 대한 색상이라고 생각하면 된다.

SurfaceVariant는 특정 위젯이 기존 위젯과 다르게 표시되어야 할 때 사용한다.


class ContentsThemes {
  static ListTileThemeData listTileTheme(ColorScheme colors) {
    return ListTileThemeData(
      tileColor: Colors.transparent,
      contentPadding: const EdgeInsets.symmetric(horizontal: 20, vertical: 4),
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadiusGeometry.circular(12),
      ),
      iconColor: colors.onSurface,
      textColor: colors.onSurface,
    );
  }

  static DividerThemeData dividerTheme(ColorScheme colors) {
    return DividerThemeData(
      color: colors.onSurface.withValues(alpha: 0.1),
      thickness: 1,
      space: 24,
      indent: 10,
      endIndent: 10,
    );
  }

  static AppBarThemeData appBarTheme(ColorScheme colors) {
    return AppBarThemeData(
      backgroundColor: Colors.transparent,
      centerTitle: true,
      titleTextStyle: TextStyle(
        fontSize: 35,
        fontWeight: FontWeight.bold,
        fontFamily: "Dongle",
      ),
      iconTheme: IconThemeData(color: colors.onSurface),
    );
  }
}

ContentsThemes는 다음과 같이 구성되어 있다.
GoogleFonts 라이브러리를 사용하게 됨으로, 리팩토링이 필요한 상태이다.
내일해야겠다..

팀 프로젝트

첫 팀프로젝트라 ui 구성은 팀원들에게 맡기고 도메인 로직과 테마 관리에 집중하고 있다.
내일은 장바구니 추가 및 삭제 기능과 테마 완료, 추가적으로 시간이 나면 나만의 기능으로 dismissible, GridView 변형까지 해봐야겠다!

[youtube] https://www.youtube.com/watch?v=-mCOoAL-eog

profile
주니어 개발자

0개의 댓글