Chào các dân chơi hệ code, anh Creyt lại lên sóng đây! Hôm nay, chúng ta sẽ cùng nhau 'mổ xẻ' một thằng cu tưởng chừng nhỏ bé nhưng lại có võ công thâm hậu trong Flutter: SelectionContainer. Nghe tên thì 'học thuật' vậy thôi, chứ nó chính là 'vệ sĩ' bảo kê cho mấy cái chữ của bạn được quyền 'đi du lịch' (copy-paste) từ app này sang app khác đấy!
1. SelectionContainer là gì mà 'uy tín' vậy?
Thôi bỏ qua mấy cái định nghĩa khô khan trên docs đi. Anh em Gen Z hiểu nôm na thế này: Tưởng tượng app của bạn là một khu vườn thượng uyển đẹp mê hồn, đầy rẫy những bông hoa (là các đoạn text, thông tin). Mặc định, bạn chỉ có thể ngắm hoa thôi, chứ không được phép 'hái' (chọn và copy) đâu nhé. Khó chịu không?
SelectionContainer chính là cái biển báo 'Tự do hái hoa' mà bạn cắm vào những khu vực cụ thể trong vườn. Nó không phải là bông hoa, cũng không phải là cái kéo để hái, mà nó là người cấp phép, định danh khu vực nào được quyền thao tác chọn văn bản.
Nói cách khác, trong Flutter, khi bạn muốn người dùng có thể chọn và sao chép một đoạn văn bản hay một nhóm văn bản mà theo mặc định nó không cho phép (hoặc bạn muốn kiểm soát chặt chẽ hơn), thì SelectionContainer chính là 'chân ái'. Nó là một widget cấp thấp, giúp bạn đánh dấu một khu vực cụ thể trong cây widget của mình là 'có thể lựa chọn' (selectable region).
2. Dùng để làm gì? 'Quyền năng' copy-paste trong tầm tay!
Tại sao lại phải dùng nó khi Text widget trong MaterialApp thường đã cho phép chọn rồi? À, đây mới là cái hay này:
- Kiểm soát vùng chọn: Đôi khi bạn có một
Columnchứa nhiềuTextwidget, và bạn muốn người dùng có thể chọn tất cả chúng như một khối duy nhất, không phải từng cái một. SelectionContainer giúp bạn làm điều đó. - Cho các widget không phải
Text: Bạn tạo một widget tùy chỉnh hiển thị văn bản, nhưng nó không phải làTextwidget truyền thống. Mặc định nó sẽ không cho chọn. SelectionContainer sẽ 'phù phép' cho nó. - Tắt/bật linh hoạt: Muốn một đoạn văn bản lúc thì cho chọn, lúc thì không? SelectionContainer là công cụ của bạn.
- Hỗ trợ
RichTextvà các layout phức tạp: Khi bạn dùngRichTextđể tạo ra các đoạn văn bản với nhiều style khác nhau, SelectionContainer sẽ đảm bảo trải nghiệm chọn mượt mà.
Nói chung, nó là công cụ để bạn 'thẩm quyền hóa' việc copy-paste trong app của mình, biến những nội dung 'bất khả xâm phạm' thành 'có thể trích xuất' một cách dễ dàng.
3. Code Ví Dụ Minh Họa: 'Chọn' ngay và luôn!
Để anh em thấy rõ 'sức mạnh' của nó, chúng ta cùng xem vài ví dụ 'thực chiến' nhé. Nhớ là SelectionContainer thường được dùng kết hợp với SelectionArea (là một widget 'cao cấp' hơn, tiện lợi hơn, bọc SelectionContainer bên trong) để quản lý vùng chọn.
Ví dụ 1: Làm cho một đoạn văn bản đơn giản có thể chọn (dù Text thường đã chọn được)
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: 'SelectionContainer 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('SelectionContainer Basic')),
body: Center(
child: SelectionContainer.disabled(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'Đoạn này không chọn được đâu nha!',
style: TextStyle(fontSize: 20, color: Colors.red),
),
const SizedBox(height: 20),
// Dùng SelectionContainer để bọc một vùng có thể chọn
SelectionContainer.selectable(
child: const Text(
'Anh Creyt chào Gen Z! Đoạn này thì chọn thoải mái nhé.',
style: TextStyle(fontSize: 20, color: Colors.green),
),
),
const SizedBox(height: 20),
const Text(
'Còn đoạn dưới đây lại vô hiệu hóa chọn.',
style: TextStyle(fontSize: 18),
),
],
),
),
),
);
}
}
Trong ví dụ trên, anh dùng SelectionContainer.disabled ở ngoài cùng để vô hiệu hóa toàn bộ khả năng chọn văn bản cho Column. Sau đó, anh dùng SelectionContainer.selectable để ghi đè lại và chỉ cho phép chọn đoạn Text cụ thể bên trong nó. Thấy 'quyền năng' chưa?
Ví dụ 2: Kết hợp với SelectionArea để quản lý vùng chọn lớn hơn
SelectionArea là một wrapper tiện lợi hơn, nó tự động quản lý SelectionContainer cho bạn. Thường thì bạn sẽ bọc toàn bộ Scaffold body hoặc thậm chí MaterialApp bằng SelectionArea.
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: 'SelectionArea 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('SelectionArea & Container')),
body: SelectionArea(
// Mọi thứ trong SelectionArea này đều có thể chọn được mặc định
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
const Text(
'Đây là một đoạn văn bản dài mà bạn có thể chọn và sao chép thoải mái. Nó nằm trong SelectionArea.',
style: TextStyle(fontSize: 18),
),
const SizedBox(height: 20),
// Dù nằm trong SelectionArea, nhưng SelectionContainer.disabled
// sẽ ghi đè và vô hiệu hóa chọn cho đoạn này.
SelectionContainer.disabled(
child: const Text(
'Đoạn văn bản này lại bị anh Creyt 'khóa' không cho chọn, dù nó nằm trong vùng SelectionArea lớn.',
style: TextStyle(fontSize: 18, fontStyle: FontStyle.italic, color: Colors.grey),
),
),
const SizedBox(height: 20),
const Text(
'Còn đây là một đoạn khác, vẫn trong SelectionArea, nên vẫn chọn được như thường.',
style: TextStyle(fontSize: 18),
),
const SizedBox(height: 20),
// Ví dụ về một widget tùy chỉnh không phải Text,
// nhưng muốn nó có thể chọn được nội dung bên trong.
SelectionContainer.selectable(
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.lightBlue.shade50,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.blueAccent),
),
child: const Text(
'Đây là nội dung từ một custom widget mà bạn vẫn có thể chọn. Tuyệt vời không?',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
),
),
],
),
),
),
);
}
}
Qua ví dụ này, anh em thấy rõ cách SelectionContainer có thể 'ghi đè' lên SelectionArea ở cấp cao hơn để kiểm soát từng vùng nhỏ một. Nó giống như bạn có một chính sách chung cho cả nước (SelectionArea), nhưng lại có những quy định đặc biệt cho từng tỉnh (SelectionContainer) vậy.
4. Mẹo (Best Practices) từ 'lão làng' Creyt
- Ưu tiên
SelectionAreatrước: Đối với phần lớn các trường hợp, bạn chỉ cần bọc toàn bộScaffoldbody hoặcMaterialAppbằngSelectionArea. Nó sẽ tự động làm cho tất cảTextwidget bên trong có thể chọn được, cực kỳ tiện lợi. SelectionContainercho trường hợp 'đặc biệt': Chỉ dùngSelectionContainerkhi bạn cần kiểm soát cực kỳ chi tiết: vô hiệu hóa chọn ở một vùng cụ thể trongSelectionArealớn, hoặc bật chọn cho một widget custom không phảiText.- Đừng lạm dụng: Không cần thiết phải bọc từng
Textwidget nhỏ bằngSelectionContainernếu chúng đã nằm trong mộtSelectionArealớn hơn. Việc này có thể gây dư thừa và đôi khi ảnh hưởng nhẹ đến hiệu năng (dù thường không đáng kể). - Hiểu cách hoạt động của SelectionManager: Mặc định,
MaterialAppvàCupertinoAppđã có mộtDefaultSelectionManagerlo vụ chọn văn bản rồi.SelectionAreavàSelectionContainerhoạt động trên nền tảng đó để cung cấp sự linh hoạt hơn. - Accessibility (Khả năng tiếp cận): Việc cho phép chọn và sao chép văn bản là một điểm cộng lớn cho khả năng tiếp cận. Người dùng có thể dễ dàng lấy thông tin để dùng cho các mục đích khác (ví dụ: tra cứu, chia sẻ, lưu trữ).
5. Ứng dụng/Website đã 'thẩm thấu' SelectionContainer
Thực ra, SelectionContainer là một widget nội bộ của Flutter để cho phép chức năng chọn văn bản, chứ không phải là một thành phần UI hiển thị rõ ràng. Tuy nhiên, bất kỳ ứng dụng Flutter nào mà bạn có thể chọn và sao chép văn bản từ đó đều đang gián tiếp sử dụng hoặc dựa vào cơ chế tương tự SelectionContainer để hoạt động. Ví dụ:
- Các ứng dụng đọc tin tức/blog (Medium, VnExpress, Báo Mới): Bạn đọc một bài báo, thấy đoạn nào hay thì bôi đen, copy để chia sẻ. Đó chính là SelectionContainer đang làm nhiệm vụ.
- Ứng dụng nhắn tin (Zalo, Messenger, WhatsApp): Bạn copy một câu nói 'bá đạo' của bạn bè để gửi cho đứa khác. SelectionContainer 'góp công' vào đó.
- Ứng dụng ghi chú (Google Keep, Notion): Chắc chắn phải có chức năng chọn/copy rồi, nếu không thì ghi chú làm gì?
- Các trang thương mại điện tử (Shopee, Lazada): Bạn muốn copy tên sản phẩm, mô tả để tìm kiếm thêm thông tin. SelectionContainer là 'người hùng thầm lặng'.
Nói chung, hễ chỗ nào bạn thao tác 'nhấn giữ' (long press) và kéo để bôi đen chữ được, thì y như rằng có 'bóng dáng' của một SelectionContainer nào đó đang làm nhiệm vụ của nó!
6. Thử nghiệm và Nên dùng cho case nào?
Nên dùng khi:
- Xây dựng các widget hiển thị văn bản tùy chỉnh: Nếu bạn không dùng
Textwidget mà tự vẽ chữ, hoặc dùng các thư viện render text đặc biệt, bạn sẽ cầnSelectionContainerđể kích hoạt tính năng chọn. - Quản lý vùng chọn phức tạp: Khi bạn muốn một
Columnchứa nhiềuTextwidget được chọn như một khối duy nhất, hoặc bạn có các vùng văn bản đan xen giữa có thể chọn và không thể chọn. - Tắt chọn cho một số vùng nhất định: Bạn có một
SelectionAreabao quát cả app, nhưng muốn một số đoạn văn bản không được phép chọn (ví dụ: số điện thoại nội bộ, mã bí mật...). DùngSelectionContainer.disabled. - Tăng cường khả năng tiếp cận: Đảm bảo người dùng có thể dễ dàng trích xuất thông tin từ ứng dụng của bạn.
Không nên dùng (hoặc nên dùng SelectionArea thay thế) khi:
- Tất cả
Textwidget trongMaterialApp: Mặc định chúng đã có thể chọn được rồi, trừ khi bạn muốn vô hiệu hóa chúng. - Bạn chỉ cần bật chọn cho toàn bộ màn hình: Dùng
SelectionAreabọcScaffoldbody là đủ, không cầnSelectionContainercho từng item nhỏ. - Nội dung hoàn toàn không liên quan đến văn bản: Ví dụ: một hình ảnh, một nút bấm (button), một icon. Dù có bọc
SelectionContainercũng chẳng có gì để chọn đâu nhé!
Nhớ nhé anh em, SelectionContainer không phải là 'siêu nhân' mà là 'người điều phối' giúp cho việc chọn văn bản trong app của bạn trở nên linh hoạt và mạnh mẽ hơn. Hiểu rõ nó, bạn sẽ có thêm một 'vũ khí' lợi hại để làm app Flutter 'xịn xò' hơn rất nhiều! Chúc anh em code vui vẻ, gặp lại trong bài giảng 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é!