如何建立一个Flutter ToDo应用程序

283 阅读6分钟

如何构建一个Flutter ToDo应用程序

多年来,软件开发人员使用框架来构建Android、iOS、桌面和Web的应用程序。Flutter是这些框架中的一个。Flutter是谷歌开发的一项技术,使开发者能够用一个代码库为所有平台构建应用程序。

这项技术正在改变软件开发行业,使应用开发更快、更便宜。在这篇文章中,我们将使用Flutter来构建一个待办事项列表应用程序。

前提条件

为了更好地理解这篇文章,读者应该对面向对象的编程语言(如Java、C++等)有一定的基础知识。

目标

在本文结束时,读者应该熟悉。

  • 创建一个基本的flutter应用程序。
  • Flutter 和 dart 包。
  • Flutter widget。
  • 无状态和有状态部件。

待办事项列表应用程序将看起来像下面的屏幕截图。

Final Project

使用 Flutter 构建的待办事项列表应用程序

要求

在本教程中,我们将使用Flutter SDK和VS Code文本编辑器。在你的终端上,导航到你想让你的应用程序所在的目录,并运行下面的命令来创建你的项目。

# create new project
flutter create todo_app

接下来,在我们的例子中,使用命令cd todo_app ,导航到创建的项目(todo_app)目录,并运行该应用程序,如下图。

# run the app
flutter run

我们将使用智能手机来运行该应用程序,按照这个链接来学习如何在物理设备上运行flutter。当项目第一次运行时,将需要一点时间来加载。

您的应用程序将是flutter默认的应用程序,看起来应该像下面的截屏。

Flutter Default App

第一次运行任何应用程序后的Flutter默认状态

转到lib目录下的main.dart 文件。将默认代码改为下图所示的片段。

import 'package:flutter/material.dart';

void main() {
  runApp(Todo());
}

class Todo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
  // app layout
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('To-do List'),
        ),
      )
      
    );
  }
}

运行应用程序后,你会发现一个空的画布,标题是To-do-List。让我们看看这段代码。

Flutter软件包

在第一行,我们导入了一个名为Material.dart 的flutter包(import 'package:flutter/material.dart';),以使应用开发变得快速,flutter自带的包使我们能够轻松开始构建一个材料风格的应用。包只是别人对你的应用程序中需要的某个功能的解决方案。

与其从头开始构建一些功能,你可以直接去pub.dartlang.org搜索一个能执行你想要构建的那个功能的包。你可以把它纳入你的应用程序中。这将为作为开发者的您节省大量的时间。当您使用flutter开发时,您会用到很多包。

Flutter widgets

Flutter部件描述了在当前配置和状态下,一个应用程序的视图应该是什么样子。根据Flutter文档,Flutter widget是使用现代框架构建的,从React中获得灵感。

一个部件可以帮助布局,定义设计等。比如说。Padding, Margin, Center, Layout rows, and columns都是widget。

从我们的代码来看,整个应用程序是一个包含MaterialApp小部件的小部件。

  • 脚手架是帮助我们创建一个适当的材料布局的部件,而不用担心手动样式设计。
  • AppBar是一个小部件,它接受一个标题,并在屏幕顶部创建一个条,这在应用程序中是正常的。这在安卓系统上将文本向左对齐,在iOS上将文本向中间对齐。

无状态小组件

一个无状态的小部件是一个小部件,它的状态一旦建立就不能改变。也就是说,变量、图标、按钮或检索数据的任何变化都不能改变应用程序的状态。

一个待办事项应用程序将始终有待办事项被添加和删除,为了实现这一点,我们将需要实现一个有状态的部件。

有状态的小组件

这种类型的小组件是动态的。这意味着它可以在接收数据时改变其外观,或者根据触发的事件改变外观。

编辑你的代码,使其与下图中的代码一致。

Flutter appbar

class _TodoListState extends State<TodoList> {
  // save data
  final List<String> _todoList = <String>[];
  // text field
  final TextEditingController _textFieldController = TextEditingController();
  @override
  Widget build(BuildContext context) {
    // app layout
    return Scaffold(
      appBar: AppBar(
        title: Text('To-do List'),
      ),
    );
  }
}

你应该注意到,我们的有状态部件类TodoList有两个类。这是为了使我们能够在不丢失数据的情况下更新我们的待办事项列表。现在,让我们为状态类添加功能。

将下面的代码添加到你的状态中。

class _TodoListState extends State<TodoList> {
  // save data
  final List<String> _todoList = <String>[];
  // text field
  final TextEditingController _textFieldController = TextEditingController();
  @override
  Widget build(BuildContext context) {
    // app layout
    return Scaffold(
      appBar: AppBar(
        title: Text('To-do List'),
      ),
    );
  }
}

这段代码最终List<String> _todoList = <String>[]; 。使我们能够将数据保存到我们的应用程序中。

而finalTextEditingController _textFieldController = TextEditingController(); 是让我们有一个文本字段用于输入项目。

接下来,将你的代码更新为下面的代码。

