Menu
CoddyTech

Danh sách việc cần làm: Không để trống công việc

Thuộc phần Dự án trong hành trình Flutter của Coddy. Bài học 5 trên 36.

Chạm vào nút thêm khi chưa nhập gì, danh sách sẽ có một hàng trống. Chỉ nhập dấu cách, bạn cũng sẽ có một hàng trông như trống. Ứng dụng thực tế sẽ từ chối cả hai trường hợp và cho biết lý do.

trim() trả về văn bản đã bỏ khoảng trắng ở đầu và cuối. Nếu không còn gì, nghĩa là không có công việc nào: hãy hiển thị SnackBar rồi return trước khi thêm bất cứ thứ gì.

final title = _controller.text.trim();
if (title.isEmpty) {
  ScaffoldMessenger.of(context).showSnackBar(
    const SnackBar(content: Text('Type a task first')),
  );
  return;
}

Dùng title cho công việc mới nữa, để " Call mom " được lưu thành "Call mom".

Lệnh return sớm như thế này được gọi là lệnh bảo vệ. Nó giữ trường hợp không hợp lệ ở đầu hàm, còn phần còn lại của hàm chỉ xử lý đầu vào hợp lệ.

challenge icon

Thử thách

Trung bình

Từ chối thêm công việc trống trong _addTask:

  • đọc nội dung trường bằng trim()
  • nếu không còn gì, hiển thị SnackBar với nội dung Type a task first và không thêm gì
  • nếu không, thêm tiêu đề đã được loại bỏ khoảng trắng thừa

Tự làm thử

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(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.teal),
      ),
      home: const TodoScreen(),
    );
  }
}

class Task {
  Task(this.title);

  final String title;
  bool done = false;
}

class TodoScreen extends StatefulWidget {
  const TodoScreen({super.key});

  @override
  State<TodoScreen> createState() => _TodoScreenState();
}

class _TodoScreenState extends State<TodoScreen> {
  final List<Task> _tasks = [Task('Buy milk'), Task('Walk the dog'), Task('Read a book')];
  final _controller = TextEditingController();

  int get _left => _tasks.where((task) => !task.done).length;

  void _addTask() {
    setState(() {
      _tasks.add(Task(_controller.text));
    });
    _controller.clear();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('My Tasks')),
      body: Column(
        children: [
          Padding(
            padding: const EdgeInsets.fromLTRB(16, 16, 16, 8),
            child: Row(
              children: [
                Expanded(
                  child: TextField(
                    controller: _controller,
                    decoration: const InputDecoration(
                      labelText: 'New task',
                      border: OutlineInputBorder(),
                    ),
                  ),
                ),
                const SizedBox(width: 8),
                IconButton.filled(
                  onPressed: _addTask,
                  icon: const Icon(Icons.add),
                ),
              ],
            ),
          ),
          Padding(
            padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
            child: Row(
              children: [
                Text(
                  _left == 1 ? '1 task left' : '$_left tasks left',
                  style: Theme.of(context).textTheme.titleSmall,
                ),
              ],
            ),
          ),
          Expanded(
            child: ListView.builder(
              itemCount: _tasks.length,
              itemBuilder: (context, index) {
                final task = _tasks[index];
                return ListTile(
                  leading: Checkbox(
                    value: task.done,
                    onChanged: (value) {
                      setState(() {
                        task.done = value!;
                      });
                    },
                  ),
                  title: Text(
                    task.title,
                    style: task.done
                        ? const TextStyle(
                            decoration: TextDecoration.lineThrough,
                            color: Colors.grey,
                          )
                        : null,
                  ),
                );
              },
            ),
          ),
        ],
      ),
    );
  }
}
quiz iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

Tất cả bài học trong Dự án

Tự luyện tập: Trình biên dịch Flutter online