
Chào các "coder nhí" tương lai của thế giới số! Anh Creyt đây, hôm nay chúng ta sẽ cùng "mổ xẻ" một "viên ngọc" bé nhỏ nhưng cực kỳ quyền năng trong Flutter, đó là SelectableText.rich. Nghe tên thôi đã thấy "sang chảnh" rồi đúng không? Đừng lo, anh sẽ "giải mã" nó dễ hiểu hơn cả việc chơi game mobile vậy.
SelectableText.rich: Khi Văn Bản Của Bạn Muốn "Làm Dáng" Và Vẫn Muốn Được "Chép Nguyên"!
Các em hình dung thế này: Bình thường, khi các em copy một đoạn văn bản từ đâu đó, nhất là mấy bài viết có màu mè, in đậm, in nghiêng lung tung, thì khi paste ra, ôi thôi! Nó "trở về tuổi thơ" với cái font mặc định, đen thui, xấu hoắc. Giống như các em đi dự tiệc với bộ đồ lộng lẫy, nhưng lúc về nhà lại bị bắt thay đồ ngủ vậy.
SelectableText thường của Flutter cũng thế. Nó cho phép người dùng chọn và sao chép văn bản, nhưng chỉ với một kiểu định dạng duy nhất. Nó giống như một chiếc hộp chỉ đựng được một loại bánh thôi vậy.
Nhưng SelectableText.rich thì khác bọt hoàn toàn! Nó chính là "chiếc hộp bento cao cấp" cho văn bản của các em. Mỗi "ngăn" trong hộp bento đó (mà trong Flutter gọi là TextSpan) có thể đựng một "món ăn" (một đoạn văn bản) với "hương vị" (định dạng: màu sắc, font chữ, in đậm, in nghiêng) riêng biệt. Và tuyệt vời hơn nữa, người dùng có thể "chọn món" (chọn đoạn văn bản) nào họ thích, và khi "mang về" (copy), món ăn đó vẫn giữ nguyên "hương vị" ban đầu! Không còn cảnh mất định dạng nữa!
Tóm lại: SelectableText.rich là một widget cho phép hiển thị một đoạn văn bản phong phú (có nhiều kiểu định dạng khác nhau) và vẫn giữ nguyên khả năng cho phép người dùng chọn (highlight) và sao chép (copy) các phần văn bản đó, bao gồm cả định dạng của chúng.
Code Ví Dụ: "Bóc Tem" Ngay "Hộp Bento Văn Bản"!
Để các em dễ hình dung, anh Creyt sẽ "phù phép" một đoạn code nhỏ để thấy được sự "thần kỳ" của SelectableText.rich:
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: 'SelectableText.rich Demo',
theme: ThemeData(
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('SelectableText.rich của Creyt'),
),
body: Center(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: SelectableText.rich(
TextSpan(
text: 'Chào các bạn! Đây là ',
style: const TextStyle(
fontSize: 18,
color: Colors.black87,
),
children: <TextSpan>[
TextSpan(
text: 'một đoạn văn bản ',
style: const TextStyle(
fontWeight: FontWeight.bold,
color: Colors.blue,
fontSize: 20,
),
),
TextSpan(
text: 'siêu cấp ',
style: TextStyle(
fontStyle: FontStyle.italic,
color: Colors.purple.shade700,
fontSize: 18,
),
),
TextSpan(
text: 'có thể chọn ',
style: const TextStyle(
decoration: TextDecoration.underline,
color: Colors.green,
fontSize: 18,
),
),
TextSpan(
text: 'và sao chép ',
style: const TextStyle(
backgroundColor: Colors.yellowAccent,
color: Colors.redAccent,
fontSize: 18,
),
),
TextSpan(
text: 'với nhiều định dạng khác nhau! ',
style: const TextStyle(
fontFamily: 'RobotoMono',
color: Colors.deepOrange,
fontSize: 16,
),
),
// Ví dụ thêm một TextSpan có thể tương tác (tùy chọn)
TextSpan(
text: 'Click vào đây!',
style: const TextStyle(
color: Colors.indigo,
decoration: TextDecoration.underline,
fontWeight: FontWeight.bold,
),
recognizer: TapGestureRecognizer()
..onTap = () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Bạn đã click vào TextSpan này!')),
);
},
),
],
),
),
),
),
);
}
}
Trong ví dụ trên, các em thấy đó, chúng ta dùng TextSpan lồng nhau để tạo ra các phần văn bản với TextStyle riêng biệt. Từ in đậm, in nghiêng, gạch chân, đổi màu, đến cả đổi font chữ hay có nền màu. Và điều "vi diệu" là tất cả những định dạng đó đều được giữ nguyên khi người dùng chọn và copy!

