Flutter数据库管理插件sqlbrite的使用

发布于 1周前 作者 yuanlaile 来自 Flutter

Flutter数据库管理插件sqlbrite的使用

SQL Brite

SQL Brite 是一个为Flutter设计的,基于 sqflite 的响应式流包装库,灵感来源于 sqlbrite。它引入了响应式流语义到SQL操作中,使得在Flutter应用中处理数据库更加简单和直观。

作者

Petrus Nguyễn Thái Học

特点

  • 响应式流包装 sqflite
  • 支持流式 sqflite
  • 基于 RxDart 的响应式流 sqflite
  • 轻量级的 sqflite 包装,引入了响应式流语义到SQL操作中

开始使用

依赖配置

在你的Flutter项目中,在 pubspec.yaml 文件中添加依赖:

dependencies:
  ...
  sqlbrite: ^latest_version # 替换为最新版本号

安装依赖

通过命令行安装依赖:

$ flutter packages get

导入库

在Dart代码中导入 sqlbrite

import 'package:sqlbrite/sqlbrite.dart';

使用方法

1. 将数据库包装成 BriteDatabase

final Database db = await openDb();
final briteDb = BriteDatabase(db);
// 禁用日志记录
final briteDb = BriteDatabase(db, logger: null); 

2. 使用 BriteDatabase

创建实体模型

class Entity {
  factory Entity.fromJson(Map<String, dynamic> map) { ... }
  
  factory Entity.empty() { ... }

  Map<String, dynamic> toJson() { ... }
}

使用 mapToOne 扩展方法

// 发射单个行,如果行不存在或结果集中有多个行则发射错误。
final Stream<Entity> singleQuery$ = briteDb.createQuery(
  'table',
  where: 'id = ?',
  whereArgs: [id],
  limit: 1,
).mapToOne((row) => Entity.fromJson(row));

使用 mapToOneOrDefault 扩展方法

// 发射单个行,如果行不存在则发射给定的默认值,如果有多个行则发射错误。
final Stream<Entity> singleOrDefaultQuery$ = briteDb.createQuery(
  'table',
  where: 'id = ?',
  whereArgs: [id],
  limit: 1,
).mapToOneOrDefault(
  (row) => Entity.fromJson(row),
  defaultValue: Entity.empty()
);

使用 mapToList 扩展方法

// 发射行列表。
final Stream<List<Entity>> listQuery$ = briteDb.createQuery(
  'table',
  where: 'name LIKE ?',
  whereArgs: [queryName],
).mapToList((row) => Entity.fromJson(row));

类似 Database 的API

// 插入数据会触发查询流重新执行
briteDb.insert(
  'table',
  Entity(...).toJson()
);

// 更新数据会触发查询流重新执行
briteDb.update(
  'table',
  Entity(...).toJson(),
  where: 'id = ?',
  whereArgs: [id],
);

// 删除数据会触发查询流重新执行
briteDb.delete(
  'table',
  where: 'id = ?',
  whereArgs: [id],
);

使用 RxDart 操作符

briteDb
    .createQuery(
      'table',
      where: 'name LIKE ?',
      whereArgs: [queryName],
    )
    .debounceTime(const Duration(milliseconds: 500))
    .where(filterQuery) // 查询是惰性的,这允许你根据需要决定是否执行查询
    .mapToList((row) => Entity.fromJson(row))
    .listen(updateUI);

示例代码

以下是一个完整的示例代码,展示了如何在Flutter应用中使用 sqlbrite

import 'package:flutter/material.dart';
import 'package:sqlbrite/sqlbrite.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  final database = await openDatabase('example.db');
  final briteDb = BriteDatabase(database);

  runApp(MyApp(briteDb: briteDb));
}

class MyApp extends StatelessWidget {
  final BriteDatabase briteDb;

  const MyApp({super.key, required this.briteDb});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData.dark(),
      home: MyHomePage(briteDb: briteDb),
    );
  }
}

class MyHomePage extends StatefulWidget {
  final BriteDatabase briteDb;

  const MyHomePage({super.key, required this.briteDb});

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  late Stream<List<Map<String, dynamic>>> queryStream;

  @override
  void initState() {
    super.initState();
    queryStream = widget.briteDb.createQuery('items').mapToList((row) => row);
  }

  void addItem() async {
    await widget.briteDb.insert('items', {'name': 'Item ${DateTime.now()}'});
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('SQL Brite Example')),
      body: StreamBuilder<List<Map<String, dynamic>>>(
        stream: queryStream,
        builder: (context, snapshot) {
          if (!snapshot.hasData) {
            return Center(child: CircularProgressIndicator());
          }
          final items = snapshot.data!;
          return ListView.builder(
            itemCount: items.length,
            itemBuilder: (context, index) {
              final item = items[index];
              return ListTile(title: Text(item['name']));
            },
          );
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: addItem,
        child: Icon(Icons.add),
      ),
    );
  }
}

哲学

SQL Brite 的唯一职责是协调和组合表更新的通知,以便在数据更改时立即更新查询。它不是一个ORM,也不是类型安全的查询机制,也不会为你执行数据库迁移。

