Flutter(二十九)——封装SQLHelpers

居天下之广居,立天下之正位,行天下之大道;得志,与民由之;不得志,独行其道。富贵不能淫,贫贱不能移,威武不能屈,此之谓大丈夫。

前言

在实际的项目中,如果碰到一个数据库操作,就使用一条SQL语句对数据库操作,那么不仅仅代码非常杂乱,而且弄不好可能造成内存溢出,所以我们常常需要封装SQL操作,封装成SQLHelpers来进行操作。
最终实现效果
有过后端开发经验的程序员来说,都或多或少使用过SQLHelpers的帮助类,下面,博主通过一个例子详解SQLHelpers如何封装。

封装SQLHelpers

假如现在博主这里有一个需求,需要做一个新闻类的App,新闻有标题和详细信息,那么如何创建一个SQLHelpers来操作新闻数据呢?不妨直接上代码把:

import 'package:sqflite/sqflite.dart';

final String tableNew='news';
final String columnId='_Id';
final String columnTitle='title';
final String columnDet='detailed';

class News{
  int id;
  String title;
  String detailed;

  /***
   * 将数据转换为键值对
   */
  Map<String,dynamic> toMap() {
    var map = <String, dynamic>{
      columnTitle: title,
      columnDet: detailed,
    };
    if (id != null) {
      map[columnId] = id;
    }
    return map;
  }
    
    
    News();

  /***
   * 将数据转换为类
   */
  News.fromMap(Map<String,dynamic> map){
      id=map[columnId];
      title=map[columnTitle];
      detailed=map[detailed];
    }
}

class NewsProvider{
  Database db;

  /***
   * 打开数据库并创建表格
   */
  Future open(String path) async{
    db=await openDatabase(path,version: 1,onCreate: (Database db,int version) async{
      await db.execute('''
        create table $tableNew(
        $columnId integer primary key autoincrement,
        $columnTitle text not null,
        $columnDet text not null)
      ''');
    });
  }

  /***
   * 插入数据
   */
  Future<News> insert(News news) async{
    news.id=await db.insert(tableNew, news.toMap());
    return news;
  }

  /***
   * 根据ID获取数据
   */
  Future<News> getNews(int id)async{
    List<Map> maps=await db.query(
        tableNew,
        columns: [columnId,columnTitle,columnDet],
        where: '$columnId=?',
        whereArgs: [id],
    );
    if(maps.length>0){
      return News.fromMap(maps.first);
    }
    return null;
  }

  /***
   * 根据ID删除数据
   */
  Future<int> delete(int id)async{
    return await db.delete(tableNew,where: '$columnId=?',whereArgs: [id]);
  }

  /***
   * 根据ID更新数据
   */
  Future<int> update(News news)async{
    return await db.update(tableNew, news.toMap(),where: '$columnId=?',whereArgs: [news.id]);
  }
  
  Future close() async=>db.close();//关闭数据库
}

这里我们将新闻的数据库封装成了SQLHelpers:NewsProvider,这样我们就可以针对数据进行反复的操作,不需要额外的其他的代码,这样程序执行起来就比较高效了。

实战上班打卡

知道怎么封装SQLHelpers类后,我们接着通过实战员工打卡系统,熟练掌握操作。大致的需求是用户在登录应用时有一个输入框,可以输入员工的姓名,然后点击保存,保存完之后可以在列表记录并显示所有已打卡的用户,打卡完成之后可以进入打卡浏览界面浏览所有打过卡的员工,也可以在保存界面清楚打卡数据。

这一次,我们直接封装成一个DBProvider类,通过直接调用这个类就可以操作数据库,首先,我们需要定义一个用户类,代码如下所示:

final String tableUser='user';
final String columnId='_id';
final String columnName='name';

class User{
  int id;
  String name;

  /***
   * 将类转换为键值对
   */
  Map<String,dynamic> toMap(){
    var map=<String,dynamic>{
      columnName:name,
    };
    if(id!=null){
      map[columnId]=id;
    }
    return map;
  }
  
  User();

  /***
   * 将键值对转换为类
   */
  User.fromMap(Map<String,dynamic> map){
    id=map[columnId];
    name=map[columnName];
  }
}

