我报名参加金石方案1期应战——分割10万奖池,这是我的第3篇文章,点击查看活动详情

今天,咱们将运用 Flutter 构建一个动态todo list 的使用。

开发完结的效果如下:

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)),
    );
  }
}

正如你所见,咱们传递一个 todoonTodoChanged 进来。

然后咱们界说了一个 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

推荐阅览

  • 求解波值的波峰和波谷「Javascript」
  • 股票中 5 日均线(MA)你会画了?