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的形式显示在界面上,如果数据库没有数据,则显示圆形进度条。显示的效果图,如博文首图所示。

相关文章
|
10月前
|
Android开发 iOS开发 容器
Flutter控件封装之轮播图Banner
Flutter中实现轮播图的方式有很多种,比如使用三方flutter_swiper,card_swiper等等,使用这些三方,可以很快很方便的实现一个轮播图展示,基本上也能满足我们日常的开发需求,如果说,想要一些定制化的操作,那么就不得不去更改源码或者自己自定义一个,自己定义的话,Flutter中提供了原生组件PageView,可以使用它很方便的来实现一个轮播图。
194 0
|
10月前
|
API Android开发
Flutter控件封装之视频进度条
视频控制器,三方所提供的样式,有时很难满足我们的需求,对于此情况,我们不得不在此基础上自行封装,今天所分享的文章就是一个很简单的控制器封装案例,包含了基本的播放暂停,全屏和退出全屏,以及时间和进度的展示,封装了事件回调以及各个属性的控制,基本上可以满足大部分的业务需求,即便不满足,大家也可以在此基础之上拓展。
148 0
|
10月前
|
Android开发 UED
Flutter控件之Tab选项卡封装
Tab选项卡,这是一个非常常见且权重很高的一个组件,随便打开一个App,比如掘金,如下图,首页顶部就是一个Tab选项卡,这个功能可以说,几乎每个App都会存在。
171 0
|
10月前
|
存储 Android开发
Flutter控件之图片Image封装
Flutter中偏偏原生的控件,少了很多需要又常用的属性,比如宽高,比如内外边距,又比如点击事件,如果不采取封装,视图的结构会一层嵌套一层,徒增很多的冗余代码,所以,为了简洁代码,还有为了拓展原生组件没有的属性,就不得不进行一次简单的封装,使其在调用的时候,可以很方便的实现某些功能。
|
10月前
|
API
Flutter控件之文本Text封装
文本Text比较简单,除了基类BaseWidget所提供的属性之外,又简单的扩展了部分属性,比如图文和富文本,都是系统原生的提供的,做了简单的封装。
|
10月前
|
API Android开发 容器
Flutter控件之基类Widget封装
基类的Widget主要确定以下几个方面,第一就是,自定义一个抽象类还是非抽象类,第二、继承方式,采取有状态还是无状态,第三、关于组件的点击方式,如何进行实现。
113 0
|
JSON 数据格式
Flutter封装技巧
Flutter 中封装可重用组件是提高开发效率和代码可维护性的重要手段。以下是一些常见的 Flutter 组件封装技巧
Flutter封装技巧
Flutter(十五)——动画的封装与简化
Flutter(十五)——动画的封装与简化
138 1
Flutter(十五)——动画的封装与简化
|
数据安全/隐私保护
Flutter 封装文本输入框
封装一个通用的文本框,提高组件的复用性。
359 0
Flutter 封装文本输入框
|
Dart API 开发工具
Day09 - Flutter -网络请求的封装
Day09 - Flutter -网络请求的封装
732 0
Day09 - Flutter -网络请求的封装