aedev.net
  • Trang chủ
  • AI
  • Mobile
  • Flutter
  • React Native
  • Backend
  • DevOps
  • Hướng dẫn
No Result
View All Result
SAVED POSTS
aedev.net
  • Trang chủ
  • AI
  • Mobile
  • Flutter
  • React Native
  • Backend
  • DevOps
  • Hướng dẫn
No Result
View All Result
aedev.net
No Result
View All Result
Minh họa phát triển ứng dụng mobile đa nền tảng với Flutter

Flutter TextField: readOnly khác enabled như thế nào với nút suffix?

Suong Nguyen by Suong Nguyen
08/11/2026
in Flutter
0
585
SHARES
3.2k
VIEWS
Summarize with ChatGPTShare to Facebook

Một ô mã đơn hàng cần hiển thị nội dung cố định nhưng vẫn có nút thông tin ở cuối. Bạn đặt enabled: false để ngăn gõ chữ, rồi thấy nút ấy cũng không phản hồi. Nguyên nhân là trạng thái vô hiệu hóa áp dụng cả các widget con bên trong trường, không chỉ thao tác nhập văn bản.

Bài đối chiếu Flutter API ngày 08/11/2026. Đây là ví dụ Material được rà theo tài liệu; chưa chạy Flutter SDK, build ứng dụng, widget test hoặc kiểm tra trên thiết bị. Khi áp dụng vào dự án, kiểm tra lại ví dụ trên phiên bản SDK đang dùng và trên từng nền tảng mục tiêu.

Chọn khả năng cần giữ lại

Theo readOnly, đặt true ngăn người dùng sửa chữ bằng bàn phím hoặc shortcut nhưng vẫn cho chọn nội dung. Theo enabled, đặt false khiến trường bỏ qua tap và vô hiệu hóa cả suffixIcon.

Nếu không muốn người dùng thay đổi vùng chọn, xét thêm enableInteractiveSelection. Ba thuộc tính này giải quyết ba yêu cầu khác nhau. Trước khi chọn, hãy ghi rõ màn hình cần cho phép sửa, chọn chữ và bấm nút phụ ở mức nào.

Dựng hai trường để so sánh trực tiếp

Ví dụ dưới dùng hai controller có cùng nội dung ban đầu. Thay nội dung lib/main.dart của dự án Material thử nghiệm bằng code này để kiểm tra bằng tay:

import 'package:flutter/material.dart';

void main() => runApp(const MaterialApp(home: FieldDemo()));

class FieldDemo extends StatefulWidget {
  const FieldDemo({super.key});
  @override
  State<FieldDemo> createState() => _FieldDemoState();
}

class _FieldDemoState extends State<FieldDemo> {
  final readOnlyCode = TextEditingController(text: 'AE-2048');
  final disabledCode = TextEditingController(text: 'AE-2048');

  @override
  void dispose() {
    readOnlyCode.dispose();
    disabledCode.dispose();
    super.dispose();
  }

