最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

flutter - Duplicate Note is being added in my stream - Stack Overflow

programmeradmin1浏览0评论

I am creating a note taking app. A not should be added when the screen is popped. The Note is being stored in Database only once but a duplicates are being added to stream when hotrestarted the duplicate automatically goes have been trying to solve this error for quite alot of time but to no benfite. I have provided two codes add Note Screen. Which is the screen through which I add notes. NotesService contains the logic of how I add code

Add Note Screen

    import 'package:flutter/material.dart';
    import 'package:google_fonts/google_fonts.dart';
    import 'package:mynotes/constants/colors/app_colors.dart';
    import 'package:mynotes/constants/widgets/helper_text.dart';
    import 'package:mynotes/services/auth/auth_services.dart';
    
    import 'dart:developer' as devtools show log;
    
    import 'package:mynotes/services/crud/notes_services.dart';
    import 'package:mynotes/utils/show_loading_dialog.dart';
    
    class AddNoteView extends StatefulWidget {
      const AddNoteView({super.key});
    
      @override
      State<AddNoteView> createState() => _AddNoteViewState();
    }
    
    class _AddNoteViewState extends State<AddNoteView> {
      DatabaseNote? _note;
      late final NotesService _notesService;
      late FocusNode _titleFocus;
      late FocusNode _textFocus;
      late TextEditingController _titleController;
      late TextEditingController _textController;
      late final Future<DatabaseNote> _noteFuture;
    
      void _deleteNoteIfTextIsEmpty() {
        final note = _note;
        if (note != null &&
            _textController.text.isEmpty &&
            _titleController.text.isEmpty) {
          _notesService.deleteNote(noteId: note.id);
          devtools.log('deleted Note because Empty');
        }
      }
    
      void _saveNoteIfTextIfTextIsNotEmpty() async {
        final note = _note;
    
        if (note != null && _textController.text.isNotEmpty ||
            _titleController.text.isNotEmpty) {
          final text = '${_titleController.text}&${_textController.text}';
          await _notesService.updateNote(note: note!, text: text);
        }
      }
    
      Future<DatabaseNote> createNewNote() async {
        final existingNote = _note;
        if (existingNote != null) {
          return existingNote;
        }
        final currentUser = AuthServices.firebase().currentUser!;
        final email = currentUser.email!;
        final owner = await _notesService.getUser(email: email);
        devtools.log("Created note: ${owner.id} Add Note View file");
        return await _notesService.createNote(
          owner: owner,
          text: '',
        );
      }
    
      @override
      void initState() {
        super.initState();
        _notesService = NotesService.singleton();
        _titleFocus = FocusNode();
        _textFocus = FocusNode();
        _titleController = TextEditingController();
        _textController = TextEditingController();
        _noteFuture = createNewNote();
    
        Future.delayed(Duration(milliseconds: 300), () {
          if (context.mounted) FocusScope.of(context).requestFocus(_titleFocus);
        });
      }
    
      @override
      void dispose() {
        _deleteNoteIfTextIsEmpty();
        _saveNoteIfTextIfTextIsNotEmpty();
        _titleFocus.dispose();
        _textFocus.dispose();
        _titleController.dispose();
        _textController.dispose();
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          backgroundColor: AppColors.softWhite,
          appBar: AppBar(
            backgroundColor: AppColors.softWhite,
            elevation: 0,
            scrolledUnderElevation: 0,
            centerTitle: false,
            automaticallyImplyLeading: false,
            title: Transform.translate(
              offset: Offset(-16, 0),
              child: Row(
                mainAxisAlignment: MainAxisAlignment.center,
                mainAxisSize: MainAxisSize.min,
                children: [
                  GestureDetector(
                    onTap: () => Navigator.of(context).pop(),
                    child: Stack(
                      children: [
                        Icon(
                          Icons.arrow_back_ios_new_rounded,
                          size: 26,
                          color: AppColors.jetBlack,
                        ),
                        Positioned(
                          left: 1,
                          child: Icon(
                            Icons.arrow_back_ios_new_rounded,
                            size: 26,
                            color: AppColors.jetBlack,
                          ),
                        ),
                      ],
                    ),
                  ),
                  HelperText(
                    text: 'Notes',
                    textColor: AppColors.jetBlack,
                    fontSize: 18,
                    fontWeight: FontWeight.w600,
                  ),
                ],
              ),
            ),
            actions: [
              IconButton(
                icon:
                    Icon(Icons.favorite_border_rounded, color: AppColors.jetBlack),
                onPressed: () {},
              ),
              IconButton(
                icon: Icon(Icons.save_alt_rounded, color: AppColors.jetBlack),
                onPressed: () {},
              ),
            ],
          ),
          body: FutureBuilder(
              future: _noteFuture,
              builder: (conext, snapshot) {
                switch (snapshot.connectionState) {
                  case ConnectionState.done:
                    _note = snapshot.data as DatabaseNote;
    
                    return Padding(
                      padding: const EdgeInsets.symmetric(horizontal: 15),
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          TextField(
                            controller: _titleController,
                            focusNode: _titleFocus,
                            textInputAction: TextInputAction.next,
                            keyboardType: TextInputType.multiline,
                            onSubmitted: (_) =>
                                FocusScope.of(context).requestFocus(_textFocus),
                            style: GoogleFonts.lobster(
                              fontSize: 34,
                              // Larger size for title
                              fontWeight: FontWeight.bold,
                              color: AppColors.jetBlack,
                            ),
                            decoration: InputDecoration(
                              hintText: "Title",
                              hintStyle: TextStyle(
                                fontSize: 34,
                                fontWeight: FontWeight.normal,
                                color:
                                    Colors.grey.shade400, // Light grey hint color
                              ),
                              border: InputBorder.none,
                            ),
                            minLines: 1,
                            maxLines: 2, // Limit to two lines like Apple's Notes
                          ),
                          // Spacing between title and body
                          Expanded(
                            child: TextField(
                              controller: _textController,
                              focusNode: _textFocus,
                              textInputAction: TextInputAction.newline,
                              style: TextStyle(
                                fontSize: 18,
                                color: AppColors.jetBlack,
                                height: 1.5, // Increase line height for readability
                              ),
                              decoration: InputDecoration(
                                hintText: "Write your note here...",
                                hintStyle: TextStyle(
                                  fontSize: 18,
                                  color:
                                      Colors.grey.shade400, // Light grey hint color
                                ),
                                border: InputBorder.none,
                              ),
                              minLines: 1,
                              maxLines: null,
                              keyboardType: TextInputType.multiline,
                            ),
                          ),
                        ],
                      ),
                    );
    
                  default:
                    return showLoadingDialog();
                }
              }),
        );
      }
    }

