这是 NotesView:
import 'package:app_two/lib/constants/routes.dart';
import 'package:app_two/lib/enums/menu_action.dart';
import 'package:app_two/lib/services/auth/auth_service.dart';
import 'package:app_two/lib/services/auth/crud/notes_service.dart';
import 'package:flutter/material.dart';
class NotesView extends StatefulWidget {
const NotesView({super.key});
@override
State<NotesView> createState() => _NotesViewState();
}
class _NotesViewState extends State<NotesView> {
late Future _myFuture;
late final NotesService _notesService;
String get userEmail => AuthService.firebase().currentUser!.email!;
@override
void initState() {
super.initState();
_notesService = NotesService();
_myFuture = getGet();
}
// @override //delete
// void dispose() {
// _notesService.close();
// super.dispose();
// }
getGet() async {
return await _notesService.getOrCreateUser(email: userEmail);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Your Notes'),
actions: [
IconButton(
onPressed: () {
Navigator.of(context).pushNamed(newNoteRoute);
},
icon: const Icon(Icons.add),
),
PopupMenuButton<MenuAction>(
onSelected: (value) async {
switch (value) {
case MenuAction.logout:
final shouldLogout = await showLogOutDialog(context);
if (shouldLogout) {
await AuthService.firebase().logOut();
Navigator.of(context).pushNamedAndRemoveUntil(
loginRoute,
(_) => false,
);
}
}
},
itemBuilder: (context) {
return const [
PopupMenuItem<MenuAction>(
value: MenuAction.logout,
child: Text('Logout'),
),
];
},
)
],
),
body: FutureBuilder(
future: _myFuture,
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.done:
return StreamBuilder(
stream: _notesService.allNotes,
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
case ConnectionState.active:
if (snapshot.hasData) {
final allNotes = snapshot.data as List<DatabaseNote>;
return ListView.builder(
itemCount: allNotes.length,
itemBuilder: (context, index) {
final note = allNotes[index];
return ListTile(
title: Text(
note.text,
maxLines: 1,
softWrap: true,
overflow: TextOverflow.ellipsis,
),
);
},
);
} else {
return const CircularProgressIndicator();
}
default:
return const CircularProgressIndicator();
}
},
);
default:
return const CircularProgressIndicator();
}
},
),
);
}
Future<bool> showLogOutDialog(BuildContext context) {
return showDialog<bool>(
context: context,
builder: (context) {
return AlertDialog(
title: const Text('Sign out'),
content: const Text('Are you sure you want to sign out?'),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop(false);
},
child: const Text('Cancel')),
TextButton(
onPressed: () {
Navigator.of(context).pop(true);
},
child: const Text('Logout'))
],
);
},
).then((value) => value ?? false);
}
}
//Notes_service
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart' show join;
import 'crud_exceptions.dart';
class NotesService {
Database? _db;
List<DatabaseNote> _notes = [];
static final NotesService _shared = NotesService._sharedInstance();
NotesService._sharedInstance() {
_notesStreamController = StreamController<List<DatabaseNote>>.broadcast(
onListen: () {
_notesStreamController.sink.add(_notes);
//populate stream with a stream of notes that we've already read from the database
},
);
}
factory NotesService() => _shared; //singleton
late final StreamController<List<DatabaseNote>> _notesStreamController;
Stream<List<DatabaseNote>> get allNotes => _notesStreamController.stream;
Future<DatabaseUser?> getOrCreateUser({required String email}) async {
try {
final user = await getUser(email: email);
return user;
} on CouldNotFindUser {
final createdUser = await createUser(email: email);
return createdUser;
} on DatabaseIsNotOpen {
open();
} catch (e) {
print(e.toString());
rethrow;
}
}
Future<void> _cacheNotes() async {
final allNotes = await getAllNotes();
_notes = allNotes.toList();
_notesStreamController.add(_notes);
}
Future<DatabaseNote> updateNote({
required DatabaseNote note,
required String text,
}) async {
await _ensureDbIsOpen();
final db = _getDatabaseorThrow();
//make sure note exists
await getNote(id: note.id);
//update db
final updatesCount = await db.update(noteTable, {
textColumn: text,
isSyncedwithCloudColumn: 0,
});
if (updatesCount == 0) {
throw CouldNotUpdateNote();
} else {
final updatedNote = await getNote(id: note.id);
_notes.removeWhere((note) => note.id == updatedNote.id);
_notes.add(updatedNote);
_notesStreamController.add(_notes);
return updatedNote;
}
}
Future<Iterable<DatabaseNote>> getAllNotes() async {
await _ensureDbIsOpen();
final db = _getDatabaseorThrow();
final notes = await db.query(noteTable);
return notes.map((noteRow) => DatabaseNote.fromRow(noteRow));
}
Future<DatabaseNote> getNote({required int id}) async {
await _ensureDbIsOpen();
final db = _getDatabaseorThrow();
final notes = await db.query(
noteTable,
limit: 1,
where: 'id = ?',
whereArgs: [id],
);
if (notes.isEmpty) {
throw CouldNotFindNote();
} else {
final note = DatabaseNote.fromRow(notes.first);
_notes.removeWhere((note) => note.id == id);
_notesStreamController.add(_notes);
return note;
}
}
Future<int> deleteAllNotes() async {
await _ensureDbIsOpen();
final db = _getDatabaseorThrow();
final numberOfdeletions = await db.delete(noteTable);
_notes = [];
_notesStreamController.add(_notes);
return numberOfdeletions;
}
Future<void> deleteNote({required int id}) async {
await _ensureDbIsOpen();
final db = _getDatabaseorThrow();
final deletedCount = await db.delete(
noteTable,
where: 'id = ?',
whereArgs: [id],
);
if (deletedCount == 0) {
throw CouldNotDeleteNote();
} else {
_notes.removeWhere((note) => note.id == id);
_notesStreamController.add(_notes);
}
}
Future<DatabaseNote> createNote({required DatabaseUser owner}) async {
await _ensureDbIsOpen();
final db = _getDatabaseorThrow();
//Make sure owner exists in database withthe correct id
final dbUser = await getUser(email: owner.email);
if (dbUser != owner) {
throw CouldNotFindUser();
}
const text = '';
//create the note
final noteId = await db.insert(noteTable, {
userIdColumn: owner.id,
textColumn: text,
isSyncedwithCloudColumn: 1,
});
final note = DatabaseNote(
id: noteId,
userId: owner.id,
text: text,
isSyncedWithCloud: true,
);
_notes.add(note);
_notesStreamController.add(_notes);
return note;
}
Future<DatabaseUser> getUser({required String email}) async {
await _ensureDbIsOpen();
final db = _getDatabaseorThrow();
final results = await db.query(
userTable,
limit: 1,
where: 'email = ?',
whereArgs: [email.toLowerCase()],
);
if (results.isEmpty) {
throw CouldNotFindUser();
} else {
return DatabaseUser.fromRow(results.first);
}
}
Future<DatabaseUser> createUser({required String email}) async {
await _ensureDbIsOpen();
final db = _getDatabaseorThrow();
final results = await db.query(
userTable,
limit: 1,
where: 'email = ?',
whereArgs: [email.toLowerCase()],
);
if (results.isNotEmpty) {
throw UserAlreadyExists();
}
final userId = await db.insert(userTable, {
emailColumn: email.toLowerCase(),
});
return DatabaseUser(id: userId, email: email);
}
Future<void> deleteUser({required String email}) async {
final db = _getDatabaseorThrow();
final deletedCount = await db.delete(
userTable,
where: 'email = ?',
whereArgs: [email.toLowerCase()],
);
if (deletedCount != 1) {
throw CouldNotDeleteUser();
}
}
Database _getDatabaseorThrow() {
final db = _db;
if (db == null) {
throw DatabaseIsNotOpen();
} else {
return db;
}
}
Future<void> close() async {
final db = _db;
if (db == null) {
try {
await open();
} on DatabaseIsNotOpen {
open();
}
// throw DatabaseIsNotOpen();
} else {
await db.close();
_db = null;
}
}
Future<void> _ensureDbIsOpen() async {
if (_db != null) {
try {
await open();
} on DatabaseAlreadyOpenException {
//empty
}
}
}
Future<void> open() async {
if (_db != null) {
throw DatabaseAlreadyOpenException();
}
try {
await _ensureDbIsOpen();
final docsPath = await getApplicationDocumentsDirectory();
final dbPath = join(docsPath.path, dbName);
final db = await openDatabase(dbPath);
_db = db;
//create user table
await db.execute(createUserTable);
//create note table
await db.execute(createNoteTable);
await _cacheNotes();
} on MissingPlatformDirectoryException {
throw UnableToGetDocumentDirectory();
}
}
}
@immutable
class DatabaseUser {
final int id;
final String email;
const DatabaseUser({
required this.id,
required this.email,
});
DatabaseUser.fromRow(Map<String, Object?> map)
: id = map[idColumn] as int,
email = map[emailColumn] as String;
@override
String toString() => 'Person, ID = $id, email= $email';
@override
bool operator ==(covariant DatabaseUser other) => id == other.id;
@override
int get hashCode => id.hashCode;
}
class DatabaseNote {
final int id;
final int userId;
final String text;
final bool isSyncedWithCloud;
DatabaseNote({
required this.id,
required this.userId,
required this.text,
required this.isSyncedWithCloud,
});
DatabaseNote.fromRow(Map<String, Object?> map)
: id = map[idColumn] as int,
userId = map[userIdColumn] as int,
text = map[textColumn] as String,
isSyncedWithCloud =
(map[isSyncedwithCloudColumn] as int) == 1 ? true : false;
@override
String toString() =>
'Note, ID = $id, userId = $userId, isSyncedWithCloud = $isSyncedWithCloud, text = $text';
@override
bool operator ==(covariant DatabaseNote other) => id == other.id;
@override
int get hashCode => id.hashCode;
}
const dbName = 'notes.db';
const noteTable = 'note';
const userTable = 'user';
const idColumn = 'id';
const emailColumn = 'email';
const userIdColumn = 'user_id';
const textColumn = 'text';
const isSyncedwithCloudColumn = 'is_synced_with_cloud';
const createUserTable = '''CREATE TABLE IF NOT EXISTS "user" (
"id" INTEGER NOT NULL,
"email" TEXT NOT NULL UNIQUE,
PRIMARY KEY("id" AUTOINCREMENT)
);''';
const createNoteTable = '''CREATE TABLE IF NOT EXISTS "note" (
"id" INTEGER NOT NULL,
"user_id" INTEGER NOT NULL,
"text" TEXT,
"is_synced_with_cloud" INTEGER DEFAULT 0,
FOREIGN KEY("user_id") REFERENCES "user"("id"),
PRIMARY KEY("id" AUTOINCREMENT)
);''';
我正在制作一个笔记应用程序,NotesView 会清除所有其他笔记并将其替换为我最近没有的笔记。我不知道为什么会这样
您输入的每个注释都应显示在图块上。但是却发生了这种情况: 但只有在我重新启动应用程序之后。在下图中,我添加了新注释,然后重新启动应用程序
我相信我在 StreamBuilder 中做了一些非常重要的事情,但不知道是什么。
如果有任何帮助,我将不胜感激。
尝试将
case ConnectionState.active
中的 case ConnectionState.done
更改为 StreamBuilder
。
由于在
case ConnectionState.active
中构建列表,可能会发生重复。
我不知道你的 db.update 做了什么,也不知道你的 SQL 查询是什么样的,但如果你不传递 id,你应该如何更新注释?
Future<DatabaseNote> updateNote({
required DatabaseNote note,
required String text,
}) async {
await _ensureDbIsOpen();
final db = _getDatabaseorThrow();
//make sure note exists
await getNote(id: note.id);
//update db
final updatesCount = await db.update(noteTable, { ///how does magically know what note to update?
textColumn: text,
isSyncedwithCloudColumn: 0,
});
if (updatesCount == 0) {
throw CouldNotUpdateNote();
} else {
final updatedNote = await getNote(id: note.id);
_notes.removeWhere((note) => note.id == updatedNote.id);
_notes.add(updatedNote);
_notesStreamController.add(_notes);
return updatedNote;
}
}
如果您使用 INSERT OR REPLACE INTO 因为您没有传递 id,那么您将创建一个新行而不是更新它,并且您的缓存列表会向您提供错误信息,因为最后您认为您正确更新了它并且只是用相同的 id 替换注释,您的缓存列表不再反映真实信息。
如果您因为 where 子句没有 id 而更新表中的所有注释,那么您将在重新启动时出现当前问题
检查你的数据库层,看看你是否真的在做你想做的事。
您需要更改 db.update 调用,以便它有一个指定注释 id 的 where 子句:
db.更新( 注释表, {textColumn:文本}, 其中:'id = ?', whereArgs: [note.id], );
我也在做同样的课程,并停留在第32章(在笔记视图中显示笔记)。你完成了课程吗?我需要一些指导。