Flutter Realm 教程:构建跨平台数据同步应用
🌟 前言
在现代移动应用开发中,数据同步和本地存储是不可或缺的功能。今天我来分享一个使用 Realm 数据库的完整示例,展示如何在 Flutter 应用中实现跨平台的本地数据存储和实时同步功能。Realm 是一个现代化的对象数据库,不仅提供了高性能的本地存储,还支持云端数据同步,让你的应用能够在多设备间无缝同步数据。
🚀 为什么选择 Realm?
Realm 相比其他数据库解决方案具有以下优势:
- 🌐 跨平台一致性:支持 Android、iOS、Flutter 等多个平台
- 🔄 实时同步:支持云端数据同步,多设备实时更新
- 🛡️ 数据加密:内置加密功能保护敏感数据
- 🎯 对象 API:直观的对象导向数据访问方式
- ⚡ 高性能:优化的查询引擎和内存管理
- 📱 响应式编程:支持数据变化的实时监听
📦 项目配置
首先,让我们配置项目依赖。在 pubspec.yaml
中添加:
yaml
name: realm_example
description: "A new Flutter project."
publish_to: 'none'
version: 0.1.0
environment:
sdk: '>=3.3.4 <4.0.0'
dependencies:
flutter:
sdk: flutter
realm: ^20.0.1
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^3.0.0
flutter:
uses-material-design: true
🏗️ 数据模型设计
1. 定义 Person 和 Address 实体类
让我们从一个人员管理应用开始,定义包含关系映射的数据模型:
dart
import 'package:realm/realm.dart';
/// 导入自动生成的 Realm 模型文件
part 'person.realm.dart';
/// 人员模型类
/// 使用 @RealmModel() 注解标记为 Realm 数据模型
@RealmModel()
class _Person {
/// 主键ID,用于唯一标识每个人员记录
@PrimaryKey()
late String id;
/// 人员姓名,必填字段
late String name;
/// 人员年龄,必填字段
late int age;
/// 电子邮箱,可选字段
String? email;
/// 关联的地址信息,可选字段(一对一关系)
late _Address? addresses;
}
/// 地址模型类
@RealmModel()
class _Address {
/// 主键ID,用于唯一标识每个地址记录
@PrimaryKey()
late String id;
/// 街道地址,必填字段
late String street;
/// 城市名称,必填字段
late String city;
/// 国家名称,必填字段
late String country;
/// 邮政编码,可选字段
String? postalCode;
}
2. 关键注解说明
- @RealmModel():标记类为 Realm 数据模型
- @PrimaryKey():定义主键,确保数据唯一性
- part 'person.realm.dart':引入自动生成的 Realm 代码
- late:延迟初始化关键字,适用于必填字段
- 关系映射:通过对象引用实现一对一、一对多关系
💾 数据库管理器
创建一个单例模式的数据库管理器:
dart
import 'package:realm/realm.dart';
import 'package:realm_example/models/person.dart';
/// 数据库管理器类
/// 负责管理 Realm 数据库的初始化、配置和生命周期
class DatabaseManager {
/// 单例实例
static DatabaseManager? _instance;
/// Realm 数据库实例
late final Realm _realm;
/// 私有构造函数,用于实现单例模式
DatabaseManager._() {
// 配置 Realm 数据库
final config = Configuration.local(
/// 定义数据库中的模型架构
[Person.schema, Address.schema],
/// 设置数据库版本号,用于数据库升级
schemaVersion: 1,
/// 数据库版本迁移回调函数
migrationCallback: (migration, oldSchemaVersion) {
// 在这里实现数据库版本迁移逻辑
// 例如:添加新字段、修改字段类型、删除字段等
print('从版本 $oldSchemaVersion 升级到版本 1');
},
);
/// 创建 Realm 数据库实例
_realm = Realm(config);
}
/// 获取数据库管理器单例实例
static Future<DatabaseManager> getInstance() async {
/// 如果实例不存在,则创建新实例
_instance ??= DatabaseManager._();
return _instance!;
}
/// 获取 Realm 数据库实例
Realm get realm => _realm;
/// 关闭数据库连接
void close() {
_realm.close();
}
}
🗂️ 仓库层实现
使用仓库模式封装数据访问逻辑:
dart
import 'package:realm/realm.dart';
import 'package:realm_example/database/database_manager.dart';
import 'package:realm_example/models/person.dart';
/// 人员数据仓库类
/// 负责处理人员相关的数据库操作,包括增删查改
class PersonRepository {
final DatabaseManager _databaseManager;
late final Realm _realm;
PersonRepository(this._databaseManager) {
_realm = _databaseManager.realm;
}
/// 创建新的人员对象
Person createPerson(String name, int age, {String? email}) {
// 创建新的人员对象
final person = Person(
ObjectId().toString(), // 生成唯一ID
name,
age,
email: email,
);
// 在数据库事务中保存人员对象
_realm.write(() => _realm.add(person));
return person;
}
/// 为人员添加地址信息
void addAddress(Person person, String street, String city, String country,
{String? postalCode}) {
// 创建新的地址对象
final address = Address(
ObjectId().toString(),
street,
city,
country,
postalCode: postalCode,
);
// 在数据库事务中更新人员的地址信息
_realm.write(() {
person.addresses = address;
});
}
/// 获取所有人员对象
List<Person> getAllPersons() {
return _realm.all<Person>().toList();
}
/// 根据ID查找人员
Person? getPersonById(String id) {
return _realm.find<Person>(id);
}
/// 根据姓名查找人员
List<Person> findPersonsByName(String name) {
return _realm.query<Person>(r'name == "$0"', [name]).toList();
}
/// 更新人员信息
void updatePerson(Person person, {String? name, int? age, String? email}) {
// 在数据库事务中更新人员信息
_realm.write(() {
if (name != null) person.name = name;
if (age != null) person.age = age;
if (email != null) person.email = email;
});
}
/// 删除人员
void deletePerson(Person person) {
// 在数据库事务中删除人员
_realm.write(() {
_realm.delete(person);
});
}
/// 删除人员的地址
void deleteAddress(Person person, Address address) {
// 在数据库事务中删除地址
_realm.write(() {
person.addresses = null; // 清空人员的地址关联
_realm.delete(address);
});
}
}
🎨 UI 实现
创建一个完整的人员管理界面:
dart
import 'package:flutter/material.dart';
import 'package:realm_example/database/database_manager.dart';
import 'package:realm_example/models/person.dart';
import 'package:realm_example/repositories/person_repository.dart';
/// 应用程序主入口
void main() async {
// 确保 Flutter 绑定已初始化
WidgetsFlutterBinding.ensureInitialized();
// 获取数据库管理器实例
final databaseManager = await DatabaseManager.getInstance();
// 启动应用程序
runApp(MyApp(databaseManager: databaseManager));
}
class MyApp extends StatelessWidget {
final DatabaseManager databaseManager;
const MyApp({super.key, required this.databaseManager});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Realm示例',
home: PersonListPage(databaseManager: databaseManager),
);
}
}
class PersonListPage extends StatefulWidget {
final DatabaseManager databaseManager;
const PersonListPage({super.key, required this.databaseManager});
@override
State<PersonListPage> createState() => _PersonListPageState();
}
class _PersonListPageState extends State<PersonListPage> {
late PersonRepository _personRepository;
List<Person> _persons = [];
@override
void initState() {
super.initState();
_personRepository = PersonRepository(widget.databaseManager);
_loadPersons();
}
/// 加载所有人员数据
void _loadPersons() {
setState(() {
_persons = _personRepository.getAllPersons();
});
}
/// 添加新人员
Future<void> _addPerson() async {
// 创建新人员
final person = _personRepository.createPerson(
'name',
25,
email: 'realm@example.com'
);
// 为人员添加地址
_personRepository.addAddress(
person,
'某某路某某号',
'上海',
'中国',
postalCode: '000000',
);
// 重新加载人员列表
_loadPersons();
}
/// 更新人员信息
void _updatePerson(Person person) {
_personRepository.updatePerson(
person,
name: '${person.name}(更新)',
age: person.age + 1,
);
_loadPersons();
}
/// 删除人员
void _deletePerson(Person person) {
_personRepository.deletePerson(person);
_loadPersons();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Realm数据库示例'),
),
body: ListView.builder(
itemCount: _persons.length,
itemBuilder: (context, index) {
final person = _persons[index];
return Card(
margin: const EdgeInsets.all(8.0),
child: ListTile(
title: Text(person.name),
subtitle: Text(
'''
年龄: ${person.age}
邮箱: ${person.email ?? "无"}
街道: ${person.addresses?.street ?? "无"}
城市: ${person.addresses?.city ?? "无"}
国家: ${person.addresses?.country ?? "无"}
编码: ${person.addresses?.postalCode ?? "无"}
''',
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: const Icon(Icons.edit),
onPressed: () => _updatePerson(person),
),
IconButton(
icon: const Icon(Icons.delete),
onPressed: () => _deletePerson(person),
),
],
),
),
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: _addPerson,
child: const Icon(Icons.add),
),
);
}
}
🔧 代码生成
在修改模型后,需要运行代码生成命令:
bash
dart run realm generate
这会生成 person.realm.dart
文件,包含必要的 Realm 对象代码。
💡 核心特性详解
1. 版本管理
Realm 通过 schemaVersion
和 migrationCallback
实现版本管理:
dart
final config = Configuration.local(
[Person.schema, Address.schema],
schemaVersion: 2, // 增加版本号
migrationCallback: (migration, oldSchemaVersion) {
// 处理从旧版本到新版本的数据迁移
if (oldSchemaVersion < 2) {
// 添加新字段的默认值
migration.renameProperty('Person', 'oldName', 'name');
}
},
);
2. 查询功能
Realm 提供了强大的查询功能:
dart
// 简单查询
var adults = realm.query<Person>('age >= 18');
// 条件查询
var emailUsers = realm.query<Person>('email != nil');
// 排序查询
var sortedPersons = realm.query<Person>('TRUEPREDICATE SORT(age ASC)');
// 复合查询
var query = realm.query<Person>('name CONTAINS "张" AND age > 20');
3. 关系映射
Realm 支持多种关系类型:
dart
// 一对一关系
@RealmModel()
class _Person {
late _Address? addresses; // 可选的一对一关系
}
// 一对多关系
@RealmModel()
class _Person {
late List<_Address> addresses; // 一对多关系
}
// 反向关系
@RealmModel()
class _Address {
@Backlink(#addresses)
late Iterable<_Person> persons; // 反向关系
}
4. 响应式编程
Realm 支持数据变化的实时监听:
dart
// 监听集合变化
realm.all<Person>().changes.listen((changes) {
// 处理数据变化
print('插入: ${changes.inserted}');
print('删除: ${changes.deleted}');
print('修改: ${changes.modified}');
});
// 监听对象变化
person.changes.listen((change) {
print('对象发生变化: ${change.object}');
});
🌐 云端同步功能
1. 配置同步
dart
final app = App(AppConfiguration('your-app-id'));
final user = await app.logIn(Credentials.anonymous());
final config = Configuration.flexibleSync(
user,
[Person.schema, Address.schema],
);
final realm = Realm(config);
// 添加同步订阅
realm.subscriptions.update((mutableSubscriptions) {
mutableSubscriptions.add(realm.all<Person>());
});
2. 权限控制
dart
// 基于用户的数据隔离
@RealmModel()
class _Person {
@PrimaryKey()
late String id;
late String ownerId; // 用户ID,用于权限控制
late String name;
late int age;
}
🎯 最佳实践
1. 数据库初始化
dart
class DatabaseService {
static DatabaseService? _instance;
late final Realm _realm;
static Future<DatabaseService> getInstance() async {
if (_instance == null) {
_instance = DatabaseService._();
await _instance!._init();
}
return _instance!;
}
DatabaseService._();
Future<void> _init() async {
final config = Configuration.local([Person.schema, Address.schema]);
_realm = Realm(config);
}
}
2. 错误处理
dart
try {
realm.write(() {
realm.add(person);
});
} on RealmException catch (e) {
print('Realm 错误: ${e.message}');
} catch (e) {
print('其他错误: $e');
}
3. 内存管理
dart
// 正确关闭 Realm 实例
@override
void dispose() {
realm.close();
super.dispose();
}
// 使用 Realm 的冻结功能传递数据
final frozenPerson = person.freeze();
📊 性能优化技巧
1. 批量操作
dart
// 批量插入
realm.write(() {
for (var personData in personDataList) {
realm.add(Person(personData));
}
});
// 使用事务减少写入次数
realm.write(() {
// 多个操作在同一事务中
realm.add(person1);
realm.add(person2);
realm.delete(oldPerson);
});
2. 索引优化
dart
@RealmModel()
class _Person {
@PrimaryKey()
late String id;
@Indexed() // 添加索引提高查询性能
late String name;
late int age;
}
3. 查询优化
dart
// 使用 limit 限制结果数量
var results = realm.query<Person>('age > 18 LIMIT(10)');
// 使用 distinct 去重
var uniqueNames = realm.query<Person>('DISTINCT(name)');
🚨 常见问题与解决方案
1. 模型变更处理
dart
// 字段重命名
migrationCallback: (migration, oldSchemaVersion) {
if (oldSchemaVersion < 2) {
migration.renameProperty('Person', 'oldName', 'newName');
}
}
// 字段类型变更
migrationCallback: (migration, oldSchemaVersion) {
if (oldSchemaVersion < 2) {
var oldObjects = migration.findInNewRealm('Person');
for (var obj in oldObjects) {
obj['age'] = int.parse(obj['age']); // String -> int
}
}
}
2. 同步冲突处理
dart
// 自定义冲突解决策略
final config = Configuration.flexibleSync(
user,
[Person.schema],
clientResetHandler: ManualRecoveryHandler(
onClientReset: (realm, clientResetError) {
// 处理客户端重置
},
),
);
3. 性能监控
dart
// 启用性能监控
final config = Configuration.local(
[Person.schema],
shouldCompactOnLaunch: (totalBytes, usedBytes) {
// 当已使用空间小于总空间的50%时进行压缩
return (usedBytes / totalBytes) < 0.5;
},
);
🎉 总结
Realm 为 Flutter 应用提供了一个功能强大的数据库解决方案。通过本教程的学习,你应该能够:
- ✅ 理解 Realm 的核心概念和优势
- ✅ 掌握数据模型的定义和关系映射
- ✅ 实现完整的数据库管理架构
- ✅ 构建功能完整的 CRUD 操作
- ✅ 了解版本管理和数据迁移
- ✅ 实现响应式数据更新
- ✅ 配置云端数据同步
Realm 的对象导向 API、自动同步功能和强大的查询能力,使其成为现代 Flutter 应用的理想选择。在实际项目中,建议根据具体需求选择本地存储或云端同步模式,充分利用 Realm 的各项特性来构建高效、可扩展的数据层。
🔗 相关资源
希望这个教程对你有所帮助!如果你有任何问题或建议,欢迎在评论区交流讨论。