Theme và ThemeData — định nghĩa, nguyên tắc và cấu hình kiểu

Tác giả: IT Sectr Đã đăng: 2026-07-03 Thời gian đọc: 8 phút

ThemeThemeData — cơ chế chủ đề trung tâm trong Flutter, cho phép định nghĩa màu sắc, phông chữ, khoảng cách và kiểu thành phần một cách toàn cục cho toàn bộ ứng dụng. ThemeData là một đối tượng cấu hình được truyền vào MaterialApp thông qua tham số theme. Theo tài liệu chính thức của Flutter (2026), cấu hình ThemeData đúng cách giảm 70–80% mã tạo kiểu trùng lặp và đảm bảo giao diện trực quan nhất quán trên tất cả các màn hình. Hệ thống kế thừa tích hợp cho phép ghi đè chủ đề ở cấp độ widget riêng lẻ thông qua Theme.of(context).

Những điểm chính

  • ThemeData — đối tượng cấu hình với bảng màu, kiểu chữ và kiểu thành phần
  • Theme.of(context) — truy cập chủ đề hiện tại từ bất kỳ widget nào qua BuildContext
  • Light/Dark — hỗ trợ tích hợp cho chủ đề sáng và tối thông qua theme và darkTheme
  • Theme — widget kế thừa chủ đề cho các phần tử con và cho phép ghi đè nó
  • ColorScheme — tập hợp các màu hài hòa tạo kiểu cho toàn bộ hệ thống Material

Theme và ThemeData là gì

ThemeData là một lớp chứa mô tả đầy đủ về kiểu trực quan của ứng dụng Material. Nó được truyền vào MaterialApp qua tham số theme hoặc darkTheme và được tất cả các widget con kế thừa. Mỗi widget Material Design sử dụng ThemeData để lấy kiểu của nó: màu nút, kích thước AppBar, kiểu chữ, lề thẻ và biểu tượng.

Trọng tâm của ThemeData là ColorScheme, định nghĩa các màu primary, secondary, surface, error và các màu khác. Từ những màu này, các trạng thái (hover, focus, pressed) được tự động tính toán cho tất cả các thành phần Material. Ví dụ, ElevatedButton sử dụng màu primary cho nền và onPrimary cho chữ — không cần cấu hình thêm.

Widget Theme là một InheritedWidget truyền chủ đề xuống cây widget. Bất kỳ widget nào cũng có thể truy cập chủ đề hiện tại qua Theme.of(context) và cũng có thể tạo chủ đề mới dựa trên chủ đề hiện có với các giá trị được ghi đè thông qua Theme.copyWith hoặc ThemeData.copyWith.

Tóm lại: ThemeData là nguồn duy nhất cho kiểu dáng của ứng dụng. Hãy bắt đầu phát triển bằng cách định nghĩa ThemeData — điều này thiết lập ngôn ngữ trực quan cho toàn bộ dự án và ngăn chặn các kiểu phân tán trên các màn hình khác nhau.

ColorScheme: nền tảng của bảng màu

ColorScheme là yếu tố trung tâm của chủ đề trong Material 3. Nó thay thế primarySwatch và primaryColor đã lỗi thời, cung cấp một bộ màu hoàn chỉnh cho tất cả các trạng thái và thành phần.

ColorScheme chứa 12 màu chính: primary, onPrimary, secondary, onSecondary, surface, onSurface, error, onError, background, onBackground, outline và shadow. Cho mỗi màu, các biến thể sáng và tối được tự động tính toán, cũng như màu cho các trạng thái hover, focus, pressed và dragged. Khi chỉ có primary được chỉ định, framework tự động tạo các màu còn lại thông qua ColorScheme.fromSeed.

Phương thức ColorScheme.fromSeed (Material 3) nhận một màu cơ sở seedColor và tạo ra một bảng màu hài hòa hoàn chỉnh dựa trên thuật toán Material Design 3. Điều này loại bỏ nhu cầu chọn thủ công các màu bổ sung và tương tự. Chỉ cần truyền một màu thương hiệu chính, và ColorScheme.fromSeed sẽ tạo ra một bảng màu nhất quán cho toàn bộ hệ thống Material.