Mẹo & Best Practices: "Bí Kíp Võ Lâm" Của Giảng Viên Creyt
-
Khi nào thì "bung lụa"
SelectableText.rich?- Khi cần văn bản "đa sắc màu": Nếu UI của các em có những đoạn văn bản cần nhiều kiểu định dạng (in đậm, nghiêng, màu mè) và người dùng cần khả năng copy nguyên xi cái "gu" đó. Ví dụ: một bài blog, một đoạn trích dẫn quan trọng, hay một phần hướng dẫn sử dụng.
- Phân biệt với "anh em":
Text: Chỉ để hiển thị, không chọn được. Dùng khi chỉ cần show thông tin tĩnh.Text.rich: Hiển thị văn bản đa dạng định dạng (dùngTextSpan), nhưng cũng không chọn được. Dùng khi cần hiển thị đẹp mà không cần tương tác copy.SelectableText: Cho phép chọn và copy, nhưng chỉ với một kiểu định dạng duy nhất cho toàn bộ văn bản. Dùng khi cần copy văn bản đơn giản.SelectableText.rich: Chính là "người hùng" của chúng ta, kết hợp ưu điểm củaText.rich(hiển thị đa định dạng) vàSelectableText(có thể chọn và copy). Dùng khi cần cả hai!
-
Đừng "lạm dụng" quá mức: Mặc dù
SelectableText.richrất mạnh mẽ, nhưng đừng dùng nó cho mọi thứ. Nếu chỉ là một đoạn văn bản đơn giản, không cần chọn hay chỉ có một style, hãy dùngTexthoặcSelectableTextthường thôi. "Đao to búa lớn" quá đôi khi lại "phản tác dụng" hoặc làm code phức tạp không cần thiết.
-
Tối ưu hóa "sức khỏe": Với
TextSpanlồng nhau, đôi khi việc render có thể tốn tài nguyên hơn một chút so vớiTextđơn giản. Tuy nhiên, trong hầu hết các trường hợp sử dụng thông thường, các em sẽ không cảm thấy sự khác biệt đáng kể. Chỉ cần lưu ý nếu các em đang hiển thị hàng ngànTextSpancùng lúc (mà điều này hiếm khi xảy ra). -
"Nhân văn" với Accessibility: Luôn đảm bảo
TextStylecủa các em có độ tương phản màu tốt, font size đủ lớn để mọi người, kể cả những người có thị lực kém, cũng có thể đọc và tương tác dễ dàng. Đây là "điểm cộng" rất lớn cho app của các em đó! -
Tùy chỉnh hành vi chọn: Các em có thể "đào sâu" hơn bằng cách dùng các thuộc tính như
onSelectionChangedđể biết khi nào người dùng thay đổi vùng chọn, hayselectionControlsđể tùy chỉnh giao diện của các "tay cầm" chọn văn bản (ví dụ: đổi màu, đổi hình dạng).
Ứng Dụng Thực Tế: "Show Hàng" Các App Đã Dùng!
Các em có thể thấy SelectableText.rich hoặc các cơ chế tương tự được áp dụng ở đâu?
- Ứng dụng đọc tin tức/blog (Medium, VnExpress, Kipalog...): Khi các em đọc một bài viết có nhiều đoạn in đậm, trích dẫn, và muốn copy một phần nào đó để chia sẻ, nó thường giữ nguyên định dạng.
SelectableText.richlà một ứng cử viên sáng giá cho việc này. - Ứng dụng ghi chú/tài liệu (Google Keep, Notion, Evernote): Các em ghi chú với highlight, in đậm, in nghiêng. Khi cần copy một đoạn ghi chú để dán vào email hay một app khác, việc giữ nguyên định dạng là cực kỳ quan trọng.
- Ứng dụng hiển thị tài liệu hướng dẫn/FAQ: Những trang này thường có các phần câu hỏi/trả lời với các từ khóa được in đậm, và người dùng có thể muốn copy câu trả lời nguyên văn.
Thử Nghiệm & Nên Dùng Cho Case Nào?
Thử nghiệm:
Anh khuyến khích các em hãy "vọc vạch" ngay với ví dụ code trên. Thay đổi các TextStyle, thêm bớt TextSpan, thử các thuộc tính như textAlign, textDirection. Hãy thử copy đoạn văn bản từ app của mình và paste vào một trình soạn thảo văn bản bất kỳ để xem nó có giữ nguyên định dạng không nhé!
Nên dùng cho case nào?
- Hiển thị nội dung "rich text" mà người dùng cần tương tác sao chép: Đây là trường hợp "chuẩn bài" nhất.
- Các trang "About Us", "Terms & Conditions", "Privacy Policy": Những trang này thường có nhiều định dạng và người dùng có thể muốn copy các điều khoản cụ thể.
- Thông báo, hướng dẫn chi tiết: Khi các em cần hiển thị thông báo quan trọng với các phần được nhấn mạnh và người dùng có thể muốn lưu lại.
Nhớ nhé các "dev tương lai", SelectableText.rich không chỉ giúp app của các em đẹp hơn, mà còn "nâng tầm" trải nghiệm người dùng lên một level mới. Hãy "thuần hóa" nó và biến nó thành "vũ khí" lợi hại trong kho tàng Flutter của mình! Hẹn gặp lại trong những bài học "chất như nước cất" lần sau!
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é!