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

相关文章
|
Android开发 iOS开发 容器
Flutter控件封装之轮播图Banner
Flutter中实现轮播图的方式有很多种,比如使用三方flutter_swiper,card_swiper等等,使用这些三方,可以很快很方便的实现一个轮播图展示,基本上也能满足我们日常的开发需求,如果说,想要一些定制化的操作,那么就不得不去更改源码或者自己自定义一个,自己定义的话,Flutter中提供了原生组件PageView,可以使用它很方便的来实现一个轮播图。
595 0
|
Dart JavaScript 前端开发
JavaScript & React & Ant Design & Flutter & Dart 基础学习笔记 Tree Form 组件通信 封装等
JavaScript & React & Ant Design & Flutter & Dart 基础学习笔记 Tree Form 组件通信 封装等
|
10月前
|
存储 缓存 JavaScript
Flutter 学习之封装 WebView
【10月更文挑战第24天】通过以上的探讨,我们可以看出,在 Flutter 中封装 WebView 是非常有必要的,它可以提高代码的复用性、增强可维护性、提供统一接口。在实际应用中,我们需要根据具体的需求和场景,选择合适的封装方法和技术,以实现更好的效果。
|
9月前
|
XML JSON 前端开发
一文带你了解 Flutter dio封装
一文带你了解 Flutter dio封装
1265 1
|
10月前
|
存储 缓存 Dart
Flutter&鸿蒙next 封装 Dio 网络请求详解:登录身份验证与免登录缓存
本文详细介绍了如何在 Flutter 中使用 Dio 封装网络请求,实现用户登录身份验证及免登录缓存功能。首先在 `pubspec.yaml` 中添加 Dio 和 `shared_preferences` 依赖,然后创建 `NetworkService` 类封装 Dio 的功能,包括请求拦截、响应拦截、Token 存储和登录请求。最后,通过一个登录界面示例展示了如何在实际应用中使用 `NetworkService` 进行身份验证。希望本文能帮助你在 Flutter 中更好地处理网络请求和用户认证。
458 1
|
10月前
|
Dart UED 开发者
Flutter&鸿蒙next中的按钮封装:自定义样式与交互
在Flutter应用开发中,按钮是用户界面的重要组成部分。Flutter提供了多种内置按钮组件,但有时这些样式无法满足特定设计需求。因此,封装一个自定义按钮组件变得尤为重要。自定义按钮组件可以确保应用中所有按钮的一致性、可维护性和可扩展性,同时提供更高的灵活性,支持自定义颜色、形状和点击事件。本文介绍了如何创建一个名为CustomButton的自定义按钮组件,并详细说明了其样式、形状、颜色和点击事件的处理方法。
221 1
|
10月前
|
开发工具 UED
Flutter&鸿蒙next中封装一个输入框组件
本文介绍了如何创建一个简单的Flutter播客应用。首先,通过`flutter create`命令创建项目;接着,在`lib`目录下封装一个自定义输入框组件`CustomInput`;然后,在主应用文件`main.dart`中使用该输入框组件,实现简单的UI布局和功能;最后,通过`flutter run`启动应用。本文还提供了后续扩展建议,如状态管理、网络请求和UI优化。
181 1
|
10月前
|
开发工具
Flutter&鸿蒙next中封装一个列表组件
Flutter&鸿蒙next中封装一个列表组件
162 0
|
10月前
|
Dart 安全 UED
Flutter&鸿蒙next中的表单封装:提升开发效率与用户体验
在移动应用开发中,表单是用户与应用交互的重要界面。本文介绍了如何在Flutter中封装表单,以提升开发效率和用户体验。通过代码复用、集中管理和一致性的优势,封装表单组件可以简化开发流程。文章详细讲解了Flutter表单的基础、封装方法和表单验证技巧,帮助开发者构建健壮且用户友好的应用。
223 0
|
JSON Dart API
Flutter dio http 封装指南说明
本文介绍了如何实现一个通用、可重构的 Dio 基础类,包括单例访问、日志记录、常见操作封装以及请求、输出、报错拦截等功能。
368 2
Flutter dio http 封装指南说明