Tóm lại: trong các dự án mới, hãy sử dụng ColorScheme.fromSeed để tạo bảng màu. Để tương thích Material 2 — sử dụng primarySwatch. ColorScheme là yêu cầu tối thiểu cho Material 3.

Kiểu chữ và TextTheme: phông chữ ứng dụng

TextTheme là một phần của ThemeData định nghĩa kiểu chữ cho tất cả các cấp tiêu đề, tiêu đề phụ, nội dung chính và nhãn. Material Design định nghĩa 15 kiểu chữ: headlineLarge, headlineMedium, headlineSmall, titleLarge, titleMedium và các kiểu khác.

Kiểu chữ được thiết lập thông qua GoogleFonts để tải phông chữ tự động, hoặc thông qua Theme.of(context).textTheme tích hợp với các đối tượng TextStyle tùy chỉnh. GoogleFonts cung cấp hàng trăm phông chữ qua gói google_fonts, lưu trữ cục bộ sau lần tải đầu tiên. Giải pháp thay thế là nhúng phông chữ vào tài nguyên dự án qua pubspec.yaml.

Đối với kiểu chữ thích ứng, TextTheme hỗ trợ MediaQuery.textScaleFactor — khi kích thước phông chữ hệ thống tăng, tất cả các kiểu chữ được chia tỷ lệ tương ứng. Ngoài ra, có thể đặt kích thước phông chữ khác nhau cho các hệ số hình dạng khác nhau thông qua isTest tùy thuộc vào MediaQuery.size.width.

Tóm lại: hãy định nghĩa TextTheme trong ThemeData và sử dụng Theme.of(context).textTheme cho tất cả các phần tử chữ. Điều này đảm bảo tính nhất quán của phông chữ và đơn giản hóa các thay đổi kiểu chữ toàn cục.

Kế thừa và ghi đè chủ đề

Theme là một InheritedWidget truyền chủ đề từ cha sang con. Bất kỳ widget con nào cũng có thể đọc chủ đề hiện tại qua Theme.of(context) và tạo bản sao riêng với các thay đổi thông qua ThemeData.copyWith.

Cơ chế kế thừa: nếu một widget được bọc trong Theme với dữ liệu mới, tất cả các widget con của nó thấy chủ đề đã được ghi đè, trong khi các widget bên ngoài lớp bọc này thấy chủ đề gốc. Điều này cho phép, ví dụ, cô lập kiểu cho một màn hình cụ thể: tiêu đề sử dụng chủ đề tùy chỉnh với AppBarTheme đã sửa đổi, trong khi phần còn lại của ứng dụng sử dụng chủ đề tiêu chuẩn.

Việc lồng Theme có thể không giới hạn. Flutter sử dụng tìm kiếm phân cấp: khi gọi Theme.of(context), framework đi lên cây đến Theme đầu tiên và trả về chủ đề của nó. Nếu không tìm thấy Theme nào — một ngoại lệ được ném ra. Do đó, MaterialApp luôn bọc ứng dụng trong một Theme.

Tóm lại: kế thừa Theme là một cơ chế linh hoạt để cô lập kiểu. Sử dụng ghi đè chủ đề cho các màn hình riêng lẻ mà không sao chép cấu hình toàn cục.

Chủ đề tối: chế độ tối của ứng dụng

Chủ đề tối là khả năng tích hợp của Flutter để chuyển đổi giữa chủ đề sáng và tối. ThemeData được lưu trữ trong các tham số theme (sáng) và darkTheme (tối) của MaterialApp. Tham số themeMode điều khiển chế độ: ThemeMode.light (luôn sáng), ThemeMode.dark (luôn tối) hoặc ThemeMode.system (theo cài đặt hệ thống).