许可证

MIT License Copyright © 2019 - 2022 Petrus Nguyễn Thái Học


更多关于Flutter数据库管理插件sqlbrite的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter数据库管理插件sqlbrite的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何在Flutter项目中使用sqlbrite进行数据库管理的示例代码。不过需要注意的是,sqlbrite本身是一个用于Android的SQLite扩展库,它提供了响应式数据库访问能力。由于Flutter主要使用Dart语言,并且有自己的插件生态系统,通常我们会使用像sqflitefloor这样的插件来管理SQLite数据库。

然而,如果你确实想在Flutter项目中使用sqlbrite(尽管这不是最常见的做法),你通常会通过平台通道(Platform Channel)与原生Android代码进行交互。以下是一个简化的示例,展示如何在Flutter项目中集成sqlbrite

1. 设置Flutter项目

首先,创建一个新的Flutter项目:

flutter create flutter_sqlbrite_example
cd flutter_sqlbrite_example

2. 在Android原生代码中集成sqlbrite

打开android/app/src/main/java/com/your_company_name/flutter_sqlbrite_example/目录,并创建一个新的Kotlin文件,例如SqlBriteHelper.kt

package com.your_company_name.flutter_sqlbrite_example

import android.content.Context
import com.squareup.sqlbrite2.BriteDatabase
import com.squareup.sqlbrite2.SqlBrite
import java.util.concurrent.Executors

class SqlBriteHelper(context: Context) {
    private val sqlBrite: SqlBrite = SqlBrite.create()
    private val db: BriteDatabase = sqlBrite.wrapDatabaseHelper(
        context,
        "my_database.db",
        1
    ) { db ->
        // Create tables here if needed
        db.execSQL("CREATE TABLE IF NOT EXISTS example (id INTEGER PRIMARY KEY, name TEXT)")
    }

    fun insert(name: String) {
        db.insert("example", null, hashMapOf("name" to name))
    }

    fun queryAll(): List<Map<String, Any?>> {
        return db.query("SELECT * FROM example").map { cursor ->
            hashMapOf(
                "id" to cursor.getLong(cursor.getColumnIndexOrThrow("id")),
                "name" to cursor.getString(cursor.getColumnIndexOrThrow("name"))
            )
        }.toList()
    }
}

3. 创建平台通道

MainActivity.kt中设置平台通道以与Flutter代码通信:

package com.your_company_name.flutter_sqlbrite_example

import android.os.Bundle
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel

class MainActivity: FlutterActivity() {
    private val CHANNEL = "com.your_company_name.flutter_sqlbrite_example/channel"

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
            if (call.method == "insert") {
                val sqlBriteHelper = SqlBriteHelper(applicationContext)
                val name = call.argument<String>("name") ?: ""
                sqlBriteHelper.insert(name)
                result.success(null)
            } else if (call.method == "queryAll") {
                val sqlBriteHelper = SqlBriteHelper(applicationContext)
                val items = sqlBriteHelper.queryAll()
                result.success(items)
            } else {
                result.notImplemented()
            }
        }
    }
}

4. 在Flutter代码中调用平台通道

打开lib/main.dart,设置与原生代码的通信:

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  static const platform = MethodChannel('com.your_company_name.flutter_sqlbrite_example/channel');

  List<Map<String, dynamic>> _items = [];

  @override
  void initState() {
    super.initState();
    _queryAll();
  }

  Future<void> _insertItem(String name) async {
    try {
      await platform.invokeMethod('insert', {'name': name});
      setState(() {
        _queryAll();
      });
    } on PlatformException catch (e) {
      print("Failed to insert item: '${e.message}'.");
    }
  }

  Future<void> _queryAll() async {
    try {
      final result = await platform.invokeMethod('queryAll');
      setState(() {
        _items = result.cast<Map<String, dynamic>>();
      });
    } on PlatformException catch (e) {
      print("Failed to query items: '${e.message}'.");
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flutter SQLBrite Example'),
        ),
        body: Padding(
          padding: const EdgeInsets.all(8.0),
          child: Column(
            children: <Widget>[
              Expanded(
                child: ListView.builder(
                  itemCount: _items.length,
                  itemBuilder: (context, index) {
                    return ListTile(
                      title: Text('ID: ${_items[index]['id']}, Name: ${_items[index]['name']}'),
                    );
                  },
                ),
              ),
              TextField(
                decoration: InputDecoration(labelText: 'Enter name'),
                onSubmitted: (name) {
                  _insertItem(name);
                },
              ),
            ],
          ),
        ),
      ),
    );
  }
}

总结

上述代码展示了如何在Flutter项目中通过平台通道与原生Android代码集成,并在原生代码中使用sqlbrite进行数据库管理。然而,请注意,这种方法并不是最常见的做法,因为Flutter有自己的数据库管理插件,如sqflitefloor,它们更适合Flutter生态系统。如果你确实需要在Flutter中使用sqlbrite的特定功能,上述方法提供了一种可行的解决方案。

回到顶部