定义了一个Model类后,接着就是我们的SQLHelpers的实现,代码如下:

class DBProvider{
  DBProvider._();
  static final DBProvider db=DBProvider._();
  Database _database;
  
  Future<Database> get database async{
    if(_database!=null){
      return _database;
    }
    _database=await initDB();
    return _database;
  }
  
  initDB() async{
    Directory documentsDirectory=await getApplicationDocumentsDirectory();
    String path=join(documentsDirectory.path,'UserDB.db');
    return await openDatabase(path,version: 1,onOpen: (db){},onCreate: (Database db,int version)async{
      await db.execute('''
      create table $tableUser(
      $columnId integer primary key autoincrement,
      $columnName text not null)
      ''');
    });
  }
  
  Future<User> insert(User user) async{
    final db=await database;
    user.id=await db.insert(tableUser, user.toMap());
    return user;
  }
  
  Future<User> getUser(int id) async{
    final db=await database;
    List<Map> maps=await db.query(tableNew,columns: [columnId,columnName],
    where: '$columnId=?',
    whereArgs: [id]);
    if(maps.length>0){
      return User.fromMap(maps.first);
    }
    return null;
  }
  
  Future<List<User>> getAllUser() async{
    final db=await database;
    var res=await db.query("User");
    List<User> list=res.isNotEmpty?res.map((c)=>User.fromMap(c)).toList():[];
    return list;
  }
  
  Future<int> delete(int id) async{
    final db=await database;
    return await db.delete(tableUser,where: '$columnId=?',whereArgs: [id]);
  }
  
  Future<int> update(User user)async{
    final db=await database;
    return await db.update(tableUser, user.toMap(),where: '$columnId=?',whereArgs: [user.id]);
  }
  
  removeAll() async{
    final db=await database;
    db.delete(tableUser);
  }

  Future close() async{
    final db=await database;
    db.close();
  }
}

这段代码与前面基本类似,唯一不同的是这里我们使用了单例模式,接着,我们要定义界面,首先是打卡界面,代码如下:

class _MyHomePageState extends State<MyHomePage> {

  final TextEditingController textEditingController=new TextEditingController();

  _saveData(){
    User user=User();
    user.name=textEditingController.text;
    DBProvider.db.insert(user);
    Navigator.of(context).push(
      new MaterialPageRoute(builder: (context){
        return new PageResult();
      })
    );
  }

  _removeData(){
    DBProvider.db.removeAll();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          children: <Widget>[
            TextField(
              controller: textEditingController,
              autofocus: true,
              decoration: new InputDecoration(hintText: '请输入打卡姓名'),
            ),
            RaisedButton(
              child: Text('保存'),
              onPressed: _saveData,
            ),
            RaisedButton(
              child: Text('清除数据'),
              onPressed: _removeData,
            ),
          ],
        ),
      ),
    );
  }
}

上面代码界面控件有三个,一个输入文本框,两个按钮,一个按钮记录打卡数据,所以点击后saveData,一个清楚数据库所有数据removeData,基本都是常用的代码,这里就不赘述了,接着就是打卡后,显示的详细打卡详情列表的界面,代码如下:

class PageResult extends StatelessWidget{

  PageResult({Key key}):super(key:key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('已打卡人员'),
      ),
      body: FutureBuilder<List<User>>(
        future: DBProvider.db.getAllUser(),
        builder: (BuildContext context,AsyncSnapshot<List<User>> snapshot){
          if(snapshot.hasData){
            return ListView.builder(
                itemBuilder: (BuildContext context,int index){
                  User item=snapshot.data[index];
                  return ListTile(
                    title: Text(item.name),
                  );
                },
                itemCount: snapshot.data.length,
            );
          }else{
            return Center(
              child: CircularProgressIndicator(),
            );
          }
        },
      ),
    );
  }

}

这段代码就是获取数据库中的数据以ListView的形式显示在界面上,如果数据库没有数据,则显示圆形进度条。显示的效果图,如博文首图所示。

发布了120 篇原创文章 · 获赞 150 · 访问量 133万+

猜你喜欢

转载自blog.csdn.net/liyuanjinglyj/article/details/105069284