class _TodoListState extends State<TodoList> {
  // save data
  final List<String> _todoList = <String>[];
  // text field
  final TextEditingController _textEditingController = TextEditingController();
  @override
  Widget build(BuildContext context) {
    // code that returns the appbar
    return Scaffold(
      appBar: AppBar(
        title: Text('To-do List'),
      ),
    );
  }

  // adds data to list.
  void _addTodoItem(String title) {
    //  a set state will notify the app that the state has changed
    setState(() {
      _todoList.add(title);
    });
    _textFieldController.clear();
  }
}

_addtodoItems函数负责将项目保存到_todolist。

再次更新你的代码以符合下面的代码。

 // adds data to List.
  void _addTodoItem(String title) {
    //  a set state will notify the app that the state has changed
    setState(() {
      _todoList.add(title);
    });
    // the text field is cleared once the item is added to list
    _textFieldController.clear();
  }

   // populate the listview
  Widget _buildTodoItem(String title) {
    return ListTile(title: Text(title));
  }

小工具ListTile通常是用来填充flutter中的listView的。为了输入todo项目,将代码更新为下面的代码。

 // populate the listview
  Widget _buildTodoItem(String title) {
    return ListTile(title: Text(title));
  }

  // display a dialog for the user to enter items
  Future<AlertDialog> _displayDialog(BuildContext context) async {
    // alter the app state to show a dialog
    return showDialog(
        context: context,
        builder: (BuildContext context) {
          return AlertDialog(
            title: const Text('Add a task to your list'),
            content: TextField(
              controller: _textFieldController,
              decoration: const InputDecoration(hintText: 'Enter task here'),
            ),
            actions: <Widget>[
              // add button
              FlatButton(
                child: const Text('ADD'),
                onPressed: () {
                  Navigator.of(context).pop();
                  _addTodoItem(_textFieldController.text);
                },
              ),
              // cancel button
              FlatButton(
                child: const Text('CANCEL'),
                onPressed: () {
                  Navigator.of(context).pop();
                },
              )
            ],
          );
        });
  }
  // iterates through our todo list titles.
  List<Widget> _getItems() {
    final List<Widget> _todoWidgets = <Widget>[];
    for (String title in _todoList) {
      _todoWidgets.add(_buildTodoItem(title));
    }
    return _todoWidgets;
  }

提醒对话框告诉用户需要确认的情况。

我们将使用警报框来收集todo项目。

为了运行代码,我们必须将构建小部件的代码更新为下面的代码。

class _TodoListState extends State<TodoList> {
  final List<String> _todoList = <String>[];
  final TextEditingController _textFieldController = TextEditingController();
  @override
  Widget build(BuildContext context) {
    // app layout
    return Scaffold(
      appBar: AppBar(title: const Text('To-Do List')),
      body: ListView(children: _getItems()),
      // add items to the to-do list
      floatingActionButton: FloatingActionButton(
          onPressed: () => _displayDialog(context),
          tooltip: 'Add Item',
          child: Icon(Icons.add)),
    );
  }

你的完整代码应该像下面这样。

import 'package:flutter/material.dart';

void main() {
  runApp(Todo());
}

class Todo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(home: TodoList());
  }
}

class TodoList extends StatefulWidget {
  @override
  _TodoListState createState() => _TodoListState();
}

class _TodoListState extends State<TodoList> {
  // save data
  final List<String> _todoList = <String>[];
  // text field
  final TextEditingController _textFieldController = TextEditingController();
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('To-Do List')),
      body: ListView(children: _getItems()),
      // add items to the to-do list
      floatingActionButton: FloatingActionButton(
          onPressed: () => _displayDialog(context),
          tooltip: 'Add Item',
          child: Icon(Icons.add)),
    );
  }

  void _addTodoItem(String title) {
    // Wrapping it inside a set state will notify
    // the app that the state has changed
    setState(() {
      _todoList.add(title);
    });
    _textFieldController.clear();
  }

  // this Generate list of item widgets
  Widget _buildTodoItem(String title) {
    return ListTile(title: Text(title));
  }

  // display a dialog for the user to enter items
  Future<AlertDialog> _displayDialog(BuildContext context) async {
    // alter the app state to show a dialog
    return showDialog(
      context: context,
      builder: (BuildContext context) {
        return AlertDialog(
          title: const Text('Add a task to your list'),
          content: TextField(
            controller: _textFieldController,
            decoration: const InputDecoration(hintText: 'Enter task here'),
          ),
          actions: <Widget>[
            // add button
            FlatButton(
              child: const Text('ADD'),
              onPressed: () {
                Navigator.of(context).pop();
                _addTodoItem(_textFieldController.text);
              },
            ),
            // Cancel button
            FlatButton(
              child: const Text('CANCEL'),
              onPressed: () {
                Navigator.of(context).pop();
              },
            )
          ],
        );
      } 
    );
  }
  // iterates through our todo list title
  List<Widget> _getItems() {
    final List<Widget> _todoWidgets = <Widget>[];
    for (String title in _todoList) {
      _todoWidgets.add(_buildTodoItem(title));
    }
    return _todoWidgets;
  }
}

结论

现在我们有了一个简单的待办事项应用程序,使我们能够添加待办事项。