Khi triển khai chủ đề tối, không cần tạo lại tất cả kiểu từ đầu. Chỉ cần truyền ColorScheme.fromSeed(seedColor, brightness: Brightness.dark) vào darkTheme. Framework sẽ tự động điều chỉnh màu cho nền tối: surface trở thành xám đậm, chữ trở nên sáng. Các chủ đề phụ khác (AppBarTheme, CardTheme, BottomNavigationBarTheme) được kế thừa từ phiên bản sáng trừ khi được ghi đè rõ ràng.

Để chuyển đổi mượt mà giữa các chủ đề, Flutter cung cấp hoạt ảnh qua AnimatedTheme. Khi themeMode thay đổi, màu sắc và kiểu được tạo hoạt ảnh, tạo ra sự chuyển đổi trực quan dễ chịu. AnimatedTheme sử dụng Duration và Curve để cấu hình tốc độ hoạt ảnh.

Tóm lại: chủ đề tối được triển khai thông qua darkTheme và ColorScheme.fromSeed với brightness: Brightness.dark. Sử dụng ThemeMode.system để theo cài đặt hệ thống của người dùng.

Ví dụ mã với Theme và ThemeData

Ví dụ 1 — cấu hình chủ đề toàn cục qua MaterialApp sử dụng ColorScheme.fromSeed.

dart
final ThemeData appTheme = ThemeData(
  useMaterial3: true,
  colorScheme: ColorScheme.fromSeed(
    seedColor: Colors.indigo,
    brightness: Brightness.light,
  ),
  appBarTheme: const AppBarTheme(
    centerTitle: true,
    elevation: 0,
  ),
  cardTheme: const CardTheme(
    elevation: 2,
    margin: EdgeInsets.all(8),
  ),
);

return MaterialApp(
  theme: appTheme,
  themeMode: ThemeMode.system,
  home: const MyHomePage(),
);

Ví dụ này tạo một ThemeData với Material 3, bảng màu dựa trên màu chàm và kiểu AppBar và Card tùy chỉnh. useMaterial3 kích hoạt hệ thống chủ đề Material 3 mới, và ColorScheme.fromSeed tạo bảng màu hoàn chỉnh.

Ví dụ 2 — truy cập chủ đề và ghi đè cho một phần của ứng dụng.

dart
Widget build(BuildContext context) {
  final theme = Theme.of(context);

  return Column(
    children: [
      Text(
        'Standard headline',
        style: theme.textTheme.headlineMedium,
      ),
      Theme(
        data: theme.copyWith(
          colorScheme: theme.colorScheme.copyWith(
            primary: Colors.orange,
          ),
        ),
        child: const SpecialSection(),
      ),
    ],
  );
}

Ví dụ thứ hai minh họa việc sử dụng Theme.of(context) để đọc kiểu và Theme() với copyWith để ghi đè chủ đề trong một phần của cây mà không thay đổi cấu hình toàn cục. SpecialSection bên trong Theme sẽ nhận màu primary cam, trong khi phần còn lại của ứng dụng giữ nguyên màu chàm ban đầu.

Tóm lại: chủ đề toàn cục được thiết lập trong MaterialApp, truy cập qua Theme.of(context) và ghi đè qua Theme với copyWith. Đối với Material 3, hãy sử dụng ColorScheme.fromSeed.

Lỗi thường gặp khi cấu hình chủ đề

Lỗi 1: sử dụng primarySwatch thay vì ColorScheme trong Material 3. primarySwatch đã lỗi thời và không được hỗ trợ trong Material 3. Khi sử dụng useMaterial3: true với primarySwatch, Flutter đưa ra cảnh báo. Giải pháp: chuyển sang ColorScheme.fromSeed để tạo bảng màu.

Lỗi 2: mã hóa cứng màu trong widget thay vì sử dụng Theme.of(context). Các nhà phát triển viết Colors.blue trực tiếp trong Container thay vì sử dụng màu primary hoặc secondary từ chủ đề. Khi chủ đề thay đổi, những màu này không thay đổi, phá vỡ tính nhất quán. Giải pháp: luôn sử dụng Theme.of(context).colorScheme.primary hoặc các màu khác từ chủ đề.

