【发布时间】:2020-08-29 13:51:14
【问题描述】:
我正在尝试使用 sambas 数据库,因为我需要将它用于我的应用程序的 Web 版本,但我正在努力解决它。
我的应用程序中有一个OpeningTimes 表单,我将它作为地图保存到数据库中。
我在保存方法中使用的逻辑是:使用我传入的OpeningTimes 对象的userName 参数从db 加载表单,如果没有找到则保存它,如果找到记录则使用传入对象。我正在使用 flutter_bloc 来管理它,所以在OpeningTimesBloc 中,当表单保存到数据库时,它会加载它并生成它,以便将 UI 更新到上次保存的状态。问题是更新后从db加载时,加载的值不是update方法中传入的值(例如monMorAct:要从true更新为false):
flutter: updateOpeningTimes() : update opening time received {userName: zazza zenigata, monMorOp: 10:00, monMorCl: 19:00, monMorAct: false, monAftOp: , monAftCl: , monAftAct: false, tueMorOp: , tueMorCl: , tueMorAct: false, tueAftOp: , tueAftCl: , tueAftAct: false, wedMorOp: , wedMorCl: , wedMorAct: false, wedAftOp: , wedAftCl: , wedAftAct: false, thuMorOp: , thuMorCl: , thuMorAct: false, thuAftOp: , thuAftCl: , thuAftAct: false, friMorOp: , friMorCl: , friMorAct: false, friAftOp: , friAftCl: , friAftAct: false, satMorOp: , satMorCl: , satMorAct: false, satAftOp: , satAftCl: , satAftAct: false, sunMorOp: , sunMorCl: , sunMorAct: false, sunAftOp: , sunAftCl: , sunAftAct: false}
但在加载时:
flutter: loadOpeningTimes() snapshot is: Record(openingTimeStorage, 1) {userName: zazza zenigata, monMorOp: 10:00, monMorCl: 19:00, monMorAct: true, monAftOp: , monAftCl: , monAftAct: false, tueMorOp: , tueMorCl: , tueMorAct: false, tueAftOp: , tueAftCl: , tueAftAct: false, wedMorOp: , wedMorCl: , wedMorAct: false, wedAftOp: , wedAftCl: , wedAftAct: false, thuMorOp: , thuMorCl: , thuMorAct: false, thuAftOp: , thuAftCl: , thuAftAct: false, friMorOp: , friMorCl: , friMorAct: false, friAftOp: , friAftCl: , friAftAct: false, satMorOp: , satMorCl: , satMorAct: false, satAftOp: , satAftCl: , satAftAct: false, sunMorOp: , sunMorCl: , sunMorAct: false, sunAftOp: , sunAftCl: , sunAftAct: false}
你能发现我做错了什么吗? 一如既往,非常感谢您的时间和帮助。
这是单例:
import 'dart:async';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sembast/sembast.dart';
import 'package:sembast/sembast_io.dart';
class AppDatabase {
// Singleton instance
static final AppDatabase _singleton = AppDatabase._();
// Singleton accessor
static AppDatabase get instance => _singleton;
// Completer is used for transforming synchronous code into asynchronous code.
Completer<Database> _dbOpenCompleter;
// A private constructor. Allows us to create instances of AppDatabase
// only from within the AppDatabase class itself.
AppDatabase._();
// Sembast database object
Database _database;
// Database object accessor
Future<Database> get database async {
// If completer is null, AppDatabaseClass is newly instantiated, so database is not yet opened
if (_dbOpenCompleter == null) {
_dbOpenCompleter = Completer();
// Calling _openDatabase will also complete the completer with database instance
_openDatabase();
}
// If the database is already opened, awaiting the future will happen instantly.
// Otherwise, awaiting the returned future will take some time - until complete() is called
// on the Completer in _openDatabase() below.
return _dbOpenCompleter.future;
}
Future _openDatabase() async {
// Get a platform-specific directory where persistent app data can be stored
final appDocumentDir = await getApplicationDocumentsDirectory();
// Path with the form: /platform-specific-directory/demo.db
final dbPath = join(appDocumentDir.path, 'demo.db');
final database = await databaseFactoryIo.openDatabase(dbPath);
// Any code awaiting the Completer's future will now start executing
_dbOpenCompleter.complete(database);
}
}
这是集团:
class OpeningTimesBloc extends Bloc<OpeningTimesEvent, OpeningTimesState> {
OpeningTimesRepository _openingTimesRepository = OpeningTimesRepository();
@override
OpeningTimesState get initialState => InitialState();
@override
Stream<OpeningTimesState> mapEventToState(OpeningTimesEvent event) async* {
if (event is LoadOpeningTimes) {
print('GetOpeningTimes event received');
yield* _mapLoadOpeningTimesToState(event);
}
if (event is SaveOpeningTimes) {
yield* _mapSaveOpeningTimesToState(event);
}
}
Stream<OpeningTimesState> _mapLoadOpeningTimesToState(
LoadOpeningTimes event) async* {
OpeningTimes openingTimes = await _openingTimesRepository.loadOpeningTimes(
userName: event.user.name);
print(
'_mapLoadOpeningTimesToState() loaded openingTimes is: ${openingTimes.toMap().toString()}');
yield ShopOpeningTimes(openingTimes);
}
Stream<OpeningTimesState> _mapSaveOpeningTimesToState(
SaveOpeningTimes event) async* {
await _openingTimesRepository.saveOpeningTimes(event.openingTimes);
add(LoadOpeningTimes(event.user));
}
}
这是存储库:
import 'package:sembast/sembast.dart';
class OpeningTimesRepository {
// name for the storage
static const String openingTimeStorage = 'openingTimeStorage';
// storage reference
final _openingTimesFolder = intMapStoreFactory.store(openingTimeStorage);
// database instance
Future<Database> get _db async => await AppDatabase.instance.database;
Future saveOpeningTimes(OpeningTimes openingTimes) async {
print(
'saveOpeningTimes(): save opening times received ${openingTimes.toMap().toString()}'); // prints correct
final snapshot = await loadOpeningTimes(userName: openingTimes.userName);
print('saveOpeningTimes() snapshot is $snapshot');
if (snapshot == null) {
print('opening times are to save');
await _openingTimesFolder.add(await _db, openingTimes.toMap());
} else {
print('opening times are to update');
await updateOpeningTimes(openingTimes);
}
}
Future updateOpeningTimes(OpeningTimes openingTimes) async {
print(
'updateOpeningTimes() : update opening time received ${openingTimes.toMap().toString()}'); // correct
final Finder finder = Finder(filter: Filter.byKey(openingTimes.userName));
await _openingTimesFolder.update(await _db, openingTimes.toMap(),
finder: finder);
}
Future<OpeningTimes> loadOpeningTimes({String userName}) async {
print(
'loadOpeningTimes() called userName is $userName'); // prints correct userName
final finder = Finder(sortOrders: [SortOrder('userName')]);
final snapshot =
await _openingTimesFolder.findFirst(await _db, finder: finder);
print('loadOpeningTimes() snapshot is: ${snapshot}'); // correct
return OpeningTimes.fromMap(snapshot.value);
}
}
这是模型:
import 'package:flutter/material.dart';
class OpeningTimes {
int id;
final String userName;
final String monMorOp;
final String monMorCl;
final bool monMorAct;
final String monAftOp;
final String monAftCl;
final bool monAftAct;
final String tueMorOp;
final String tueMorCl;
final bool tueMorAct;
final String tueAftOp;
final String tueAftCl;
final bool tueAftAct;
final String wedMorOp;
final String wedMorCl;
final bool wedMorAct;
final String wedAftOp;
final String wedAftCl;
final bool wedAftAct;
final String thuMorOp;
final String thuMorCl;
final bool thuMorAct;
final String thuAftOp;
final String thuAftCl;
final bool thuAftAct;
final String friMorOp;
final String friMorCl;
final bool friMorAct;
final String friAftOp;
final String friAftCl;
final bool friAftAct;
final String satMorOp;
final String satMorCl;
final bool satMorAct;
final String satAftOp;
final String satAftCl;
final bool satAftAct;
final String sunMorOp;
final String sunMorCl;
final bool sunMorAct;
final String sunAftOp;
final String sunAftCl;
final bool sunAftAct;
OpeningTimes(
{@required this.userName,
@required this.monMorOp,
@required this.monMorCl,
@required this.monMorAct,
@required this.monAftOp,
@required this.monAftCl,
@required this.monAftAct,
@required this.tueMorOp,
@required this.tueMorCl,
@required this.tueMorAct,
@required this.tueAftOp,
@required this.tueAftCl,
@required this.tueAftAct,
@required this.wedMorOp,
@required this.wedMorCl,
@required this.wedMorAct,
@required this.wedAftOp,
@required this.wedAftCl,
@required this.wedAftAct,
@required this.thuMorOp,
@required this.thuMorCl,
@required this.thuMorAct,
@required this.thuAftOp,
@required this.thuAftCl,
@required this.thuAftAct,
@required this.friMorOp,
@required this.friMorCl,
@required this.friMorAct,
@required this.friAftOp,
@required this.friAftCl,
@required this.friAftAct,
@required this.satMorOp,
@required this.satMorCl,
@required this.satMorAct,
@required this.satAftOp,
@required this.satAftCl,
@required this.satAftAct,
@required this.sunMorOp,
@required this.sunMorCl,
@required this.sunMorAct,
@required this.sunAftOp,
@required this.sunAftCl,
@required this.sunAftAct});
@override
List<Object> get props => [
userName,
monMorOp,
monMorCl,
monMorAct,
monAftOp,
monAftCl,
monAftAct,
tueMorOp,
tueMorCl,
tueMorAct,
tueAftOp,
tueAftCl,
tueAftAct,
wedMorOp,
wedMorCl,
wedMorAct,
wedAftOp,
wedAftCl,
wedAftAct,
thuMorOp,
thuMorCl,
thuMorAct,
thuAftOp,
thuAftCl,
thuAftAct,
friMorOp,
friMorCl,
friMorAct,
friAftOp,
friAftCl,
friAftAct,
satMorOp,
satMorCl,
satMorAct,
satAftOp,
satAftCl,
satAftAct,
sunMorOp,
sunMorCl,
sunMorAct,
sunAftOp,
sunAftCl,
sunAftAct
];
factory OpeningTimes.fromMap(Map<String, dynamic> map) => new OpeningTimes(
userName: map['userName'],
monMorOp: map['monMorOp'],
monMorCl: map['monMorCl'],
monMorAct: map['monMorAct'],
monAftOp: map['monAftOp'],
monAftCl: map['monAftCl'],
monAftAct: map['monAftAct'],
tueMorOp: map['tueMorOp'],
tueMorCl: map['tueMorCl'],
tueMorAct: map['tueMorAct'],
tueAftOp: map['tueAftOp'],
tueAftCl: map['tueAftCl'],
tueAftAct: map['tueAftAct'],
wedMorOp: map['wedMorOp'],
wedMorCl: map['wedMorCl'],
wedMorAct: map['wedMorAct'],
wedAftOp: map['wedAftOp'],
wedAftCl: map['wedAftCl'],
wedAftAct: map['wedAftAct'],
thuMorOp: map['thuMorOp'],
thuMorCl: map['thuMorCl'],
thuMorAct: map['thuMorAct'],
thuAftOp: map['thuAftOp'],
thuAftCl: map['thuAftCl'],
thuAftAct: map['thuAftAct'],
friMorOp: map['friMorOp'],
friMorCl: map['friMorCl'],
friMorAct: map['friMorAct'],
friAftOp: map['friAftOp'],
friAftCl: map['friAftCl'],
friAftAct: map['friAftAct'],
satMorOp: map['satMorOp'],
satMorCl: map['satMorCl'],
satMorAct: map['satMorAct'],
satAftOp: map['satAftOp'],
satAftCl: map['satAftCl'],
satAftAct: map['satAftAct'],
sunMorOp: map['sunMorOp'],
sunMorCl: map['sunMorCl'],
sunMorAct: map['sunMorAct'],
sunAftOp: map['sunAftOp'],
sunAftCl: map['sunAftCl'],
sunAftAct: map['sunAftAct'],
);
Map<String, dynamic> toMap() => {
'userName': userName,
'monMorOp': monMorOp,
'monMorCl': monMorCl,
'monMorAct': monMorAct,
'monAftOp': monAftOp,
'monAftCl': monAftCl,
'monAftAct': monAftAct,
'tueMorOp': tueMorOp,
'tueMorCl': tueMorCl,
'tueMorAct': tueMorAct,
'tueAftOp': tueAftOp,
'tueAftCl': tueAftCl,
'tueAftAct': tueAftAct,
'wedMorOp': wedMorOp,
'wedMorCl': wedMorCl,
'wedMorAct': tueMorAct,
'wedAftOp': wedAftOp,
'wedAftCl': wedAftCl,
'wedAftAct': wedAftAct,
'thuMorOp': thuMorOp,
'thuMorCl': thuMorCl,
'thuMorAct': thuMorAct,
'thuAftOp': thuAftOp,
'thuAftCl': thuAftCl,
'thuAftAct': thuAftAct,
'friMorOp': friMorOp,
'friMorCl': friMorCl,
'friMorAct': friMorAct,
'friAftOp': friAftOp,
'friAftCl': friAftCl,
'friAftAct': friAftAct,
'satMorOp': satMorOp,
'satMorCl': satMorCl,
'satMorAct': satMorAct,
'satAftOp': satAftOp,
'satAftCl': satAftCl,
'satAftAct': satAftAct,
'sunMorOp': sunMorOp,
'sunMorCl': sunMorCl,
'sunMorAct': sunMorAct,
'sunAftOp': sunAftOp,
'sunAftCl': sunAftCl,
'sunAftAct': sunAftAct
};
}
更新存储库方法以使用自动生成的密钥:
static const String openingTimeStorage = 'openingTimeStorage';
// storage reference
final _openingTimesFolder = intMapStoreFactory.store(openingTimeStorage);
// database instance
Future<Database> get _db async => await AppDatabase.instance.database;
Future saveOpeningTimes(OpeningTimes openingTimes) async {
print(
'saveOpeningTimes(): save opening times received ${openingTimes.toMap().toString()}'); // prints correct
final snapshot = await loadOpeningTimes();
print('saveOpeningTimes() snapshot is $snapshot');
if (snapshot == null) {
print('opening times are to save');
await _openingTimesFolder.add(await _db, openingTimes.toMap());
return;
} else {
print('opening times are to update');
await updateOpeningTimes(openingTimes);
}
}
Future updateOpeningTimes(OpeningTimes openingTimes) async {
print(
'updateOpeningTimes() : update opening time received ${openingTimes.toMap().toString()}'); // correct
final Finder finder = Finder(
filter: Filter.byKey(
_openingTimesFolder.record(openingTimes.id).get(await _db)));
await _openingTimesFolder.update(await _db, openingTimes.toMap(),
finder: finder);
}
Future<OpeningTimes> loadOpeningTimes() async {
final finder = Finder(sortOrders: [SortOrder('userName')]);
final snapshot =
await _openingTimesFolder.findFirst(await _db, finder: finder);
if (snapshot != null) {
print('loadOpeningTimes() snapshot is: $snapshot'); // correct
OpeningTimes openingTimes = OpeningTimes.fromMap(snapshot.value);
openingTimes.id = snapshot.key;
return openingTimes;
} else {
return null;
}
}
更新时我收到flutter: Erros is Invalid argument(s): Record key cannot be null。
【问题讨论】: