Flutter数据同步插件synchroflite的使用

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

Flutter数据同步插件synchroflite的使用

Dart实现的Conflict-free Replicated Data Types (CRDTs),提供Sqflite API。 基于Daniel Cachapa的工作。

该实现的范围是为了在drift_crdt中使用API。

此包实现了sql_crdt。

设置

等待异步函数完成非常重要,不这样做可能会导致各种奇怪的行为。
你可以通过在analysis_options.yaml中激活unawaited_futures linter警告来避免它们:

linter:
  rules:
    unawaited_futures: true

此包使用了sqflite。根据你打算运行代码的位置,需要一些额外的设置:

Android & iOS

synchroflite使用了一些可能不在每个系统嵌入库中的最新Sqlite功能。

为了解决这个问题,你需要将sqlite3_flutter_libs包导入到你的项目中:

sqlite3_flutter_libs: ^0.5.12
桌面和服务器

在桌面和服务器上,Sqflite使用系统库,因此请确保已安装这些库。

在Debian, Raspbian, Ubuntu等系统上:

sudo apt install libsqlite3 libsqlite3-dev

在Fedora上:

sudo dnf install sqlite-devel

否则,请查看sqflite_common_ffi的说明。

Web

此包具有对Flutter Web的实验性支持,感谢sqflite_common_ffi_web。

为了使用此功能,你需要安装Sqlite3 Web二进制文件,从项目的根目录运行以下命令:

dart run sqflite_common_ffi_web:setup

使用

更多详细信息,请参阅example.dart

特性和错误

请在issue tracker中提交特性请求和错误报告。

注意

此包并不实现完整的Sqflite API。它只实现了drift_crdt所需的API。如果你需要更多功能,请打开一个issue或PR。


示例代码

// Copyright 2023 Janez Stupar
// This code is based on Daniel Cachapa's work in sqlite_crdt:
// https://github.com/cachapa/sqlite_crdt
// SPDX-License-Identifier: Apache-2.0
import 'package:synchroflite/synchroflite.dart';

Future<void> main() async {
  // 创建或加载数据库
  final crdt = await Synchroflite.openInMemory(
    version: 1,
    onCreate: (db, version) async {
      // 创建一个表
      await db.execute('''
        CREATE TABLE users (
          id INTEGER NOT NULL,
          name TEXT,
          PRIMARY KEY (id)
        )
      ''');
    },
  );

  // 向数据库插入一条记录
  await crdt.execute('''
    INSERT INTO users (id, name)
    VALUES (?1, ?2)
  ''', [1, 'John Doe']);

  // 删除该记录
  await crdt.execute('DELETE FROM users WHERE id = ?1', [1]);

  // 合并远程数据集
  await crdt.merge({
    'users': [
      {
        'id': 2,
        'name': 'Jane Doe',
        'hlc': Hlc.now(generateNodeId()), // 使用当前节点生成的时间戳
      },
    ],
  });

  // 查询语句很简单,但请注意:
  // 1. CRDT列:hlc, modified, is_deleted
  // 2. 先生Doe以is_deleted = 1的形式出现在结果中
  final result = await crdt.query('SELECT * FROM users');
  printRecords('SELECT * FROM users', result);

  // 或许更好的查询是
  final betterResult =
      await crdt.query('SELECT id, name FROM users WHERE is_deleted = 0');
  printRecords('SELECT id, name FROM users WHERE is_deleted = 0', betterResult);

  // 我们也可以观察特定查询的结果,但请注意这可能效率低下,因为每次数据库更改都会重新运行观察查询
  crdt.watch('SELECT id, name FROM users WHERE is_deleted = 0').listen((e) =>
      printRecords(
          'Watch: SELECT id, name FROM users WHERE is_deleted = 0', e));

  // 更新数据库
  await crdt.execute('''
    UPDATE users SET name = ?1
    WHERE id = ?2
  ''', ['Jane Doe 👍', 2]);

  // 因为条目只是被标记为删除,所以撤销删除操作非常简单
  await crdt.execute('''
    UPDATE users SET is_deleted = ?1
    WHERE id = ?2
  ''', [1, 1]);

  // 在事务中执行多个写操作,以便它们获得相同的时间戳
  await crdt.transaction((txn) async {
    // 确保你使用事务对象(txn)
    // 使用[crdt]会导致死锁
    await txn.execute('''
      INSERT INTO users (id, name)
      VALUES (?1, ?2)
    ''', [3, 'Uncle Doe']);
    await txn.execute('''
      INSERT INTO users (id, name)
      VALUES (?1, ?2)
    ''', [4, 'Grandma Doe']);
  });
  final timestamps =
      await crdt.query('SELECT id, hlc, modified FROM users WHERE id > 2');
  printRecords('SELECT id, hlc, modified FROM users WHERE id > 2', timestamps);

  // 创建一个变更集以与另一个节点同步
  final changeset = await crdt.getChangeset();
  print('> 变更集大小: ${changeset.recordCount} 条记录');
  changeset.forEach((key, value) {
    print(key);
    for (var e in value) {
      print('  $e');
    }
  });
}

void printRecords(String title, List<Map<String, Object?>> records) {
  print('> $title');
  records.forEach(print);
}

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

1 回复

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


当然,以下是一个关于如何使用Flutter数据同步插件synchroflite的示例代码。synchroflite是一个用于Flutter的SQLite数据库同步插件,可以帮助你在多个设备之间同步SQLite数据库。

首先,确保你已经在pubspec.yaml文件中添加了synchroflite依赖:

dependencies:
  flutter:
    sdk: flutter
  synchroflite: ^最新版本号 # 请替换为当前最新版本号

然后,运行flutter pub get来获取依赖。

接下来,我们将展示如何使用synchroflite来同步SQLite数据库。这个示例将包括初始化数据库、配置同步服务器、以及执行数据同步的基本步骤。

1. 初始化数据库

首先,我们需要初始化SQLite数据库并创建一个表:

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Synchroflite Example'),
        ),
        body: SynchrofliteExample(),
      ),
    );
  }
}

class SynchrofliteExample extends StatefulWidget {
  @override
  _SynchrofliteExampleState createState() => _SynchrofliteExampleState();
}

class _SynchrofliteExampleState extends State<SynchrofliteExample> {
  late SynchrofliteDatabaseHelper _dbHelper;

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

  Future<void> _initializeDatabase() async {
    _dbHelper = SynchrofliteDatabaseHelper(
      databaseName: 'example.db',
      tables: [
        SynchrofliteTable(
          tableName: 'users',
          columns: [
            SynchrofliteColumn(name: 'id', type: 'INTEGER', primaryKey: true, autoIncrement: true),
            SynchrofliteColumn(name: 'name', type: 'TEXT'),
            SynchrofliteColumn(name: 'email', type: 'TEXT'),
          ],
        ),
      ],
    );

    // Create the database
    await _dbHelper.openDatabase();

    // Create the sync configuration (this would typically be done once)
    await _dbHelper.createSyncConfig(
      syncUrl: 'https://your-sync-server-url.com/sync', // Replace with your sync server URL
      tables: ['users'],
    );
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Text('Database Initialized'),
    );
  }
}

2. 插入数据

接下来,我们插入一些数据到数据库中:

Future<void> _insertData() async {
    final db = await _dbHelper.database;
    await db.insert(
      'users',
      {
        'name': 'John Doe',
        'email': 'john.doe@example.com',
      },
      conflictAlgorithm: ConflictAlgorithm.replace,
    );
  }

你可以在按钮点击事件中调用_insertData()方法:

ElevatedButton(
  onPressed: () async {
    await _insertData();
    ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Data Inserted')));
  },
  child: Text('Insert Data'),
),

3. 执行同步

最后,我们可以执行数据同步:

Future<void> _syncData() async {
    await _dbHelper.syncData();
    ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Data Synced')));
}

同样地,你可以在另一个按钮点击事件中调用_syncData()方法:

ElevatedButton(
  onPressed: () async {
    await _syncData();
  },
  child: Text('Sync Data'),
),

完整示例

将上述代码整合到一个完整的示例中:

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Synchroflite Example'),
        ),
        body: SynchrofliteExample(),
      ),
    );
  }
}

class SynchrofliteExample extends StatefulWidget {
  @override
  _SynchrofliteExampleState createState() => _SynchrofliteExampleState();
}

class _SynchrofliteExampleState extends State<SynchrofliteExample> {
  late SynchrofliteDatabaseHelper _dbHelper;

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

  Future<void> _initializeDatabase() async {
    _dbHelper = SynchrofliteDatabaseHelper(
      databaseName: 'example.db',
      tables: [
        SynchrofliteTable(
          tableName: 'users',
          columns: [
            SynchrofliteColumn(name: 'id', type: 'INTEGER', primaryKey: true, autoIncrement: true),
            SynchrofliteColumn(name: 'name', type: 'TEXT'),
            SynchrofliteColumn(name: 'email', type: 'TEXT'),
          ],
        ),
      ],
    );

    await _dbHelper.openDatabase();
    await _dbHelper.createSyncConfig(
      syncUrl: 'https://your-sync-server-url.com/sync', // Replace with your sync server URL
      tables: ['users'],
    );
  }

  Future<void> _insertData() async {
    final db = await _dbHelper.database;
    await db.insert(
      'users',
      {
        'name': 'John Doe',
        'email': 'john.doe@example.com',
      },
      conflictAlgorithm: ConflictAlgorithm.replace,
    );
  }

  Future<void> _syncData() async {
    await _dbHelper.syncData();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        ElevatedButton(
          onPressed: () async {
            await _insertData();
            ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Data Inserted')));
          },
          child: Text('Insert Data'),
        ),
        SizedBox(height: 20),
        ElevatedButton(
          onPressed: () async {
            await _syncData();
            ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Data Synced')));
          },
          child: Text('Sync Data'),
        ),
      ],
    );
  }
}

请注意,上述代码示例假设你已经有一个同步服务器在运行,并且URL https://your-sync-server-url.com/sync 是有效的。你需要根据你的实际情况替换这个URL。此外,synchroflite插件的具体API可能会随着版本的更新而有所变化,因此请参考最新的官方文档以确保兼容性。

回到顶部