Lỗi 3: thiếu darkTheme. Nếu ứng dụng không định nghĩa darkTheme, khi chủ đề tối được bật trên thiết bị, Flutter cố gắng tự động thích ứng chủ đề sáng, thường cho kết quả chất lượng thấp. Giải pháp: luôn truyền darkTheme vào MaterialApp, ngay cả khi chủ đề tối không phải ưu tiên — đây là trải nghiệm người dùng quan trọng.

Lỗi 4: tạo chủ đề mới mỗi lần build. Nếu ThemeData được tạo bên trong phương thức build, mỗi lần xây dựng lại widget tạo ra một đối tượng chủ đề mới, phá vỡ tối ưu hóa InheritedWidget. Giải pháp: di chuyển ThemeData ra trường static hoặc hàm tạo const bên ngoài build.

Tóm lại: chủ đề hóa đúng cách đòi hỏi một cách tiếp cận tập trung. Di chuyển ThemeData vào hằng số, sử dụng bảng màu từ chủ đề và luôn định nghĩa cả hai chủ đề — sáng và tối.

Câu hỏi thường gặp

Theme khác ThemeData như thế nào?

ThemeData là dữ liệu cấu hình (màu sắc, phông chữ, kiểu). Theme là InheritedWidget truyền ThemeData xuống cây widget. Theme.of(context) trả về ThemeData mà Theme gần nhất truyền trong ngữ cảnh hiện tại.

Làm thế nào để chuyển đổi chủ đề ứng dụng động?

Điều khiển themeMode trong MaterialApp thông qua trạng thái ứng dụng. Thay đổi trạng thái từ ThemeMode.light sang ThemeMode.dark, và Flutter sẽ xây dựng lại toàn bộ cây với chủ đề mới. Để có hoạt ảnh mượt mà, hãy sử dụng AnimatedTheme.

ColorScheme.fromSeed là gì?

ColorScheme.fromSeed là phương thức Material 3 nhận một màu cơ sở (seedColor) và tạo ra một bảng màu hài hòa hoàn chỉnh: primary, secondary, tertiary, surface, error và các biến thể on của chúng. Cho phép tạo bảng màu nhất quán từ một màu thương hiệu duy nhất.

Làm thế nào để sử dụng phông chữ tùy chỉnh với Theme?

Thông qua TextTheme bên trong ThemeData: chỉ định TextStyle với fontFamily cho mỗi cấp. Đối với Google Fonts, sử dụng gói google_fonts, truyền fontFamily trực tiếp vào TextTheme. Đối với phông chữ cục bộ — bao gồm chúng qua pubspec.yaml trong phần fonts.

Có thể ghi đè chủ đề cho một màn hình không?

. Bọc màn hình trong Theme với dữ liệu được ghi đè qua copyWith. Tất cả widget con bên trong Theme này sẽ nhận được kiểu đã sửa đổi, trong khi phần còn lại của ứng dụng giữ chủ đề gốc. Điều này không tạo ra chi phí bổ sung.

Tổng kết

  • ThemeData — đối tượng cấu hình với bảng màu, kiểu chữ và kiểu cho tất cả thành phần Material
  • ColorScheme.fromSeed — cách chính để tạo bảng màu trong Material 3 từ một màu thương hiệu duy nhất
  • Theme.of(context) — truy cập chủ đề hiện tại để đọc kiểu mà không trùng lặp mã
  • Theme — InheritedWidget để kế thừa và ghi đè chủ đề trong cây con của widget
  • Chủ đề tối — ThemeData riêng với brightness: Brightness.dark, điều khiển qua themeMode
  • Khuyến nghị: di chuyển ThemeData vào static hoặc const, luôn định nghĩa theme và darkTheme trong MaterialApp

Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay

IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.

Thảo luận dự án

Đọc thêm