Một dòng cài đặt có ô được tick nhưng phần chữ không mang trạng thái selected như bạn mong muốn. Hoặc callback đã chạy mà dấu tick vẫn quay lại giá trị cũ. CheckboxListTile nhận giá trị từ cha; nó không tự đồng bộ value, selected và state của ứng dụng.
Bài đối chiếu API Flutter hiện hành ngày 06/11/2026. Ví dụ Dart được rà thủ công theo các thuộc tính của CheckboxListTile, chưa chạy Flutter SDK, build app hoặc thao tác trên thiết bị. Các bước kiểm tra bên dưới là checklist để bạn chạy trong project; không có ảnh chụp hoặc kết quả widget test đã thu được.
Ba phần cần giữ tách biệt
value quyết định trạng thái checkbox. selected điều khiển cách tile thể hiện trạng thái chọn theo theme. onChanged nhận giá trị mới được đề nghị để phần cha cập nhật state và dựng lại widget.
Nếu muốn tile trông selected khi checkbox được tick, truyền cùng một biến cho hai thuộc tính. Nếu thiết kế không cần đổi cách thể hiện của tile, có thể giữ selected ở mặc định. Đừng dùng selected làm nơi lưu một bản sao state rồi cập nhật nó riêng.
1. Demo có một nguồn dữ liệu
Đặt đoạn sau vào lib/main.dart của project Flutter đã tạo. Ví dụ chỉ dùng Material, không có package hoặc thao tác lưu trữ bên ngoài:
import 'package:flutter/material.dart';
void main() => runApp(const MaterialApp(home: CheckboxDemo()));
class CheckboxDemo extends StatefulWidget {
const CheckboxDemo({super.key});
@override
State<CheckboxDemo> createState() => _CheckboxDemoState();
}
class _CheckboxDemoState extends State<CheckboxDemo> {
bool _enabled = false;
bool _locked = false;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Cài đặt minh họa')),
body: ListView(
children: [
CheckboxListTile(
title: const Text('Hiện phần giải thích'),
subtitle: const Text('Giá trị được giữ trong state của màn hình'),
value: _enabled,
selected: _enabled,
tristate: false,
onChanged: _locked ? null : (bool? next) {
if (next == null) return;
setState(() => _enabled = next);
},
),
Text(_enabled ? 'Giải thích đang bật' : 'Giải thích đang tắt'),
TextButton(
onPressed: () => setState(() => _locked = !_locked),
child: Text(_locked ? 'Cho phép chỉnh sửa' : 'Khóa chỉnh sửa'),
),
],
),
);
}
}
_enabled nằm trên State, không được khai báo lại thành false trong build. Callback lấy giá trị next được widget gửi, cập nhật nguồn dữ liệu rồi setState yêu cầu dựng lại giao diện. Mẫu không dùng việc gọi callback như bằng chứng state đã tự đổi.
Scaffold cung cấp môi trường Material phù hợp cho tile. Text bên dưới là một cách kiểm tra cùng dữ liệu đang điều khiển dấu tick. Nếu dấu tick và Text lệch nhau, tìm lại các nguồn state hoặc điều kiện truyền value.
2. Khóa chỉnh sửa mà không đổi lựa chọn
_locked quyết định có truyền callback hay null. Khi onChanged bằng null, tile được hiển thị ở trạng thái disabled. Việc khóa chỉnh sửa không tự đổi _enabled thành false: một lựa chọn đang bật vẫn có thể bị khóa.
Không dùng callback rỗng để biểu diễn disabled. Callback rỗng vẫn là callback khác null, trong khi ứng dụng không cập nhật giá trị nên thao tác trông như không có hiệu lực. Nếu người dùng không được sửa, thể hiện đúng bằng API disabled của component.
Kiểm tra hai trạng thái và cả vùng nhãn
Đầu tiên tick từ false sang true, kiểm tra Text trạng thái đi cùng _enabled. Chạm vào phần nhãn của tile để kiểm tra toàn dòng tương tác như thiết kế. Sau đó khóa chỉnh sửa khi đang true, thử thao tác lại và xác nhận lựa chọn vẫn được giữ.
Mở khóa rồi tắt lựa chọn, sau đó thử lại ở false. So sánh cả dấu tick và cách theme thể hiện selected; không khẳng định một màu cụ thể sẽ xuất hiện trên mọi theme. Khi đưa vào app, kiểm tra chữ lớn, chiều rộng hẹp và trải nghiệm screen reader phù hợp.
Nullable của callback không buộc dùng ba trạng thái
Chữ ký onChanged nhận bool?, nhưng demo đặt tristate false và dùng value không null. Nếu bật tristate, null là trạng thái thứ ba có ý nghĩa riêng; không chuyển null về false một cách máy móc khi sản phẩm cần phân biệt “chưa xác định”.
Khi thêm lưu cài đặt, tách việc cập nhật UI khỏi xác nhận lưu thành công. Mẫu hiện chỉ giữ state trong màn hình; không có persistence, API hay khả năng phục hồi sau khi process bị tạo lại. Bạn có thể bắt đầu bằng demo này để kiểm tra giao diện, rồi bổ sung luồng lưu với lỗi và trạng thái chờ được thiết kế rõ ràng.