  Widget codeField(TextEditingController controller, bool enabled) {
    return TextField(
      controller: controller,
      enabled: enabled,
      readOnly: true,
      enableInteractiveSelection: true,
      decoration: InputDecoration(
        labelText: enabled ? 'Chỉ đọc' : 'Vô hiệu hóa',
        border: const OutlineInputBorder(),
        suffixIcon: IconButton(
          tooltip: 'Thông tin mã',
          icon: const Icon(Icons.info_outline),
          onPressed: () {
            ScaffoldMessenger.of(context).showSnackBar(
              const SnackBar(content: Text('Mã dùng để tra cứu đơn')),
            );
          },
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Hai trạng thái TextField')),
      body: Padding(
        padding: const EdgeInsets.all(24),
        child: Column(
          children: [
            codeField(readOnlyCode, true),
            const SizedBox(height: 24),
            codeField(disabledCode, false),
          ],
        ),
      ),
    );
  }
}

Cả hai trường đều đặt readOnly để ví dụ chỉ thay một biến so sánh: enabled. Trường đầu vẫn có cơ hội nhận tương tác chọn chữ và bấm nút; trường sau mô tả trạng thái vô hiệu hóa. Mã đơn là dữ liệu giả phục vụ giao diện, không gắn với API hoặc quyền truy cập đơn hàng.

Lập kế hoạch kiểm tra theo thao tác của người dùng

Thử nhập thêm ký tự vào trường đầu: nội dung không được thay đổi bởi thao tác nhập. Sau đó thử chọn phần mã và bấm nút thông tin; nút cần mở SnackBar. Ở trường thứ hai, kiểm tra cả tap vào nội dung lẫn nút suffix để xác nhận trạng thái vô hiệu hóa theo hợp đồng API.

Tiếp theo, chỉ đổi enableInteractiveSelection của trường đầu thành false. Kiểm tra lại việc chọn chữ và menu thao tác văn bản, đồng thời xác nhận nút thông tin vẫn đáp ứng yêu cầu màn hình. Đây là danh sách ca kiểm tra cần thực hiện trên môi trường của bạn, không phải kết quả thiết bị đã đo trong bài.

Đừng gộp trạng thái giao diện với dữ liệu

TextEditingController quản lý nội dung và cần được dispose khi không dùng nữa. Một trường readOnly vẫn có thể nhận thay đổi do code cập nhật controller. Vì vậy, ngăn thao tác nhập không phải là cách khóa dữ liệu ở tầng nghiệp vụ hoặc bảo vệ một API.

Trong sản phẩm, hãy tách câu hỏi “người dùng có thể gõ ở đây không?” khỏi “nguồn dữ liệu nào được phép cập nhật mã?”. Nếu mã thay đổi sau khi tải lại đơn, controller cần theo đúng luồng dữ liệu đã thiết kế. Ví dụ này chỉ giữ mã cố định để làm rõ tương tác UI.

Lỗi thường gặp và bước tiếp theo

  • Đặt enabled false rồi mong suffixIcon hoạt động: xem lại yêu cầu và dùng readOnly khi phù hợp.
  • Đặt readOnly true rồi giả định không còn thao tác chọn chữ: kiểm tra thêm thuộc tính selection.
  • Tạo controller mới ở mỗi lần build: nội dung ví dụ dùng controller thuộc State và giải phóng trong dispose.
  • Chỉ kiểm tra bằng chuột: bổ sung bàn phím, thao tác chọn chữ và trình đọc màn hình trên nền tảng mục tiêu.

Hãy giữ trường ở trạng thái enabled khi cần nút phụ hoạt động, rồi cấu hình riêng khả năng sửa và chọn nội dung. Cách tách này giúp yêu cầu UX được thể hiện trực tiếp trong props và dễ kiểm tra từng phần.

Nguồn tham khảo

  • Flutter: TextField.readOnly
  • Flutter: TextField.enabled
  • Flutter: TextField.enableInteractiveSelection
  • Flutter: TextEditingController

Tags: FlutterForm UITextField
SummarizeShare234
Suong Nguyen

Suong Nguyen

Related Stories

Minh họa phát triển ứng dụng mobile đa nền tảng với Flutter

Flutter InkWell: đặt nền và clip ở Material để thấy ripple đúng góc

by Suong Nguyen
07/11/2026
0

Dựng một thẻ tương tác với Material, Ink và InkWell, rồi kiểm tra vị trí vẽ ripple cùng vùng cắt bo góc.

Minh họa phát triển ứng dụng mobile đa nền tảng với Flutter

Flutter CheckboxListTile: value và selected không tự đồng bộ

by Suong Nguyen
06/11/2026
0

Cập nhật giá trị checkbox trong state, phân biệt selected của cả tile và khóa tương tác bằng onChanged null.

Minh họa phát triển ứng dụng mobile đa nền tảng với Flutter

Flutter Wrap: cho nhóm Chip xuống dòng và tách spacing với runSpacing

by Suong Nguyen
05/11/2026
0

Dựng một nhóm nhãn tự xuống dòng theo bề rộng khả dụng và kiểm tra đúng khoảng cách giữa Chip với giữa các dòng.

Minh họa phát triển ứng dụng mobile đa nền tảng với Flutter

Flutter ListView.separated: itemCount đếm dữ liệu, không đếm separator

by Suong Nguyen
04/11/2026
0

Tách itemBuilder và separatorBuilder để thêm khoảng cách giữa các dòng mà không lệch index hoặc tạo đường phân cách thừa ở cuối danh sách.

0 0 votes
Article Rating
Subscribe
Login
Notify of
guest
guest
0 Comments
Oldest
Newest Most Voted
AE Dev

AE Dev mang đến các bài viết về lập trình, mobile app, backend, frontend, WordPress và công nghệ, phù hợp cho developer muốn học hỏi và cập nhật kiến thức thực tế.

Recent Posts

  • React Native Platform.select: native và default được chọn theo thứ tự nào?
  • Phỏng vấn JavaScript: vì sao regex.test cùng chuỗi lại đổi kết quả?
  • Android TextView.setText(Int): số đếm có thể bị hiểu thành resource ID

Categories

  • AI
  • Backend
  • Đánh giá
  • DevOps
  • Flutter
  • Hướng dẫn
  • Lập trình Mobile
  • Phỏng vấn
  • React Native
  • Uncategorized

Weekly Newsletter

  • Buy JNews
  • Support Forum
  • Pre-sale Question
  • Contact Us

© 2026 AE Dev - Blog công nghệ, lập trình và chia sẻ kiến thức IT.

Welcome Back!

Login to your account below

Forgotten Password?

Retrieve your password

Please enter your username or email address to reset your password.

Log In
No Result
View All Result
  • Trang chủ
  • AI
  • Mobile
  • React Native
  • Backend
  • DevOps
  • Hướng dẫn
  • Flutter

© 2026 AE Dev - Blog công nghệ, lập trình và chia sẻ kiến thức IT.

wpDiscuz
0
0
Would love your thoughts, please comment.x
()
x
| Reply