NotesService

    import 'dart:async';
    
    import 'package:flutter/foundation.dart';
    import 'package:mynotes/services/crud/crud_exceptions.dart';
    
    import 'package:sqflite/sqflite.dart';
    import 'package:path_provider/path_provider.dart';
    import 'package:path/path.dart' show join;
    import 'dart:developer' as devtools show log;
    
    class NotesService {
      Database? _db;
      List<DatabaseNote> _notes = [];
      late final StreamController<List<DatabaseNote>> _notesStreamController;
    
      //Here we are declaring a private constructor only
      NotesService._sharedInstance() {
        _notesStreamController = StreamController<List<DatabaseNote>>.broadcast(
            onListen: () => _notesStreamController.sink.add(_notes));
      }
      static final NotesService _shared = NotesService._sharedInstance();
      factory NotesService.singleton() => _shared;
    
      //broadCastStream Mean It can be listened more than once;
    
      Stream<List<DatabaseNote>> get allNotes => _notesStreamController.stream;
    
      Future<DatabaseUser> getOrCreateUser({required String email}) async {
        await ensureDbIsOpen();
    
        try {
          final user = await getUser(email: email);
          return user;
        } on CouldNotFindUser {
          final createdUser = await createUser(email: email);
          return createdUser;
        } catch (e) {
          rethrow;
        }
      }
    
      Future<void> _cacheNotes() async {
        final allNotes = await getAllNotes();
    
        _notes = allNotes.toList();
        devtools.log(_notes.toString());
        _notesStreamController.add(_notes);
      }
    
      Future<DatabaseNote> updateNote({
        required DatabaseNote note,
        required String text,
      }) async {
        final db = _getDatabaseOrThrow();
    
        //Id there is no note an exception will be thrown
        await getNote(id: note.id);
    
        final updateCount = await db.update(noteTable, {
          textColumn: text,
          isSyncedWithCloudColumn: 0,
        });
    
        if (updateCount == 0) {
          throw CouldNotOpenNote();
        } else {
          final updatedNote = await getNote(id: note.id);
          _notes.removeWhere(
            (element) => element.id == updatedNote.id,
          );
          devtools.log('Note is removed before updaing not shoulf  == ""');
          _notes.add(await getNote(id: note.id));
          devtools.log(
              'Before updating stream: ${_notes.length} updateNote NotesServices file');
          _notesStreamController.add(_notes);
          devtools.log(
              'After updating stream: ${_notes.length}Update Note Note Services file');
          return updatedNote;
        }
      }
    
      Future<List<DatabaseNote>> getAllNotes() async {
        await ensureDbIsOpen();
        final db = _getDatabaseOrThrow();
    
        final notes = await db.query(noteTable);
        devtools.log(notes.toString());
    
        return notes.map((note) => DatabaseNote.fromRow(note)).toList();
      }
    
      Future<DatabaseNote> getNote({
        required int id,
      }) async {
        await ensureDbIsOpen();
        final db = _getDatabaseOrThrow();
        final notes = await db.query(
          noteTable,
          where: 'id = ?',
          whereArgs: [id],
        );
        if (notes.isEmpty) {
          throw CouldNotFindNote();
        } else {
          final note = DatabaseNote.fromRow(notes.first);
          _notes.removeWhere((note) => note.id == id);
          _notes.add(note);
          _notesStreamController.add(_notes);
          return DatabaseNote.fromRow(notes.first);
        }
      }
    
      Future<int> deleteAllUsers() async {
        await ensureDbIsOpen();
        final db = _getDatabaseOrThrow();
        return await db.delete(userTable);
      }
    
      Future<int> deleteAllNotes({required int userId}) async {
        await ensureDbIsOpen();
        final db = _getDatabaseOrThrow();
        return await db.delete(noteTable);
      }
    
      Future<void> deleteNote({required int noteId}) async {
        await ensureDbIsOpen();
        final db = _getDatabaseOrThrow();
    
        final deletedCount = await db.delete(
          noteTable,
          where: 'id = ?',
          whereArgs: [noteId],
        );
    
        if (deletedCount == 0) {
          throw CouldNotDeleteNote();
        }
        _notes.removeWhere(
          (element) => element.id == noteId,
        );
        _notesStreamController.add(_notes);
      }
    
      Future<DatabaseNote> createNote({
        required DatabaseUser owner,
        required String text,
      }) async {
        await ensureDbIsOpen();
        final db = _getDatabaseOrThrow();
    
        final DatabaseUser dbUser = await getUser(email: owner.email);
    
        if (dbUser != owner) {
          throw CouldNotFindUser();
        }
    
        //make sure owner has the same id which is stored in the database;
    
        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,
        );
        devtools.log("Created note: ${owner.id} Notes Services file");
        _notes.add(note);
        devtools.log(
            'Before updating stream: ${_notes.length} createNoteFunction notesServices file');
        _notesStreamController.add(_notes);
        devtools.log(
            'After updating stream: ${_notes.length} create Note function Notes Services file');
    
        return note;
      }
    
      Future<DatabaseUser> getUser({required String email}) async {
        final db = _getDatabaseOrThrow();
        final user = await db.query(
          userTable,
          limit: 1,
          where: 'email = ?',
          whereArgs: [email.toLowerCase()],
        );
    
        if (user.isEmpty) {
          throw CouldNotFindUser();
        } else {
          return DatabaseUser.fromRow(user.first);
        }
      }
    
      Future<DatabaseUser> createUser({required String email}) async {
        await ensureDbIsOpen();
        final db = _getDatabaseOrThrow();
        final rusults = await db.query(
          userTable,
          limit: 1,
          where: 'email = ?',
          whereArgs: [email.toLowerCase()],
        );
    
        if (rusults.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 {
        await ensureDbIsOpen();
        final db = _getDatabaseOrThrow();
        final int 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) {
          throw DatabaseIsNotOpen();
        } else {
          await db.close();
          _db = null;
        }
      }
    
      //Insuring Database is not being opened again and again and also to open database automatically when using these functions. other eg delte user, create user,
      Future<void> ensureDbIsOpen() async {
        try {
          await open();
        } on DatabaseAlreadyOpenException {
          //
        }
      }
    
      //It will open and create database
      Future<void> open() async {
        if (_db != null) {
          throw DatabaseAlreadyOpenException();
        }
        try {
          final docsPath = await getApplicationDocumentsDirectory();
          final dbPath = join(docsPath.path, dbName);
          final db = await openDatabase(dbPath);
          _db = db;
          // create the user table
          await db.execute(createUserTable);
          // create note table
          await db.execute(createNoteTable);
          await _cacheNotes();
        } on MissingPlatformDirectoryException {
          throw UnableToGetDocumentsDirectory();
        }
      }
    }
    
    @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 NOT NULL DEFAULT 0,
            FOREIGN KEY("user_id") REFERENCES "user"("id"),
            PRIMARY KEY("id" AUTOINCREMENT)
          );''';

I am creating a note taking app. A not should be added when the screen is popped. The Note is being stored in Database only once but a duplicates are being added to stream when hotrestarted the duplicate automatically goes have been trying to solve this error for quite alot of time but to no benfite. I have provided two codes add Note Screen. Which is the screen through which I add notes. NotesService contains the logic of how I add code

Add Note Screen

    import 'package:flutter/material.dart';
    import 'package:google_fonts/google_fonts.dart';
    import 'package:mynotes/constants/colors/app_colors.dart';
    import 'package:mynotes/constants/widgets/helper_text.dart';
    import 'package:mynotes/services/auth/auth_services.dart';
    
    import 'dart:developer' as devtools show log;
    
    import 'package:mynotes/services/crud/notes_services.dart';
    import 'package:mynotes/utils/show_loading_dialog.dart';
    
    class AddNoteView extends StatefulWidget {
      const AddNoteView({super.key});
    
      @override
      State<AddNoteView> createState() => _AddNoteViewState();
    }
    
    class _AddNoteViewState extends State<AddNoteView> {
      DatabaseNote? _note;
      late final NotesService _notesService;
      late FocusNode _titleFocus;
      late FocusNode _textFocus;
      late TextEditingController _titleController;
      late TextEditingController _textController;
      late final Future<DatabaseNote> _noteFuture;
    
      void _deleteNoteIfTextIsEmpty() {
        final note = _note;
        if (note != null &&
            _textController.text.isEmpty &&
            _titleController.text.isEmpty) {
          _notesService.deleteNote(noteId: note.id);
          devtools.log('deleted Note because Empty');
        }
      }
    
      void _saveNoteIfTextIfTextIsNotEmpty() async {
        final note = _note;
    
        if (note != null && _textController.text.isNotEmpty ||
            _titleController.text.isNotEmpty) {
          final text = '${_titleController.text}&${_textController.text}';
          await _notesService.updateNote(note: note!, text: text);
        }
      }
    
      Future<DatabaseNote> createNewNote() async {
        final existingNote = _note;
        if (existingNote != null) {
          return existingNote;
        }
        final currentUser = AuthServices.firebase().currentUser!;
        final email = currentUser.email!;
        final owner = await _notesService.getUser(email: email);
        devtools.log("Created note: ${owner.id} Add Note View file");
        return await _notesService.createNote(
          owner: owner,
          text: '',
        );
      }
    
      @override
      void initState() {
        super.initState();
        _notesService = NotesService.singleton();
        _titleFocus = FocusNode();
        _textFocus = FocusNode();
        _titleController = TextEditingController();
        _textController = TextEditingController();
        _noteFuture = createNewNote();
    
        Future.delayed(Duration(milliseconds: 300), () {
          if (context.mounted) FocusScope.of(context).requestFocus(_titleFocus);
        });
      }
    
      @override
      void dispose() {
        _deleteNoteIfTextIsEmpty();
        _saveNoteIfTextIfTextIsNotEmpty();
        _titleFocus.dispose();
        _textFocus.dispose();
        _titleController.dispose();
        _textController.dispose();
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          backgroundColor: AppColors.softWhite,
          appBar: AppBar(
            backgroundColor: AppColors.softWhite,
            elevation: 0,
            scrolledUnderElevation: 0,
            centerTitle: false,
            automaticallyImplyLeading: false,
            title: Transform.translate(
              offset: Offset(-16, 0),
              child: Row(
                mainAxisAlignment: MainAxisAlignment.center,
                mainAxisSize: MainAxisSize.min,
                children: [
                  GestureDetector(
                    onTap: () => Navigator.of(context).pop(),
                    child: Stack(
                      children: [
                        Icon(
                          Icons.arrow_back_ios_new_rounded,
                          size: 26,
                          color: AppColors.jetBlack,
                        ),
                        Positioned(
                          left: 1,
                          child: Icon(
                            Icons.arrow_back_ios_new_rounded,
                            size: 26,
                            color: AppColors.jetBlack,
                          ),
                        ),
                      ],
                    ),
                  ),
                  HelperText(
                    text: 'Notes',
                    textColor: AppColors.jetBlack,
                    fontSize: 18,
                    fontWeight: FontWeight.w600,
                  ),
                ],
              ),
            ),
            actions: [
              IconButton(
                icon:
                    Icon(Icons.favorite_border_rounded, color: AppColors.jetBlack),
                onPressed: () {},
              ),
              IconButton(
                icon: Icon(Icons.save_alt_rounded, color: AppColors.jetBlack),
                onPressed: () {},
              ),
            ],
          ),
          body: FutureBuilder(
              future: _noteFuture,
              builder: (conext, snapshot) {
                switch (snapshot.connectionState) {
                  case ConnectionState.done:
                    _note = snapshot.data as DatabaseNote;
    
                    return Padding(
                      padding: const EdgeInsets.symmetric(horizontal: 15),
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          TextField(
                            controller: _titleController,
                            focusNode: _titleFocus,
                            textInputAction: TextInputAction.next,
                            keyboardType: TextInputType.multiline,
                            onSubmitted: (_) =>
                                FocusScope.of(context).requestFocus(_textFocus),
                            style: GoogleFonts.lobster(
                              fontSize: 34,
                              // Larger size for title
                              fontWeight: FontWeight.bold,
                              color: AppColors.jetBlack,
                            ),
                            decoration: InputDecoration(
                              hintText: "Title",
                              hintStyle: TextStyle(
                                fontSize: 34,
                                fontWeight: FontWeight.normal,
                                color:
                                    Colors.grey.shade400, // Light grey hint color
                              ),
                              border: InputBorder.none,
                            ),
                            minLines: 1,
                            maxLines: 2, // Limit to two lines like Apple's Notes
                          ),
                          // Spacing between title and body
                          Expanded(
                            child: TextField(
                              controller: _textController,
                              focusNode: _textFocus,
                              textInputAction: TextInputAction.newline,
                              style: TextStyle(
                                fontSize: 18,
                                color: AppColors.jetBlack,
                                height: 1.5, // Increase line height for readability
                              ),
                              decoration: InputDecoration(
                                hintText: "Write your note here...",
                                hintStyle: TextStyle(
                                  fontSize: 18,
                                  color:
                                      Colors.grey.shade400, // Light grey hint color
                                ),
                                border: InputBorder.none,
                              ),
                              minLines: 1,
                              maxLines: null,
                              keyboardType: TextInputType.multiline,
                            ),
                          ),
                        ],
                      ),
                    );
    
                  default:
                    return showLoadingDialog();
                }
              }),
        );
      }
    }

NotesService

    import 'dart:async';
    
    import 'package:flutter/foundation.dart';
    import 'package:mynotes/services/crud/crud_exceptions.dart';
    
    import 'package:sqflite/sqflite.dart';
    import 'package:path_provider/path_provider.dart';
    import 'package:path/path.dart' show join;
    import 'dart:developer' as devtools show log;
    
    class NotesService {
      Database? _db;
      List<DatabaseNote> _notes = [];
      late final StreamController<List<DatabaseNote>> _notesStreamController;
    
      //Here we are declaring a private constructor only
      NotesService._sharedInstance() {
        _notesStreamController = StreamController<List<DatabaseNote>>.broadcast(
            onListen: () => _notesStreamController.sink.add(_notes));
      }
      static final NotesService _shared = NotesService._sharedInstance();
      factory NotesService.singleton() => _shared;
    
      //broadCastStream Mean It can be listened more than once;
    
      Stream<List<DatabaseNote>> get allNotes => _notesStreamController.stream;
    
      Future<DatabaseUser> getOrCreateUser({required String email}) async {
        await ensureDbIsOpen();
    
        try {
          final user = await getUser(email: email);
          return user;
        } on CouldNotFindUser {
          final createdUser = await createUser(email: email);
          return createdUser;
        } catch (e) {
          rethrow;
        }
      }
    
      Future<void> _cacheNotes() async {
        final allNotes = await getAllNotes();
    
        _notes = allNotes.toList();
        devtools.log(_notes.toString());
        _notesStreamController.add(_notes);
      }
    
      Future<DatabaseNote> updateNote({
        required DatabaseNote note,
        required String text,
      }) async {
        final db = _getDatabaseOrThrow();
    
        //Id there is no note an exception will be thrown
        await getNote(id: note.id);
    
        final updateCount = await db.update(noteTable, {
          textColumn: text,
          isSyncedWithCloudColumn: 0,
        });
    
        if (updateCount == 0) {
          throw CouldNotOpenNote();
        } else {
          final updatedNote = await getNote(id: note.id);
          _notes.removeWhere(
            (element) => element.id == updatedNote.id,
          );
          devtools.log('Note is removed before updaing not shoulf  == ""');
          _notes.add(await getNote(id: note.id));
          devtools.log(
              'Before updating stream: ${_notes.length} updateNote NotesServices file');
          _notesStreamController.add(_notes);
          devtools.log(
              'After updating stream: ${_notes.length}Update Note Note Services file');
          return updatedNote;
        }
      }
    
      Future<List<DatabaseNote>> getAllNotes() async {
        await ensureDbIsOpen();
        final db = _getDatabaseOrThrow();
    
        final notes = await db.query(noteTable);
        devtools.log(notes.toString());
    
        return notes.map((note) => DatabaseNote.fromRow(note)).toList();
      }
    
      Future<DatabaseNote> getNote({
        required int id,
      }) async {
        await ensureDbIsOpen();
        final db = _getDatabaseOrThrow();
        final notes = await db.query(
          noteTable,
          where: 'id = ?',
          whereArgs: [id],
        );
        if (notes.isEmpty) {
          throw CouldNotFindNote();
        } else {
          final note = DatabaseNote.fromRow(notes.first);
          _notes.removeWhere((note) => note.id == id);
          _notes.add(note);
          _notesStreamController.add(_notes);
          return DatabaseNote.fromRow(notes.first);
        }
      }
    
      Future<int> deleteAllUsers() async {
        await ensureDbIsOpen();
        final db = _getDatabaseOrThrow();
        return await db.delete(userTable);
      }
    
      Future<int> deleteAllNotes({required int userId}) async {
        await ensureDbIsOpen();
        final db = _getDatabaseOrThrow();
        return await db.delete(noteTable);
      }
    
      Future<void> deleteNote({required int noteId}) async {
        await ensureDbIsOpen();
        final db = _getDatabaseOrThrow();
    
        final deletedCount = await db.delete(
          noteTable,
          where: 'id = ?',
          whereArgs: [noteId],
        );
    
        if (deletedCount == 0) {
          throw CouldNotDeleteNote();
        }
        _notes.removeWhere(
          (element) => element.id == noteId,
        );
        _notesStreamController.add(_notes);
      }
    
      Future<DatabaseNote> createNote({
        required DatabaseUser owner,
        required String text,
      }) async {
        await ensureDbIsOpen();
        final db = _getDatabaseOrThrow();
    
        final DatabaseUser dbUser = await getUser(email: owner.email);
    
        if (dbUser != owner) {
          throw CouldNotFindUser();
        }
    
        //make sure owner has the same id which is stored in the database;
    
        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,
        );
        devtools.log("Created note: ${owner.id} Notes Services file");
        _notes.add(note);
        devtools.log(
            'Before updating stream: ${_notes.length} createNoteFunction notesServices file');
        _notesStreamController.add(_notes);
        devtools.log(
            'After updating stream: ${_notes.length} create Note function Notes Services file');
    
        return note;
      }
    
      Future<DatabaseUser> getUser({required String email}) async {
        final db = _getDatabaseOrThrow();
        final user = await db.query(
          userTable,
          limit: 1,
          where: 'email = ?',
          whereArgs: [email.toLowerCase()],
        );
    
        if (user.isEmpty) {
          throw CouldNotFindUser();
        } else {
          return DatabaseUser.fromRow(user.first);
        }
      }
    
      Future<DatabaseUser> createUser({required String email}) async {
        await ensureDbIsOpen();
        final db = _getDatabaseOrThrow();
        final rusults = await db.query(
          userTable,
          limit: 1,
          where: 'email = ?',
          whereArgs: [email.toLowerCase()],
        );
    
        if (rusults.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 {
        await ensureDbIsOpen();
        final db = _getDatabaseOrThrow();
        final int 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) {
          throw DatabaseIsNotOpen();
        } else {
          await db.close();
          _db = null;
        }
      }
    
      //Insuring Database is not being opened again and again and also to open database automatically when using these functions. other eg delte user, create user,
      Future<void> ensureDbIsOpen() async {
        try {
          await open();
        } on DatabaseAlreadyOpenException {
          //
        }
      }
    
      //It will open and create database
      Future<void> open() async {
        if (_db != null) {
          throw DatabaseAlreadyOpenException();
        }
        try {
          final docsPath = await getApplicationDocumentsDirectory();
          final dbPath = join(docsPath.path, dbName);
          final db = await openDatabase(dbPath);
          _db = db;
          // create the user table
          await db.execute(createUserTable);
          // create note table
          await db.execute(createNoteTable);
          await _cacheNotes();
        } on MissingPlatformDirectoryException {
          throw UnableToGetDocumentsDirectory();
        }
      }
    }
    
    @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 NOT NULL DEFAULT 0,
            FOREIGN KEY("user_id") REFERENCES "user"("id"),
            PRIMARY KEY("id" AUTOINCREMENT)
          );''';

Share Improve this question edited 5 hours ago hab 426 bronze badges asked yesterday Mujtaba FarhanMujtaba Farhan 13 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 0

Hot restarts can lead to multiple active listeners or streams if they're not properly managed, resulting in duplicated data.

When you perform a hot restart, the Flutter framework destroys and recreates the widget tree but doesn't automatically dispose of existing streams or listeners. This behavior can cause multiple streams or listeners to remain active, leading to duplicated data in your application.

u can read more about this issue #33678

发布评论

评论列表(0)

  1. 暂无评论