Flutter实现 TodoList的开发与数据持久化

一、前言

业务需求要使用到TodoList的功能,查找到类似的方案,但存在一定的缺陷,为了实现想要的功能,决定自己动手来实现,以下技术实现难度不大,就是有坑,接下来是填坑过程。

二、效果图

初始界面
添加删除

三、TodoList逻辑

  1. TodoList的标题效果(CheckboxListTile)
  2. 左右滑动删除(Dismissible)
  3. 数据持久化处理(sqlite)

四、具体实现方案

  1. 使用的第三方库

sqflite: ^1.1.6+5

  1. 主要代码
   ///数据库处理

  ///查询数据库
  Future _getPersonProvider(Database db) async {
    List<Map<String, dynamic>> maps =
    await db.rawQuery("select * from $name ");
    return maps;
  }
  
  ///插入到数据库
  Future insert(TodoModel model) async {
    Database db = await getDataBase();
    var userProvider = await _getPersonProvider(db);
    if(model.id!=null){
      if (userProvider != null) {
        ///删除数据
        await db.delete(name, where: "$columnId = ?", whereArgs: [model.id]);
      }
    }
    await db.rawInsert("insert into $name ($columnTitle,$columnIsDone) values (?,?)",[model.title,model.isDone]);
    List<Map<String, dynamic>> maps =
    await db.rawQuery("SELECT LAST_INSERT_ROWID();", null);
    if (maps.length > 0) {
      TodoModel userModel = await TodoModel.fromJson(maps[0]);
      return userModel.lastid;
    }
    return null;
  }

  ///更新数据库
  Future<void> update(TodoModel model) async {
    Database database = await getDataBase();
    await database.rawUpdate(
        "update $name set $columnTitle = ?,$columnIsDone = ? where $columnId= ?",[model.title,model.isDone,model.id]);

  }
  
  ///删除数据
  Future<void> delete(TodoModel model) async {
    Database database = await getDataBase();
    await database.delete(name, where: "$columnId = ?", whereArgs: [model.id]);

  }
  // 主要界面代码
  Widget _createListView(todos, onTodoToggle) {
    return ListView.builder(
      itemCount: todos.length,
      itemBuilder: (context, index) {
        return Dismissible(
          background: Container(
            color: Colors.green,
            // 这里使用 ListTile 因为可以快速设置左右两端的Icon
            child: ListTile(
              leading: Icon(
                Icons.delete,
                color: Colors.white,
              ),
            ),
          ),

          secondaryBackground: Container(
            color: Colors.red,
            // 这里使用 ListTile 因为可以快速设置左右两端的Icon
            child: ListTile(
              trailing: Icon(
                Icons.delete,
                color: Colors.white,
              ),
            ),
          ),
          // Key
          key: Key(UniqueKey().toString()),
          // Child
          child: CheckboxListTile(
            value: todos[index].isDone,
            activeColor: Colors.red,
            title: todos[index].isDone
                ? Text(
                    todos[index].title,
                    style: TextStyle(
                      color: Colors.grey,
                      decoration: TextDecoration.lineThrough,
                      decorationColor: Colors.grey,
                    ),
                  )
                : Text(todos[index].title),
            controlAffinity: ListTileControlAffinity.leading,
            onChanged: (bool isChecked) {
              onTodoToggle(todos[index], isChecked);
            },
          ),
          onDismissed: (direction) {
            // 展示 SnackBar
//            Scaffold.of(context).showSnackBar(SnackBar(
//              content: Text('删除了${todos[index].title}'),
//            ));
            // 删除后刷新列表,以达到真正的删除
            delete(todos[index].id);
            setState(() {
              todos.removeAt(index);
            });
          },
          confirmDismiss: (direction) async {
            var _confirmContent;
            var _alertDialog;
            // 从右向左  也就是删除
            _confirmContent = '确认删除:${todos[index].title}?';
            _alertDialog = AlertDialog(
              title: Text(_confirmContent),
              actions: <Widget>[
                FlatButton(
                  onPressed: () {
                    Navigator.of(context).pop(true);
                  },
                  child: Text('确认'),
                ),
                FlatButton(
                  onPressed: () {
                    Navigator.of(context).pop(false);
                  },
                  child: Text('取消'),
                )
              ],
              shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.all(Radius.circular(20))),
            );

//            else if (direction == DismissDirection.startToEnd) {
//              _confirmContent = '确认收藏${_listData[index]}?';
//              _alertDialog = _createDialog(
//                _confirmContent,
//                    () {
//                  // 展示 SnackBar
//                  Scaffold.of(context).showSnackBar(SnackBar(
//                    content: Text('确认收藏${_listData[index]}'),
//                    duration: Duration(milliseconds: 400),
//                  ));
//                  Navigator.of(context).pop(true);
//                },
//                    () {
//                  // 展示 SnackBar
//                  Scaffold.of(context).showSnackBar(SnackBar(
//                    content: Text('不收藏${_listData[index]}'),
//                    duration: Duration(milliseconds: 400),
//                  ));
//                  Navigator.of(context).pop(false);
//                },
//              );
//            }

            var isDismiss = await showDialog(
                context: context,
                builder: (BuildContext context) {
                  return _alertDialog;
                });
            return isDismiss;
          },
        );
      },
    );
  }

五、注意事项

  1. sqlite数据库的查看方式,Android studio

在android项目的build.gradle中添加代码

debugImplementation 'com.amitshekhar.android:debug-db:1.0.6'

之后Sync,启动AVD或者连接USB设备
在Logcat中输入D/DebugDB,即可查看IP与端口,在浏览器中打开访问即可
之后再用浏览器访问

localhost:8080

即可查看数据库中的结构及各表的内容

查看数据库
  1. 数据加载完成后无效果,需要点击按钮才有
    通过加载进度条组件来实现效果,当sqlite的数据全部获取完进度条结束,显示页面。

  2. sqlite添加完成后返回id,配合更新删除

await db.rawInsert("insert into $name ($columnTitle,$columnIsDone) values (?,?)",[model.title,model.isDone]);
    List<Map<String, dynamic>> maps =
    await db.rawQuery("SELECT LAST_INSERT_ROWID();", null);
    if (maps.length > 0) {
      TodoModel userModel = await TodoModel.fromJson(maps[0]);
      return userModel.lastid;

插入完成后通过
await db.rawQuery("SELECT LAST_INSERT_ROWID();", null);
返回{ LAST_INSERT_ROWID(),1}

最后附上Demo地址,如有什么不足和错误请指正,觉得受用反手就给颗吧

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。