Code前端首页关于Code前端联系我们

Flutter数据库:本地数据存储和状态管理的优雅结合

terry 2年前 (2023-09-22) 阅读数 80 #移动小程序

效果展示

Flutter 数据库: 本地数据存储和状态管理的优雅结合

描述:将数据保存到本地数据库,将你的汽车存储在本地并使用状态管理更新页面来控制车辆启动状态(读取和写数据库)

前言?

最近因为业务需要开始研究本地数据存储Flutter。要在本地数据存储领域取得成功,最重要的是对数据库的充分了解。基础数据、建库、建表、插入、删除、编辑、查询都是基本的数据库操作。在开始之前你需要了解数据库,否则你会感到困惑。一点解释 没什么可学的。手写demo其实非常简单。您可以轻松地执行许多活动,例如构建数据库,但本文将这些看似简单的活动与实际业务和细节推论联系起来。我想看完详细内容你会收获很多。

技术选择

当我们在Flutter中使用数据库时,我们的首要目的肯定是对一些数据进行一些操作(有一定的数据量,而不是光)。我们活动的目的是什么?还要满足用户需求,显示不同类型的信息?!这种情况下,我们如何才能更优雅的展示我们的数据呢?也就是说,在使用数据库的情况下,如何保证Flutter播放的最佳性能?这与Flutter国政中的陈词滥调内容密不可分。国家行政部门有很多解决方案。在研究这部分内容时,我个人看到很多使用block的方法,但是我公司项目已经很早就实现了provider,那又怎样?如果您不知道该怎么做,您所能做的就是进行自己的研究。弄清楚我的空间管理计划后,是时候选择插件了。 provider作为官方扩展,当然优先。数据库呢?经过广泛考虑,我们的数据库使用SQLite。有人会问,综合方面有哪些? ?,还需要我再问吗? Flutter 数据库: 本地数据存储和状态管理的优雅结合

