闽公网安备 35020302035485号
                dev_dependencies:
    flutter_test:
        sdk: flutter
    # ...
    floor_generator: ^1.4.1
    build_runner: ^2.3.3
接下来我们就以之前的备忘录为例,来看看使用 floor 后的改善。// 堆代码 duidaima.com
@entity
class Memo {
  @PrimaryKey(autoGenerate: true)
  final int? id;
  String title;
  String content;
  @ColumnInfo(name: 'created_time')
  DateTime createdTime;
  @ColumnInfo(name: 'modified_time')
  DateTime modifiedTime;
  List<String> tags;
  Memo({
    this.id,
    required this.title,
    required this.content,
    required this.createdTime,
    required this.modifiedTime,
    required this.tags,
  });
}
这里说明一下常见的注解:@ignore:忽略某个成员属性,即该属性不产生相应的数据表字段。注意,通过 get 方法产生的计算属性默认就会被忽略,例如长方形面积 double get area => width * height。
	
@dao
abstract class MemoDao {
  @Query('SELECT * FROM Memo ORDER BY modified_time DESC')
  Future<List<Memo>> findAllMemos();
  @Query(
      'SELECT * FROM Memo WHERE title LIKE :searchKey OR content LIKE :searchKey ORDER BY modified_time DESC')
  Future<List<Memo>> findMemoWithSearchKey(String searchKey);
  @Query('SELECT * FROM Memo WHERE id = :id')
  Stream<Memo?> findMemoById(int id);
  @insert
  Future<void> insertMemo(Memo memo);
  @Update(onConflict: OnConflictStrategy.replace)
  Future<void> updateMemo(Memo memo);
  @delete
  Future<void> deleteMemo(Memo memo);
}
class StringListConverter extends TypeConverter<List<String>, String> {
  @override
  List<String> decode(String databaseValue) {
    return databaseValue.isNotEmpty ? databaseValue.split('|') : [];
  }
  @override
  String encode(List<String> value) {
    return value.join('|');
  }
}
class DateTimeConverter extends TypeConverter<DateTime, int> {
  @override
  DateTime decode(int databaseValue) {
    return DateTime.fromMillisecondsSinceEpoch(databaseValue);
  }
  @override
  int encode(DateTime value) {
    return value.millisecondsSinceEpoch;
  }
}
使用转换器只需要在定义数据库FloorDatabase 的抽象类的时候引入到注解@TypeConverters就可以了。@TypeConverters([StringListConverter, DateTimeConverter])
@Database(version: 1, entities: [Memo])
abstract class MemoDatabase extends FloorDatabase {
  MemoDao get memoDao;
}
代码改造Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  final database =
      await $FloorMemoDatabase.databaseBuilder('app_database.db').build();
  final dao = database.memoDao;
  getIt.registerSingleton<MemoDao>(dao, signalsReady: true);
  runApp(const MyApp());
}
这里调用ensureInitialized这个方法是保证 Flutter 和原生交互的部分已经完成,因为在 sqflite 中需要使用原生的文件存储。备忘录列表的代码改造涉及数据操作的有两处,分别是列表刷新和删除备忘录。列表模糊搜索时需要自己组装模糊搜索的字符,比如我们这里使用了百分号将搜索关键词包裹实现任意匹配。删除备忘录需要根据是否有搜索调用不同的方法,这是因为对应的 SQL 不同。void _refreshMemoList({String? searchKey}) async {
  List<Memo> memoList = searchKey == null
      ? await GetIt.I<MemoDao>().findAllMemos()
      : await GetIt.I<MemoDao>().findMemoWithSearchKey('%$searchKey%');
  setState(() {
    _memoList = memoList;
  });
}
删除就非常简单了,直接调用删除方法就好了。void _deleteMemo(Memo memo) async {
    final confirmed = await _showDeleteConfirmationDialog(memo);
    if (confirmed != null && confirmed) {
      await GetIt.I<MemoDao>().deleteMemo(memo);
      _refreshMemoList();
      if (!mounted) return;
      ScaffoldMessenger.of(context).showSnackBar(SnackBar(
        content: Text('已删除 "${memo.title}"'),
        duration: const Duration(seconds: 2),
      ));
    }
  }
添加备忘录的页面只需要更改保存备忘录的方法,而且因为不需要再对时间做转换,方法更为简洁。Future<void> _saveMemo(BuildContext context) async {
  var memo = Memo(
      title: _title,
      content: _content,
      createdTime: DateTime.now(),
      modifiedTime: DateTime.now(),
      tags: _tags);
  // 保存备忘录
  await GetIt.I<MemoDao>().insertMemo(memo);
}
编辑备忘录页面也类似,调用 updateMemo 方法即可完成保存。Future<void> _saveMemo(BuildContext context) async {
  widget.memo.title = _title;
  widget.memo.content = _content;
  widget.memo.modifiedTime = DateTime.now();
  // 保存备忘录
  await GetIt.I<MemoDao>().updateMemo(widget.memo);
}