Flutter实现一个简单的待办事项

根据之前学的控件,我们来撸一个最简单的待办事项app,我们先看下最终效果图:
首先我们在脑子里大概构思下这个app要实现什么功能,页面构成是什么。
第一步:构思这个app的主要功能:
1、可以通过用户自己点击按钮进行待办事项增加操作。
2、将用户的待办事项滚动列表的形式展现在页面上。
3、当用户点击某个事项后,弹出一个对话框询问是否标记成完成,如果是则删除该事项。
有了上面的大概构思,我们开始设计UI构成
页面UI主要包含三部分内容:
1、AppBar,用于显示标题。
2、中间内容显示区域,由一个ListView构成,用于展示用户添加的待办事项。
3、右下角一个圆形按钮,用于点击添加新的待办事项。

下面我们按照上面分析的先进行ui布局:

import 'package:flutter/material.dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Todo Demo',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new TodoList(),
    );
  }
}

class TodoList extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => new TodoListState();
}

class TodoListState extends State<TodoList> {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text('待办清单'),
      ),
      body: new Center(
        child: new Text(
          '点击按钮开始添加',
          style: new TextStyle(fontSize: 20.0, color: Colors.grey),
        ),
      ),
      floatingActionButton: new FloatingActionButton(
        onPressed: null,
        tooltip: '新增待办事项',
        child: new Icon(
          Icons.add,
          size: 25.0,
        ),
      ),
    );
  }
}

以上代码包含一个程序入口main函数,返回一个符合Material风格的app。其中home返回一个TodoList页面,该页面由脚手架包含的appbar、body、button组成。
尝试着运行一下,效果如下:

《Flutter实现一个简单的待办事项》 IMG_3412.PNG

下面我们添加一个ListView用于展示待办事项清单:

//自动生成20个字符串构成一个数组
  List<String> _todoItems = List.generate(20, (i) => 'item ${i}');

  //ListView控件,用于滚动展示待办事项
  Widget TodoListView() {
    return new ListView.builder(
        itemCount: 20,
        itemBuilder: (context, index) {
          return TodoItem(index);
        });
  }

  //ListTile控件,描绘每个待办事项
  Widget TodoItem(int i) {
    return new ListTile(
      title: new Text(_todoItems[i]),
    );
  }

修改TodoListState中body部分内容

body: new Center(
          child: _todoItems.length == 0 ? hintText() : TodoListView()),

//当待办事项记录是0的时候,显示'点击按钮开始添加'在屏幕作为提示语
  Widget hintText() {
    return new Text(
      '点击按钮开始添加',
      style: new TextStyle(fontSize: 20.0, color: Colors.grey),
    );
  }

尝试运行后效果如下:

《Flutter实现一个简单的待办事项》 IMG_3413.PNG

你们肯定发现了,现在的事项是自动生成的,无法满足用户自己添加待办事项的需求,因此我们需要继续改造,让用户可以自己编辑添加事项。
我们通过点击右下角的添加按钮,跳转至添加待办事项页面,用户点击编辑框可以自己编辑内容,并保存结果。
我们添加一个编辑待办事项的页面:

//点击右下角的按钮后,将新的路由页面推入栈中,该页面包含一个文本编辑控件,用于用户编辑内容。
  void _todoEdit() {
    Navigator.push(context, new MaterialPageRoute(builder: (context) {
      return new Scaffold(
        appBar: new AppBar(
          title: new Text('添加待办事项'),
          leading: new BackButton(),
        ),
        body: new TextField(
          decoration: new InputDecoration(
            hintText: '编辑待办事项',
            contentPadding: const EdgeInsets.all(10.0),
          ),
          onSubmitted: (text) {
            if (text.length == 0) {
              Navigator.of(context).pop();
            } else {
              _todoItemsChanged(text);
              Navigator.of(context).pop();
            }
          },
        ),
      );
    }));
  }

修改TodoListState中floatingActionButton的onPressed内容:

floatingActionButton: new FloatingActionButton(
        onPressed: () => _todoEdit(),

再次尝试运行程序,当点击屏幕右下角按钮后,页面跳转至编辑待办事项的页面 如图:

《Flutter实现一个简单的待办事项》 IMG_3414.PNG

这个时候,虽然我们可以编辑待办事项,但是当我们编辑点击完成后,首页并没有展示出来,这是为什么,因为我们没有通知程序状态发生变化,所以程序没有重绘UI,所以要通过setState来告诉程序待办事项列表发生变化,需要更新:

//待办事项列表有新的变化,通过setState重绘页面UI
  _todoItemsChanged(String text) {
    setState(() {
      _todoItems.add(text);
    });
  }

以上代码表示,当编辑完成后,会告诉程序待办事项清单已经发生变化,需要重绘UI。运行程序尝试点击按钮添加两条待办事项,如果一切正常的话会出现以下效果:

《Flutter实现一个简单的待办事项》 IMG_3415.PNG

到这里工程已经完成大半了,下面我们要实现用户点击待办事项后,弹出对话框提示是否标记成完成,如果是则从列表中删除该事项。
因为是点击对应的事项,所以需要改造TodoItem():

//ListTile控件,描绘每个待办事项
  Widget TodoItem(int i) {
    return new ListTile(
      title: new Text(_todoItems[i]),
      onTap: () {
        showDialog(
            context: context,
            builder: (context) {
              return new AlertDialog(
                title: new Text('"${_todoItems[i]}" 是否标记成已完成'),
                actions: <Widget>[
                  new FlatButton(onPressed: null, child: new Text('否',style: new TextStyle(
                            fontSize: 18.0, color: Colors.redAccent))),
                  new FlatButton(onPressed: null, child: new Text('是',style: new TextStyle(
                            fontSize: 18.0, color: Colors.redAccent))),
                ],
              );
            });
      },
    );
  }

以上代码主要更改了onTap内容,返回一个弹出对话框,并显示一条文本内容,同时包含两个按钮用于后续选择操作。
运行程序,先添加几条待办事项,然后点击其中一条会弹出对话框 如下图:

《Flutter实现一个简单的待办事项》 IMG_3417.PNG

但是这个时候不管点击“是”还是“否”都没有效果,因为我们还没有添加点击逻辑,我们继续修改上面那段代码:

              actions: <Widget>[
                  new FlatButton(
                  //点击否的话不进行任何操作,退出对话框
                      onPressed: () => Navigator.pop(context),
                      child: new Text(
                        '否',
                        style: new TextStyle(
                            fontSize: 18.0, color: Colors.redAccent),
                      )),
                  new FlatButton(
                  //点击是,需要移除该事项,并告诉程序状态发生变化,需要重绘视图UI
                      onPressed: (){
                  //调用_removeTodoItem()方法刷新页面,,同时退出对话框
                        _removeTodoItem(i);
                        Navigator.pop(context);
                      },
                      child: new Text(
                        '是',
                        style: new TextStyle(
                            fontSize: 18.0, color: Colors.redAccent),
                      )),
                  ]

然后通过setState告诉程序事项删除,需要重绘视图UI:

//从列表中删除该事项,并告知程序状态发生变化,需要重绘视图UI
  _removeTodoItem(int index){
    setState(() {
      _todoItems.removeAt(index);
    });
  }

这样,当点击某个事项后,如果点击否,不会有任何改变,当点击是,该事项会从当前展示区域消失。

那么现在基本功能就全部实现了,当然我们可以继续美化或者添加其他交互逻辑,比如在每个事项之间添加一条分割线,又比如在appbar上添加一个按钮,点击切换显示所有任务和已完成的任务等。后面再研究继续完善吧 _

下面附上全部代码:

import 'package:flutter/material.dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Todo Demo',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new TodoList(),
    );
  }
}

class TodoList extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => new TodoListState();
}

class TodoListState extends State<TodoList> {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text('待办清单'),
      ),
      body: new Center(
          child: _todoItems.length == 0 ? hintText() : TodoListView()),
      floatingActionButton: new FloatingActionButton(
        onPressed: () => _todoEdit(),
        tooltip: '新增待办事项',
        child: new Icon(
          Icons.add,
          size: 25.0,
        ),
      ),
    );
  }

  //当待办事项记录是0的时候,显示点击按钮开始添加提示语
  Widget hintText() {
    return new Text(
      '点击按钮开始添加',
      style: new TextStyle(fontSize: 20.0, color: Colors.grey),
    );
  }

  //用于存放待办事项
  List<String> _todoItems = [];

  //ListView控件,用于滚动展示待办事项
  Widget TodoListView() {
    return new ListView.builder(itemBuilder: (context, index) {
      if (index < _todoItems.length) {
        return TodoItem(index);
      }
    });
  }

  //ListTile控件,描绘每个待办事项
  Widget TodoItem(int i) {
    return new ListTile(
      title: new Text(_todoItems[i]),
      onTap: () {
        showDialog(
            context: context,
            builder: (context) {
              return new AlertDialog(
                title: new Text('"${_todoItems[i]}" 是否标记成已完成'),
                actions: <Widget>[
                  new FlatButton(
                      onPressed: () => Navigator.pop(context),
                      child: new Text(
                        '否',
                        style: new TextStyle(
                            fontSize: 18.0, color: Colors.redAccent),
                      )),
                  new FlatButton(
                      onPressed: (){
                        _removeTodoItem(i);
                        Navigator.pop(context);
                      },
                      child: new Text(
                        '是',
                        style: new TextStyle(
                            fontSize: 18.0, color: Colors.redAccent),
                      )),
                ],
              );
            });
      },
    );
  }
  
  //从列表中删除该事项,并告知程序状态发生变化,需要重绘视图UI
  _removeTodoItem(int index){
    setState(() {
      _todoItems.removeAt(index);
    });
  }

  //点击右下角的按钮后,将新的路由页面推入栈中,该页面包含一个文本编辑控件,用于用户编辑内容。
  void _todoEdit() {
    Navigator.push(context, new MaterialPageRoute(builder: (context) {
      return new Scaffold(
        appBar: new AppBar(
          title: new Text('添加待办事项'),
          leading: new BackButton(),
        ),
        body: new TextField(
          decoration: new InputDecoration(
            hintText: '编辑待办事项',
            contentPadding: const EdgeInsets.all(10.0),
          ),
          onSubmitted: (text) {
            if (text.length == 0) {
              Navigator.of(context).pop();
            } else {
              _todoItemsChanged(text);
              Navigator.of(context).pop();
            }
          },
        ),
      );
    }));
  }

  //待办事项列表有新的变化,通过setState重绘页面UI
  _todoItemsChanged(String text) {
    setState(() {
      _todoItems.add(text);
    });
  }
}
    原文作者:走路不穿鞋oO
    原文地址: https://www.jianshu.com/p/052bdb7349c2
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