
Chào các "coder hệ Z"! Anh Creyt lại lên sóng đây. Hôm nay, chúng ta sẽ "soi" một thằng bé mà nhiều khi mấy đứa bỏ qua, nhưng nó lại cực kỳ quan trọng để app của mình không chỉ "chạy" mà còn phải "chất", phải "đỉnh của chóp" – đó chính là TextSelectionThemeData trong Flutter.
1. TextSelectionThemeData là "thằng nào" và nó "làm gì"?
Nghe cái tên TextSelectionThemeData chắc mấy đứa thấy hơi "academic" đúng không? Đừng lo, anh Creyt sẽ "dịch" cho dễ hiểu. Tưởng tượng thế này: khi mấy đứa dùng điện thoại hoặc máy tính, mỗi khi chọn một đoạn văn bản nào đó (ví dụ, để copy cái caption thả thính), nó sẽ hiện lên một cái "vùng sáng" (highlight) và hai cái "chấm tròn" (selection handles) để mấy đứa kéo chọn, cùng với cái "vạch nhấp nháy" (cursor) khi gõ chữ.
TextSelectionThemeData chính là "nghệ nhân" đứng sau để "trang điểm" cho mấy cái thành phần đó. Nó cho phép mấy đứa tùy chỉnh màu sắc của:
selectionColor: Cái vùng highlight màu mè khi mấy đứa kéo chọn chữ.cursorColor: Cái vạch nhấp nháy "lấp la lấp lánh" khi con trỏ đang chờ nhập liệu.selectionHandleColor: Hai cái "tay cầm" bé xinh hình tròn hay hình giọt nước mà mấy đứa dùng để mở rộng hoặc thu hẹp vùng chọn.
Nói cách khác, nó là "bộ kit trang điểm" cho trải nghiệm tương tác với văn bản. Thay vì để Flutter dùng màu mặc định "nhạt nhẽo" như "trà sữa không đường", mấy đứa có thể biến nó thành "ly trà sữa full topping" đúng chuẩn branding của app mình. Mục tiêu là gì? Để app của mấy đứa không chỉ "ngon" về chức năng mà còn "đã mắt" về thị giác, tạo ra một trải nghiệm "smooth như kem" cho người dùng.
2. Code Ví Dụ Minh Họa: "Thực hành ngay và luôn!"
Giờ thì "xắn tay áo" vào code thôi! Anh sẽ hướng dẫn mấy đứa cách áp dụng TextSelectionThemeData một cách toàn cục (cho cả app) và cục bộ (cho một widget cụ thể).
Cách 1: Áp dụng toàn cục với ThemeData (phương pháp "một phát ăn ngay")
Đây là cách "nhanh gọn lẹ" nhất để đồng bộ màu sắc cho toàn bộ app của mấy đứa. Mấy đứa sẽ "nhét" nó vào trong ThemeData của MaterialApp.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Creyt\'s Text Selection Demo',
theme: ThemeData(
// Đây rồi, "ngôi sao" của chúng ta!
textSelectionTheme: TextSelectionThemeData(
selectionColor: Colors.purple.withOpacity(0.3), // Màu highlight "tím mộng mơ"
cursorColor: Colors.deepPurpleAccent, // Con trỏ "tím biếc"
selectionHandleColor: Colors.deepPurple, // Tay kéo "tím lịm tìm sim"
),
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Text Selection Theme Demo'),
),
body: Center(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'Thử chọn đoạn văn bản này xem! Màu sắc đã khác rồi đó nha.',
style: TextStyle(fontSize: 20),
textAlign: TextAlign.center,
),
const SizedBox(height: 30),
TextField(
decoration: const InputDecoration(
labelText: 'Nhập gì đó vào đây nè',
border: OutlineInputBorder(),
),
// TextField sẽ tự động kế thừa theme từ MaterialApp
),
const SizedBox(height: 30),
TextFormField(
maxLines: 3,
decoration: const InputDecoration(
labelText: 'Vùng nhập liệu nhiều dòng',
border: OutlineInputBorder(),
),
),
],
),
),
),
);
}
}
Cách 2: Áp dụng cục bộ với TextSelectionTheme (phương pháp "đặc trị")
Đôi khi, mấy đứa chỉ muốn một vùng chọn văn bản cụ thể có màu sắc "độc lạ" mà không ảnh hưởng đến phần còn lại của app. Lúc này, TextSelectionTheme widget sẽ là "cứu tinh".
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Creyt\'s Local Text Selection Demo',
theme: ThemeData(
// Theme mặc định cho cả app (có thể khác)
primarySwatch: Colors.green,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Local Text Selection Theme Demo'),
),
body: Center(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'Đoạn văn bản này sẽ dùng theme mặc định của app.',
style: TextStyle(fontSize: 18),
textAlign: TextAlign.center,
),
const SizedBox(height: 30),
// Áp dụng theme riêng cho TextField này
TextSelectionTheme(
data: TextSelectionThemeData(
selectionColor: Colors.orange.withOpacity(0.4), // Màu cam "nổi bần bật"
cursorColor: Colors.red, // Con trỏ "đỏ rực"
selectionHandleColor: Colors.deepOrange, // Tay kéo "cam cháy"
),
child: TextField(
decoration: const InputDecoration(
labelText: 'Nhập gì đó vào đây (theme cam)',
border: OutlineInputBorder(),
),
),
),
const SizedBox(height: 30),
TextField(
decoration: const InputDecoration(
labelText: 'TextField này vẫn dùng theme mặc định.',
border: OutlineInputBorder(),
),
),
],
),
),
),
);
}
}

