# 不專業教學系列 - Flutter To-Do List 第二集(番外篇) 典型的亡羊補牢 >tag: `Flutter`、`Dart` 突然發現新增事項的時候,沒有輸入內容也可以新增資料 ![l2_5](https://hackmd.io/_uploads/r1AmGYXvC.gif) 這很明顯是有問題的,為了解決這問題,我們可以檢查文字輸入框的`Controller.Text` 是否為空字串。 ## 新增驗證器 宣告一個Key叫做`_todoFormKey`。 ```dart final GlobalKey<FormState> _todoFormKey = GlobalKey<FormState>(); ``` `GlobalKey<FormState>` 是用來唯一標識一個 Form 小部件的全局鍵。它允許我們在應用程式的其他部分訪問和操作這個表單的狀態。 接下來搞定文字輸入框,原本的文字輸入框是使用兩個`TextFeild`,現在我們改用`TextFormFeild`,修改`floatingActionButton`的`onPressed()`事件,將`AlertDialog`的`content`改寫: ```dart // display_todo_screen.dart content: Form( key: _todoFormKey, child: Column( mainAxisSize: MainAxisSize.min, children: <Widget>[ TextFormField( controller: _todoTitleController, decoration: const InputDecoration(hintText: "待辦事項名稱"), validator: (value) { if (value == null || value.isEmpty) { return '名稱不能為空!'; } // 如果內容只有一堆空格,一樣回傳錯誤 // value.trim() 是將字串中的空格都去掉 // 這樣我們就可以檢查處理過的字串有沒有問題 if (value.trim().isEmpty){ return '請輸入名稱!'; } return null; }, ), TextFormField( controller: _todoContentController, decoration: const InputDecoration(hintText: "待辦事項內容"), validator: (value) { if (value == null || value.isEmpty) { return '內容不能為空!'; } if (value.trim().isEmpty){ return '請輸入內容!'; } return null; }, ), ], ), ), ``` 可以看到我們增加了一個酷東西`validator`,它是對我們的TextFormField做驗證用的,可以在裡面對文字做一些條件限制。 為了要檢查文字是否符合規定,我們要修改`新增`按鈕的`onPressed()`事件: ```dart // display_todo_screen.dart // 調用_todoFormKey if (_todoFormKey.currentState!.validate()) { final provider = Provider.of<TodoProvider>(context, listen: false); provider.addTodo(TodoData( title: _todoTitleController.text, content: _todoContentController.text, isChecked: false, )); _todoTitleController.clear(); _todoContentController.clear(); Navigator.pop(context); } ``` ## 成果 來看看成果吧! ![l2_5_2](https://hackmd.io/_uploads/BJoyf5QwA.gif) ### 完整程式碼 今天只有改動`display_todo_screen.dart`,所以直接貼上完整程式碼: ```dart import 'package:flutter/material.dart'; import 'package:todo_app/models/todo_data.dart'; import 'package:todo_app/providers/todo_provider.dart'; import 'package:provider/provider.dart'; class DisplayTodoScreen extends StatefulWidget { const DisplayTodoScreen({super.key}); @override State<DisplayTodoScreen> createState() { return _DisplayTodoListState(); } } class _DisplayTodoListState extends State<DisplayTodoScreen> { final TextEditingController _todoTitleController = TextEditingController(); final TextEditingController _todoContentController = TextEditingController(); final GlobalKey<FormState> _todoFormKey = GlobalKey<FormState>(); @override Widget build(BuildContext context) { return Scaffold( body: Column( children: <Widget>[ Expanded( child: _showTodos(), ), _complectedButton(), ], ), floatingActionButton: FloatingActionButton( backgroundColor: Colors.blueGrey.shade600, onPressed: () { showDialog( context: context, builder: (context) { return AlertDialog( title: const Text("新增待辦事項", style: TextStyle(fontWeight: FontWeight.bold)), content: Form( key: _todoFormKey, child: Column( mainAxisSize: MainAxisSize.min, children: <Widget>[ TextFormField( controller: _todoTitleController, decoration: const InputDecoration(hintText: "待辦事項名稱"), validator: (value) { if (value == null || value.isEmpty) { return '名稱不能為空!'; } if (value.trim().isEmpty){ return '請輸入名稱!'; } return null; }, ), TextFormField( controller: _todoContentController, decoration: const InputDecoration(hintText: "待辦事項內容"), validator: (value) { if (value == null || value.isEmpty) { return '內容不能為空!'; } if (value.trim().isEmpty){ return '請輸入內容!'; } return null; }, ), ], ), ), actions: <Widget>[ ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: Colors.blueGrey.shade600, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8.0), ), ), onPressed: () { Navigator.pop(context); _todoTitleController.clear(); _todoContentController.clear(); }, child: const Text( "取消", style: TextStyle(color: Colors.white), ), ), ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: Colors.blueGrey.shade600, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8.0), ), ), onPressed: () { // 先檢查文字 if (_todoFormKey.currentState!.validate()) { final provider = Provider.of<TodoProvider>(context, listen: false); provider.addTodo(TodoData( title: _todoTitleController.text, content: _todoContentController.text, isChecked: false, )); _todoTitleController.clear(); _todoContentController.clear(); Navigator.pop(context); } }, child: const Text( "新增", style: TextStyle(color: Colors.white), ), ), ], ); }, ); }, child: const Icon(Icons.playlist_add_rounded, color: Colors.white), ), ); } Widget _showTodos() { // 取得 Provider 中的資料 final provider = Provider.of<TodoProvider>(context); return ListView.builder( itemCount: provider.todoList.length, itemBuilder: (BuildContext context, int index) { return Column( children: <Widget>[ ListTile( // 改用 Provider 來顯示待辦事項 title: Text(provider.todoList[index].title, style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 20)), subtitle: Text(provider.todoList[index].content, style: const TextStyle(fontSize: 16)), leading: Checkbox( activeColor: Colors.blueGrey.shade600, checkColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8.0), ), value: provider.todoList[index].isChecked, onChanged: (bool? value) { // 呼叫 Provider 來更新待辦事項的狀態 provider.updateTodoCheck(index, value!); }, ), trailing: IconButton( icon: const Icon(Icons.delete, color: Colors.blueGrey), onPressed: () { provider.removeTodoAt(index); }, ), ), const Divider(), ], ); }, ); } Widget _complectedButton() { return Padding( padding: const EdgeInsets.only(top: 10, bottom: 22), child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: Colors.blueGrey.shade600, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8.0), ), minimumSize: const Size(130, 50), maximumSize: const Size(130, 50), ), onPressed: () { // 呼叫 Provider 來清除已完成的待辦事項 Provider.of<TodoProvider>(context, listen: false).clearCompleted(); }, child: const Text( "清除完成事項", style: TextStyle(color: Colors.white, fontSize: 12), )), ); } } ``` ## 結語 實在是沒想到忘記去檢查文字輸入時的狀況,導致有奇怪的東西混進todo了XD 如果沒處理好,到了後面要吧資料存進資料庫時就有可能GG了哈哈,趕快去修復你的To-Do List吧! 感謝觀看!