Flutter 構建一個 todo list 應用
我報名參加金石計劃1期挑戰——瓜分10萬獎池,這是我的第3篇文章,點選檢視活動詳情
今天,我們將使用 Flutter
構建一個動態的 todo list
的應用。
開發完成的效果如下:
我們直接進入正題。
基礎 Flutter 應用腳手架
```dart
create new project
flutter create flutter_todo_app
navigate to project
cd flutter_todo_app
run flutter
flutter run ```
我們清除檔案 lib/main.dart
,從頭開始開發。
main.dart 這個檔案是 Flutter 應用的入口檔案。在這篇文章中,我將僅僅使用這個檔案來開發。
首先,我們先匯入 material
包。
dart
import 'package:flutter/material.dart';
下一步,我們得有一個主要的方法。在這個例子中,它將返回 TodoApp
例項。
dart
void main() => runApp(
new TodoApp(),
);
這個 TodoApp
應該是一個 statelessWidget
。這將會是我們列表的骨架
dart
class TodoApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Todo list',
home: new TodoList(),
);
}
}
正如你所見,我返回了一個 MaterialApp
例項,它具有一個 title
屬性和一個 home
功能。這個 home
函式返回一個 TodoList
例項。這個 TodoList
類才是我們控制的列表項。
dart
class TodoList extends StatefulWidget {
@override
_TodoListState createState() => new _TodoListState();
}
等等,這是什麼?所有的掛件都會呼叫一個狀態去知道將要發生什麼和渲染什麼。在這個例子中,我們呼叫了 _TodoListState
。這將包含應用中的列表及其執行邏輯。
```dart
class _TodoListState extends State
@override Widget build(BuildContext context) { // Widget template comes here }
// Other functions } ```
接下來,建立列表變數。
dart
final List<Todo> _todos = <Todo>[];
也許你已經注意到了,我們定義了這個列表的型別是 Todo
,但 Flutter
怎麼知道 Todo
長是什麼樣呢?
Flutter
並不會知道,所以我們得建立一個類來定義。如下:
dart
class Todo {
Todo({required this.name, required this.checked});
final String name;
bool checked;
}
這跟 typescript
中的型別定義很像。我們告訴 flutter
一個 todo 項應該包含什麼,什麼欄位是必須的。在我們的案例中,我們有名字和 checked
兩個狀態屬性。
回到 _TodoListState
中,我們開始讓我們的掛件展示點東西。
dart
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('Todo list'),
),
body: ListView(
padding: EdgeInsets.symmetric(vertical: 8.0),
children: _todos.map((Todo todo) {
return TodoItem(
todo: todo,
onTodoChanged: _handleTodoChange,
);
}).toList(),
),
floatingActionButton: FloatingActionButton(
onPressed: () => _displayDialog(),
tooltip: 'Add Item',
child: Icon(Icons.add)),
);
}
讓我們看看上面發生了什麼。我們返回了應用的一個腳手架,在腳手架上,我們添加了一個包含標題的 appBar
的屬性。我們定義了 body
屬性,這將存放 ListView
元件。
在上面程式碼片段中,通過 map
方法返回每個元素的 TodoItem
。
然後,在應用的底部,我們定義了一個按鈕。當按鈕被點選時候,將呼叫 _displayDialog
方法。
到目前為止,我們還需要完成下面的程式碼片段:
- 建立
TodoItem
- 定義一個
_displayDialog
函式 - 定義一個
_handleTodoChange
函式
讓我們一個一個來解決。
建立 TodoItem
TodoItem
是我們列表項的單獨體現。
```dart class TodoItem extends StatelessWidget { TodoItem({ required this.todo, required this.onTodoChanged, }) : super(key: ObjectKey(todo));
final Todo todo; final onTodoChanged;
TextStyle? _getTextStyle(bool checked) { if (!checked) return null;
return TextStyle(
color: Colors.black54,
decoration: TextDecoration.lineThrough,
);
}
@override Widget build(BuildContext context) { return ListTile( onTap: () { onTodoChanged(todo); }, leading: CircleAvatar( child: Text(todo.name[0]), ), title: Text(todo.name, style: _getTextStyle(todo.checked)), ); } } ```
正如你所見,我們傳遞一個 todo
和 onTodoChanged
進來。
然後我們定義了一個 TextStyle
去處理列表項是否被勾選。
然後我們使用 ListTile
掛件來展示內容和新增點選事件。
展示 Dialog 去新增列表項
點選應用的右下角的按鈕,將會調起 _displayDialog
方法。
這將調起一個帶有文字框的對話方塊。當點選確認的時候,將以文字框的內容基礎新增一個新的列表項。
在 _TodoListState
中建立 _displayDialog
。
dart
Future<void> _displayDialog() async {
return showDialog<void>(
context: context,
barrierDismissible: false, // user must tap button!
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Add a new todo item'),
content: TextField(
controller: _textFieldController,
decoration: const InputDecoration(hintText: 'Type your new todo'),
),
actions: <Widget>[
TextButton(
child: const Text('Add'),
onPressed: () {
Navigator.of(context).pop();
_addTodoItem(_textFieldController.text);
},
),
],
);
},
);
}
Flutter
中的 Future
表明在將來的某個時候將返回潛在的值或者錯誤資訊。在我們的案例中,將會返回使用者輸入的值。
對話方塊中有一個動作,就是當我們點選按鈕的時候,將會關閉對話方塊並且呼叫 _addTodoItem
函式。
我們看看 _addTodoItem
函式長什麼樣:
dart
void _addTodoItem(String name) {
setState(() {
_todos.add(Todo(name: name, checked: false));
});
_textFieldController.clear();
}
這函式比你想象中的簡單,是吧。
列表項新增狀態
最後一部分是,我們應該為列表項進行標記。我們需要一個處理函式 _handleTodoChange
:
dart
void _handleTodoChange(Todo todo) {
setState(() {
todo.checked = !todo.checked;
});
}
這裡我們只是改變了其列表項的狀態。
完整的程式碼如下:
```dart // lib/main.dart import 'package:flutter/material.dart';
class Todo { Todo({required this.name, required this.checked}); final String name; bool checked; }
class TodoItem extends StatelessWidget { TodoItem({ required this.todo, required this.onTodoChanged, }) : super(key: ObjectKey(todo));
final Todo todo; final onTodoChanged;
TextStyle? _getTextStyle(bool checked) { if (!checked) return null; return TextStyle( color: Colors.black54, decoration: TextDecoration.lineThrough, );
}
@override Widget build(BuildContext context) { return ListTile( onTap: () { onTodoChanged(todo); }, leading: CircleAvatar( child: Text(todo.name[0]), ), title: Text(todo.name, style: _getTextStyle(todo.checked)), ); } }
class TodoList extends StatefulWidget { @override _TodoListState createState() => new _TodoListState(); }
class _TodoListState extends State
@override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar( title: new Text('Todo list'), ), body: ListView( padding: EdgeInsets.symmetric(vertical: 8.0), children: _todos.map((Todo todo) { return TodoItem( todo: todo, onTodoChanged: _handleTodoChange, ); }).toList(), ), floatingActionButton: FloatingActionButton( onPressed: () => _displayDialog(), tooltip: 'Add Item', child: Icon(Icons.add)), ); }
void _handleTodoChange(Todo todo) { setState(() { todo.checked = !todo.checked; }); }
void _addTodoItem(String name) { setState(() { _todos.add(Todo(name: name, checked: false)); }); _textFieldController.clear(); }
Future
class TodoApp extends StatelessWidget { @override Widget build(BuildContext context) { return new MaterialApp( title: 'Todo list', home: new TodoList(), ); } }
void main() => runApp(new TodoApp()); ```
本文采用的是意譯的方式。原文連結 - Build a todo list app with Flutter
推薦閱讀
- 前端開發中 5 個很讚的資源
- 懂點心理學 - 馬太效應
- Flutter 構建一個 todo list 應用
- Dart 知識點 - 資料型別
- Dart 知識點 - 混入 Mixin
- Dart 知識點 - 集合 List, Set, Map
- Flutter - 使用 push(), pop() 和路由進行導航
- Dart 知識點 - 面向物件基礎
- Flutter: Stateful 掛件 vs Stateless 掛件
- Flutter 實現登入 UI
- Dart 知識點 - 抽象類和介面
- 自 2020 年以來全球的開源商業化軟體融資情況
- IstioCon 2022 回顧及錄影、PPT 分享
- 網頁實現 1CM 物理長度
- Flutter 開發出現的那些 Bugs 和解決方案「持續更新... 」
- 仿寫新聞客戶端
- Beyond Istio OSS —— Istio 服務網格的現狀及未來
- 在外企的工作生活「年中總結」
- 如何在 Istio 中整合 SPRIRE?
- Javascript尾遞迴程式設計