高人气➕高喜欢Flutter最爱。我流口水了,有那么好吃吗?

  • 数据库(数据库):SQLite —— sqflite
  • 空间管理:♷服务提供商?第三方库:
      sqflite: ^1.3.1
      provider: ^4.3.2
      path_provider: ^1.6.11
    复制代码
    • path_provider:用于获取本地存储路径

    第二步:从简单到复杂?

    首先,在第一步中,我们看到这样一个项目,并且我们要保存数据库,那么首先需要什么?它是我们的两个函数readwrite中使用的数据结构。这个数据结构就是数据结构?。我们需要的各种属性:汽车品牌、汽车型号、是否已启动。还有id,方便我们阅读。其实熟悉数据库的同学也应该想到这个id以后会是我们的主键吗?

    class Car {
      int id;
      String brand;
      String type;
      bool start;
    
      Car({@required this.brand, this.type, this.start = false});
    
      Map<String, dynamic> toMap() {
        var map = Map<String, dynamic>();
        if (id != null) {
          map['id'] = id;
        }
        map['brand'] = brand;
        map['type'] = type;
        map['start'] = start == true ? 1 : 0;
        return map;
      }
    
      Car.fromMapObject(Map<String, dynamic> map) {
        this.id = map['id'];
        this.brand = map['brand'];
        this.type = map['type'];
        this.start = map['start'] == 1 ? true : false;
      }
    }
    复制代码

    第三步:主要数据库操作??‍♂️

    下一步非常关键,也对应了我们今天的主题。我们在数据库中的活动包括构建数据库、创建表、添加、删除、修改和查询。如果我们把这些基本功能都写在UI层的话,我们的结构看起来很乱,可复用性很差,所以我们这里单独写一个。类出来并封装。我称她为DatabaseHelper。得益于强大的扩展,我们只需要处理上层API?封装了底层的多终端功能。

    void _createDb(Database db, int newVersion) async {
        await db.execute(
            'CREATE TABLE $carsTable($colId INTEGER PRIMARY KEY AUTOINCREMENT, $colBrand TEXT, $colType TEXT, $colStart INTEGER)');
      }
    
      //  读取数据
      Future<List<Map<String, dynamic>>> getCarMapList() async {
        Database db = await this.database;
        var result = await db.query(carsTable);
        return result;
      }
    
      //  增加数据
      Future<int> insertCar(Car car) async {
        Database db = await this.database;
        var result = await db.insert(carsTable, car.toMap());
        return result;
      }
    
      //  刷新数据
      Future<int> updateCar(Car car) async {
        Database db = await this.database;
        var result = await db.update(carsTable, car.toMap(),
            where: '$colId = ?', whereArgs: [car.id]);
        return result;
      }
    
      //  删除数据
      Future<int> deleteCar(int id) async {
        Database db = await this.database;
        int result =
        await db.rawDelete('DELETE FROM $carsTable WHERE $colId = $id');
        return result;
      }
    复制代码

    第四步:相同的密钥空间管理?

    这个阶段也是核心。我们如何将数据库中的数据连接到UI部分并合理地管理渲染区域?听起来很复杂。 ,我们可以想出几个核心点,一点一点形成自己的部分⚒。

    • 首先,招标人已被选定为国家行政部门。按照传统,我们将封装的 provider 类与 ChangeNotifier 类结合起来。
    • 我们需要多个方法来进行状态更改,并且每个方法都必须通知更新,即添加 notifyListeners() 方法。
    • 我们需要明确我们需要哪些方法:添加数据、修改数据、减少数据、读取数据。相信上面的几点,看完下面的代码,你会有更多的想法吗?:
    class CarsProvider with ChangeNotifier {
      DatabaseHelper _databaseHelper = DatabaseHelper();
      List<Car> _cars;
      int _count = 0;
    
      UnmodifiableListView<Car> get allCars => UnmodifiableListView(_cars);
    
      UnmodifiableListView<Car> get unStartedCars =>
          UnmodifiableListView(_cars.where((car) => car.start == false));
    
      UnmodifiableListView<Car> get startedCars =>
          UnmodifiableListView(_cars.where((car) => car.start == true));
    
      void getCarList() {
        final Future<Database> dbFuture = _databaseHelper.initializeDatabase();
        dbFuture.then((database) {
          Future<List<Car>> carListFuture = _databaseHelper.getCarList();
          carListFuture.then((carList) {
            this._cars = carList;
            this._count = carList.length;
            notifyListeners();
          });
        });
      }
    
      int get count => _count;
    
      void addCar(Car car) async {
        int result;
        if (car.id != null) {
          result = await _databaseHelper.updateCar(car);
        } else {
          result = await _databaseHelper.insertCar(car);
        }
        if (result != 0) {
          print('Success');
        } else {
          print('Failed');
        }
        notifyListeners();
      }
    
      void startCar(BuildContext context, Car car) async {
        car.start = !car.start;
        int result = await _databaseHelper.updateCar(car);
    
        if (result != 0 && car.start == true) {
          _showSnackBar(context, '${car.brand}' +'  '+'${car.type}' + '      ' +'Start');
        }
        notifyListeners();
      }
    
      void deleteCar(BuildContext context, Car car) async {
        if (car.id == null) {
          _showSnackBar(context, 'No Car was deleted');
          return;
        }
    
        int result = await _databaseHelper.deleteCar(car.id);
        if (result != 0) {
          _showSnackBar(context, 'Car Deleted Successfully');
        } else {
          _showSnackBar(context, 'Error Occurred while deleting note');
        }
        notifyListeners();
      }
    
      void _showSnackBar(BuildContext context, String message) {
        final snackBar = SnackBar(
          duration: Duration(milliseconds: 500),
          content: Text(
            message,
            style: TextStyle(color: Colors.white),
          ),
          backgroundColor: Theme.of(context).primaryColor,
        );
        Scaffold.of(context).showSnackBar(snackBar);
      }
    }
    复制代码

    第五步:整个界面内容?

    其实上面两部分才是核心。如果你理解了上面两个核心类,我留下一些UI层面的操作,你应该就能轻松理解了吧?这里我给大家介绍一下添加车辆的功能。更多信息请参见文章末尾的源代码。

    //新增一辆车
      void onAdd() {
        if (carBrandController.text.isNotEmpty && carTypeController.text.isNotEmpty ) {
          final Car car = Car(brand: carBrandController.text, type:carTypeController.text, start: started);
          Provider.of<CarsProvider>(context, listen: false).addCar(car);
          Navigator.pop(context);
        }
      }

    作者:多肉葡萄五五糖
    链接:https://juejin.im/post/6863236259335766024
    来源:掘金版权归作者所有。商业转载请联系作者获得许可。非商业转载请注明出处。

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门