網站首頁 編程語言 正文
正文
今天,我們將使用 Flutter
構建一個動態的 todo list
的應用。
開發完成的效果如下:
我們直接進入正題。
基礎 Flutter 應用腳手架
# 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
包。
import 'package:flutter/material.dart';
下一步,我們得有一個主要的方法。在這個例子中,它將返回 TodoApp
實例。
void main() => runApp( new TodoApp(), );
這個 TodoApp
應該是一個 statelessWidget
。這將會是我們列表的骨架
class TodoApp extends StatelessWidget { @override Widget build(BuildContext context) { return new MaterialApp( title: 'Todo list', home: new TodoList(), ); } }
正如你所見,我返回了一個 MaterialApp
實例,它具有一個 title
屬性和一個 home
功能。這個 home
函數返回一個 TodoList
實例。這個 TodoList
類才是我們控制的列表項。
class TodoList extends StatefulWidget { @override _TodoListState createState() => new _TodoListState(); }
等等,這是什么?所有的掛件都會調用一個狀態去知道將要發生什么和渲染什么。在這個例子中,我們調用了 _TodoListState
。這將包含應用中的列表及其運行邏輯。
class _TodoListState extends State<TodoList> { final TextEditingController _textFieldController = TextEditingController(); final List<Todo> _todos = <Todo>[]; @override Widget build(BuildContext context) { // Widget template comes here } // Other functions }
接下來,創建列表變量。
final List<Todo> _todos = <Todo>[];
也許你已經注意到了,我們定義了這個列表的類型是 Todo
,但 Flutter
怎么知道 Todo
長是什么樣呢?
Flutter
并不會知道,所以我們得創建一個類來定義。如下:
class Todo { Todo({required this.name, required this.checked}); final String name; bool checked; }
這跟 typescript
中的類型定義很像。我們告訴 flutter
一個 todo 項應該包含什么,什么字段是必須的。在我們的案例中,我們有名字和 checked
兩個狀態屬性。
回到 _TodoListState
中,我們開始讓我們的掛件展示點東西。
@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
是我們列表項的單獨體現。
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
。
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
函數長什么樣:
void _addTodoItem(String name) { setState(() { _todos.add(Todo(name: name, checked: false)); }); _textFieldController.clear(); }
這函數比你想象中的簡單,是吧。
列表項添加狀態
最后一部分是,我們應該為列表項進行標記。我們需要一個處理函數 _handleTodoChange
:
void _handleTodoChange(Todo todo) { setState(() { todo.checked = !todo.checked; }); }
這里我們只是改變了其列表項的狀態。
完整的代碼如下:
// 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<TodoList> { final TextEditingController _textFieldController = TextEditingController(); final List<Todo> _todos = <Todo>[]; @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<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); }, ), ], ); }, ); } } 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
原文鏈接:https://juejin.cn/post/7147496701065068574
相關推薦
- 2022-09-15 Android自定義ViewGroup實現選擇面板_Android
- 2022-06-10 ASP.NET?Core使用AutoMapper組件_實用技巧
- 2022-02-13 C++ SFINAE簡介和std::enable_if_t的簡單使用
- 2022-11-06 React?hook實現簡單的websocket封裝方式_React
- 2022-08-02 深入了解Golang的map增量擴容_Golang
- 2022-05-18 C語言自定義類型超詳細梳理之結構體?枚舉?聯合體_C 語言
- 2022-11-06 React中useEffect與生命周期鉤子函數的對應關系說明_React
- 2022-12-06 C++如何將字符串顛倒輸出_C 語言
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支