3. Mẹo Vặt (Best Practices) từ "lão làng" Creyt
- Đồng bộ là "chân ái": Luôn cố gắng giữ màu sắc vùng chọn khớp với bảng màu (color scheme) tổng thể của app. Đừng để nó "lạc quẻ" như "nốt trầm giữa bản nhạc rap". Dùng
Theme.of(context).colorScheme.primaryhoặcsecondaryđể đảm bảo tính nhất quán. - Độ tương phản (Contrast): Mấy đứa chọn màu highlight phải đảm bảo chữ vẫn đọc được rõ ràng. Màu highlight quá sáng trên nền chữ sáng, hoặc quá tối trên nền chữ tối là "điểm trừ cực mạnh" đó nha. Hãy nghĩ về khả năng tiếp cận (accessibility) cho người dùng có thị lực kém.
- Trải nghiệm người dùng (UX): Màu sắc vùng chọn nên "mềm mại", không gây chói mắt. Mục đích là để dẫn dắt sự chú ý, không phải để "hù dọa" người dùng.
- Dark Mode/Light Mode: Đừng quên tùy chỉnh
TextSelectionThemeDatacho cả hai chế độ sáng/tối. Một màu đẹp ở light mode có thể "thảm họa" ở dark mode và ngược lại.
4. Ứng dụng thực tế: Ai đã dùng? Dùng như thế nào?
Thực ra, mấy đứa đang dùng nó mỗi ngày mà không biết đó thôi! Hầu hết các ứng dụng "xịn sò" đều tùy chỉnh cái này để tạo nên "chất riêng":
- Các ứng dụng nhắn tin (WhatsApp, Telegram): Mỗi ứng dụng có một tông màu chủ đạo riêng. Khi mấy đứa chọn tin nhắn hay nhập liệu, vùng highlight và con trỏ sẽ theo màu brand của họ, tạo cảm giác "thuộc về" ứng dụng đó.
- Ứng dụng ghi chú (Notion, Evernote): Họ thường cho phép người dùng tùy chỉnh theme, và tất nhiên, màu sắc vùng chọn cũng thay đổi theo để đồng bộ với theme đó.
- Các trình duyệt web (Chrome, Safari): Mấy đứa để ý xem, khi chọn văn bản trên một website, màu highlight có thể thay đổi tùy thuộc vào CSS của trang web đó. Trong Flutter,
TextSelectionThemeDatalàm công việc tương tự. - Ứng dụng đọc sách (Kindle, Google Books): Khi mấy đứa highlight để đánh dấu một đoạn sách, màu highlight đó cũng được tùy chỉnh để dễ đọc và phù hợp với giao diện đọc sách.
5. Thử nghiệm và Nên dùng cho Case nào?
Nên dùng khi:
- Xây dựng Brand Identity: Đây là cách đơn giản nhưng hiệu quả để "khắc dấu" thương hiệu của mấy đứa vào từng ngóc ngách của app.
- Cải thiện UX/UI: Một app có giao diện đẹp, đồng bộ sẽ tạo ấn tượng tốt hơn rất nhiều, giúp người dùng cảm thấy "dễ chịu" khi tương tác.
- Hỗ trợ Dark Mode/Light Mode: Khi app của mấy đứa có nhiều theme,
TextSelectionThemeDatalà công cụ không thể thiếu để đảm bảo mọi thứ đều "nuột nà" ở mọi chế độ. - Tạo điểm nhấn đặc biệt: Trong một số trường hợp, mấy đứa muốn một
TextFieldnào đó có màu sắc khác biệt để thu hút sự chú ý (ví dụ: trường nhập mã khuyến mãi).
Không nên dùng khi:
- Lạm dụng màu sắc: Đừng biến app của mình thành "cầu vồng 7 sắc" mỗi khi chọn văn bản. Sự đơn giản và tinh tế luôn là chìa khóa.
- Phá vỡ tính nhất quán: Trừ khi có lý do rất rõ ràng, đừng đặt những màu sắc "chỏi" nhau hoặc khác biệt hoàn toàn với theme chung của app. Nó sẽ làm người dùng cảm thấy "khó hiểu" và "rối mắt".
Nhớ nhé, TextSelectionThemeData không chỉ là một "công cụ" mà còn là một "ngôn ngữ" để app của mấy đứa "nói" lên cá tính. Đừng bỏ qua nó, hãy "chơi đùa" với nó để tạo ra những trải nghiệm "đỉnh cao" cho người dùng! Hẹn gặp lại trong bài học tiếp theo của anh Creyt!
Thuộc Series: Flutter
Bài giảng này được tự động xuất bản ngẫu nhiên từ thư viện kiến thức. Đừng quên đón xem các Từ khoá Hướng Dẫn tiếp theo nhé!