I have a user box where I store some data about the user.
In the main.dart file, I am registering and opening the UserDBAdapter
void main() async {
await Hive.initFlutter();
Hive.registerAdapter(UserDBAdapter());
await Hive.openBox<UserDB>('users');
return runApp(
MyApp(),
);
}
In my Bloc, I have a function which logins the user and then saves true in the isLoggedIn field of the user
class UserBloc extends Bloc<UserEvent, UserState> {
UserDB user = UserDB();
final usersBox = Hive.box<UserDB>('users');
UserRepository userRepository;
UserBloc(this.userRepository) : super(UserInitial()) {
createUser();
verifyUser();
loginUser();
logoutUser();
usersBox.put('user', user);
}
Future<void> loginUser() async {
return on<LoginUser>((event, emit) async {
emit(UserLoading());
await Future.delayed(const Duration(milliseconds: 500));
try {
final result = await userRepository.login(
event.email,
event.password,
);
if (result['success'] == false) {
emit(UserError(result['message']));
} else {
emit(
UserLoggedIn(),
);
user.isLoggedIn = true; // I set it to true here
user.token = result['token'];
user.save(); // Then I save it here. which means the new changes is persisted in the local storage
}
} catch (error) {
emit(
UserError(
error.toString(),
),
);
}
});
}
But my point is that, when I try hot reloading the app, it sends me to the login screen instead of the HomeScreen()
#override
void initState() {
if (Hive.box<UserDB>('users').get('user') == null) {
print('empty'); // This does not print, that means I have a user in the local storage
Hive.box<UserDB>('users').put(
'user',
UserDB(isVerified: false, isLoggedIn: false),
);
}
super.initState();
}
#override
Widget build(BuildContext context) {
final userDB = Hive.box<UserDB>('users').get('user')!;
print(userDB.isLoggedIn); // Here prints false instead of true
if (userDB.isLoggedIn) {
return const NavigationScreen();
} else if (userDB.isVerified && !userDB.isLoggedIn) {
return AnimatedSplashScreen(
splash: const SplashScreen(),
duration: 4000,
nextScreen: const LoginSignUpSwitch(),
);
} else {
return AnimatedSplashScreen(
splash: const SplashScreen(),
duration: 4000,
nextScreen: const WelcomeScreen(),
);
}
}
Below is the UserDB class
import 'package:hive/hive.dart';
part 'user.g.dart';
#HiveType(typeId: 1)
class UserDB extends HiveObject {
UserDB({
this.name,
this.email,
this.isVerified = false,
this.isLoggedIn = false,
this.token,
});
// NAME (0)
#HiveField(0)
String? name;
//EMAIL(1)
#HiveField(1)
String? email;
//IS-VERIFIED(2)
#HiveField(2, defaultValue: false)
bool isVerified;
//IS-LOGGEDIN(3)
#HiveField(3, defaultValue: false)
bool isLoggedIn;
//TOKEN(4)
#HiveField(4)
String? token;
}
NB: I am printing the value of the isLoggedIn in the HomeScreen and it prints true
final userDB = Hive.box<UserDB>('users').get('user');
print(userDB!.isLoggedIn); // Prints true here
Related
I have a simple controller like this
class UserController with ChangeNotifier {
UserData user = UserData();
UserData get userdata => user;
void setUser(UserData user) {
user = user;
print(user.sId);
notifyListeners();
}
login(data) async {
var response = await ApiService().login(data);
final databody = json.decode(response);
if (databody['success']) {
UserData authUser = UserData.fromJson(databody['data']);
setUser(authUser);
notifyListeners();
return true;
} else {
return false;
}
}
}
I am trying to just print it like this on both widget and in initstate function but values are showing null. I can see in set function value is not null.
print('id ${context.watch<UserController>().user.sId.toString()}');
print(
'id2 ${Provider.of<UserController>(context, listen: false).user.sId.toString()}');
I already have added
ChangeNotifierProvider(create: (_) => UserController()),
],
in main.dart in MultiProvider
Also on Tap of login button I am doing this
showLoader(context);
UserController auth = Provider.of<UserController>(
context,
listen: false);
var data = {
"userEmail":
emailController.text.trim().toLowerCase(),
"userPassword": passwordController.text.trim(),
};
auth.login(data).then((v) {
if (v) {
hideLoader(context);
context.go('/homeroot');
} else {
hideLoader(context);
Fluttertoast.showToast(
backgroundColor: green,
textColor: Colors.white,
msg:
'Please enter correct email and password');
}
});
Try to include this while naming is same,
void setUser(UserData user) {
this.user = user;
print(user.sId);
notifyListeners();
}
Follow this structure
class UserController with ChangeNotifier {
UserData user = UserData();
UserData get userdata => user;
void setUser(UserData user) {
this.user = user;
print(user.sId);
notifyListeners();
}
Future<bool> login(String data) async {
await Future.delayed(Duration(seconds: 1));
UserData authUser = UserData(sId: data);
setUser(authUser);
notifyListeners();
return true;
}
}
class HPTest extends StatelessWidget {
const HPTest({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
body: Consumer<UserController>(
builder: (context, value, child) {
return Text(value.user.sId);
},
),
floatingActionButton: FloatingActionButton(onPressed: () async {
final result = await Provider.of<UserController>(context, listen: false)
.login("new ID");
print("login $result");
;
}),
);
}
}
when I try to login it doesn't redirect me to my authenticated page(the state doesn't update) but it stays as it is, it redirects me only after I hot restart the app. I don't know what is wrong with my code.I control the user =null in landing page but landingPage cant return home page.
this is Landing Page
import '/material.dart';
import '/provider.dart';
import '/login-directory/home_page.dart';
import '/login-directory/sign_in_page.dart';
import '/model/user_model.dart' as usr;
import '/viewmodel/viewusermodel.dart';
class LandingPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
final _userModel = Provider.of<UserModel>(context,listen: false);
if (_userModel.state == ViewState.Idle) {
if (_userModel.user == null) {
return SignInPage();//user==null
} else {
return HomePage(user: _userModel.user as usr.User);
}
}
else {
return Scaffold(
body: Center(child: CircularProgressIndicator(),),
);
}
}
}
this is User Repository:
import '/locator.dart';
import '/model/user_model.dart';
import '/services/auth_base.dart';
import '/services/fake_auth_service.dart';
import '/services/firebase_auth_service.dart';
enum AppMode{DEBUG,RELEASE}
class UserRepository implements AuthBase {
FirebaseAuthService _firebaseAuthService = locator<FirebaseAuthService>();
final FakeAuthService _fakeAuthService = locator<FakeAuthService>();
AppMode appMode = AppMode.RELEASE;
#override
Future<User?> currentUser() async {
if (appMode == AppMode.DEBUG) {
return await _fakeAuthService.currentUser();
}
else {
return await _firebaseAuthService.currentUser();
}
}
#override
Future<bool> logOut() async {
if (appMode == AppMode.DEBUG) {
return await _fakeAuthService.logOut();
}
else {
return await _firebaseAuthService.logOut();
}
}
#override
Future<User?> singInAnonymously() async {
if (appMode == AppMode.DEBUG) {
return await _fakeAuthService.singInAnonymously();
}
else {
return await _firebaseAuthService.singInAnonymously();
}
}
#override
Future<User?> signInWithGoogle() async {
if (appMode == AppMode.DEBUG) {
return await _fakeAuthService.singInAnonymously();
}
else {
return await _firebaseAuthService.signInWithGoogle();
}
}
}
Authbase :
abstract class AuthBase{
Future<User?> currentUser();
Future<User?> singInAnonymously();
Future<bool> logOut();
Future<User?> signInWithGoogle();
}
ViewUserModel:
import 'package:flutter/material.dart';
import 'package:/locator.dart';
import 'package:/model/user_model.dart' as usr;
import 'package:/repository/user_repository.dart';
import 'package:/services/auth_base.dart';
enum ViewState{Idle,Busy}
class UserModel with ChangeNotifier implements AuthBase {
ViewState _state = ViewState.Idle;
final UserRepository _userRepository = locator<UserRepository>();
usr.User? _user;
usr.User? get user =>_user;
ViewState get state => _state;
set state(ViewState value) {
_state = value;
notifyListeners(); //
}
UserModel(){
currentUser();
}
#override
Future<usr.User?> currentUser() async {
try {
state = ViewState
.Busy; //kullanıcı verisi cekilirken ekran verisini guncelliyorum
_user = (await _userRepository.currentUser())!;
return _user;
} catch (e) {
debugPrint("ViewModel currentUser is error $e" );
return null;
} finally {
state = ViewState.Idle;
}
}
#override
Future<bool> logOut() async {
try {
state = ViewState.Busy;
bool result =await _userRepository.logOut();
_user= null;
return result;
} catch (e) {
debugPrint("ViewModel currentUser is error $e" );
return false;
} finally {
state = ViewState.Idle;
}
}
#override
Future<usr.User?> singInAnonymously() async {
try {
state = ViewState
.Busy;
_user = (await _userRepository.singInAnonymously())!;
return _user;
} catch (e) {
debugPrint("ViewModel currentUser is error $e");
return null;
} finally {
state = ViewState.Idle;
}
}
#override
Future<usr.User?> signInWithGoogle() async {
try {
state = ViewState
.Busy;
_user = (await _userRepository.signInWithGoogle())!;
return _user;
} catch (e) {
debugPrint("ViewModel currentUser is error $e");
return null;
} finally {
state = ViewState.Idle;
}
}
}
GetIt locator:
GetIt locator=GetIt.instance;
void setupLocator(){
locator.registerLazySingleton(() => FirebaseAuthService());
locator.registerLazySingleton(() => UserRepository());
}
this is main.dart:
import '/viewmodel/viewusermodel.dart';
void main() async{
WidgetsFlutterBinding.ensureInitialized();;
await Firebase.initializeApp();
setupLocator();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title:'Title',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch:Colors.purple,
),
home:ChangeNotifierProvider(
create:(context)=>UserModel(),
child: LandingPage()),
);
}
}
This is the 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)
);''';
I'm making a note app and the NotesView clears all other notes and replaces it with my most recent not. I have no idea why this is happening
Each note you input should display on a tile. But instead this happens:
But only after I restart the app. In the next images, I add a new note and then restart the application
I believe I did something very in my StreamBuilder but don't know what.
I'd appreciate any assistance.
Try changing the case ConnectionState.active to case ConnectionState.done in your StreamBuilder.
Duplication may be occurring due to building your list inside case ConnectionState.active.
I don't know what your db.update does or your SQL query looks like but if you don't pass the id how are you supposed to update the note?
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;
}
}
If you're using INSERT OR REPLACE INTO because you don't pass the id you're creating a new row instead of updating it and your cache list is giving you false information because at the end you think you update it correctly and just replace the note with the same id, your cache list doesn't reflect the real information anymore.
If you're updating all notes in your table because the where clause doesn't have an id you will present your current problem when restarting
Check your db layer apart to see if you're actually doing what you want.
I'm beginner in Flutter, Riverpod for the state management and firebase for the authentication.
I'm looking to retrieve the logged user's email to pass to my postgres database and retrieve all the user information. In a first time, I just try to display the nickname of the current user. I am facing a problem when I log out of the app to log back in. The auth providers are not updated so I get informations from the very first connected user. For example currentUserEmailProvider still get the first connected user email. Any help is welcome, I'm really stuck.
My auth_repository.dart:
class AuthRepository {
const AuthRepository(this._auth);
final FirebaseAuth _auth;
Stream<User?> get authStateChange => _auth.idTokenChanges();
Stream<User?>get authUserChange => _auth.userChanges();
String? get currentUserEmail => _auth.currentUser?.email;
Future<User?> signInWithEmailAndPassword(
String email, String password) async {
_auth.currentUser?.reload();
try {
final result = await _auth.signInWithEmailAndPassword(
email: email,
password: password,
);
return result.user;
} on FirebaseAuthException catch (e) {
if (e.code == 'user-not-found') {
throw const ErrorHandler(message: 'User not found');
} else if (e.code == 'wrong-password') {
throw const ErrorHandler(message : 'Wrong password');
} else {
throw const ErrorHandler(message: 'An error occurred. Please try again later');
}
}
}
Future<AppUser?> registerWithEmailAndPassword(String email, String password, String nickname, String role, String firstname, String lastname) async {
// Sans ces deux lignes la création d'un nouveau compte entraîne un login automatique sur ce compte
FirebaseApp app = await Firebase.initializeApp(
name: 'Secondary', options: Firebase.app().options);
try {
AppUser? appUser = await UserRepository(email).saveUser(email, nickname, role, firstname, lastname);
if(appUser != null) {
try {
UserCredential result =
await FirebaseAuth.instanceFor(app: app).createUserWithEmailAndPassword(email: email, password: password);
User? user = result.user;
if(user == null) {
throw Exception("user from firebase not found");
}
return appUser;
} on FirebaseException catch(e) {
await UserRepository(email).deleteUser(email);
print(ErrorHandler(message: e.code.toString()));
}
} else {
throw Exception("user from postgres database not found");
}
} on PostgreSQLException catch(e) {
print(ErrorHandler(message: e.code.toString()));
}
return null;
}
Future<void> signOut() async {
await _auth.signOut();
}
}
My user_repository.dart:
class UserRepository {
final String email;
PostgreSQLConnection? connection;
UserRepository(this.email){
connection = (connection == null || connection!.isClosed == true
? PostgreSQLConnection(
'10.0.2.2', 5432, DatabaseAccess.databaseName,
queryTimeoutInSeconds: 3600,
timeoutInSeconds: 3600,
username: DatabaseAccess.databaseUser,
password: DatabaseAccess.databasePassword) : connection);
}
}
Future<AppUser?> getCurrentUser(String? currentEmail) async {
print(currentEmail);
try {
await connection!.open();
final result = await connection!.mappedResultsQuery(
'select * from public.user where email = #emailValue',
substitutionValues: {
'emailValue': currentEmail,
},
allowReuse: true,
timeoutInSeconds: 30,
);
final userFromDataBase = result[0]['user']!;
return AppUser(
email: userFromDataBase['email'],
nickname: userFromDataBase['nickname'],
role: userFromDataBase['role'],
firstname: userFromDataBase['firstname'],
lastname: userFromDataBase['lastname'],
);
} on PostgreSQLException catch(e) {
print(ErrorHandler(message: e.toString()));
return null;
}
}
}
My providers.dart:
final authRepositoryProvider = Provider<AuthRepository>((ref) {
return AuthRepository(FirebaseAuth.instance);
});
final authStateProvider = StreamProvider<User?>((ref) {
return ref.read(authRepositoryProvider).authStateChange;
});
final currentUserEmailProvider = Provider<String?>((ref) {
return AuthRepository(FirebaseAuth.instance).currentUserEmail;
});
final userRepositoryProvider = Provider.autoDispose<UserRepository>((ref) {
return UserRepository(ref.read(currentUserEmailProvider)!);
});
final futureCurrentUserProvider = Provider<Future<AppUser?>>((ref) {
return ref.read(userRepositoryProvider).getCurrentUser(ref.read(currentUserEmailProvider));
});
final currentUserProvider = FutureProvider.autoDispose<AppUser?>((ref) => ref.read(userRepositoryProvider).getCurrentUser(ref.read(currentUserEmailProvider)));
My home_screen.dart:
class HomeScreen extends HookConsumerWidget {
const HomeScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context, WidgetRef ref) {
final currentUser = ref.watch(currentUserProvider);
return Scaffold(
body: currentUser.when(
data: (user) => _buildBody(context, user, ref),
loading: () => const Center(child: CircularProgressIndicator()),
error: (error, _) => _errorBody(context, ref),
)
);
}
Widget _buildBody(BuildContext context, AppUser? user, WidgetRef ref) {
if(user == null) {
return _errorBody(context, ref);
} else {
return Center(child: Text(
'Welcome ${user.getNickname}',
style: const TextStyle(fontSize: 20),
));
}
}
Widget _errorBody(BuildContext context, WidgetRef ref) {
return const Center(child: Text(
"Error: No user found",
style: TextStyle(fontSize: 20, color: Colors.red),
));
}
}
Try changing
final currentUserProvider = FutureProvider.autoDispose<AppUser?>((ref) => ref.read(userRepositoryProvider).getCurrentUser(ref.read(currentUserEmailProvider)));
to
final currentUserProvider = FutureProvider.autoDispose<AppUser?>((ref) => ref.read(userRepositoryProvider).getCurrentUser(ref.watch(currentUserEmailProvider)));
and changing
final currentUserEmailProvider = Provider<String?>((ref) {
return AuthRepository(FirebaseAuth.instance).currentUserEmail;
});
to
final currentUserEmailProvider = Provider<String?>((ref) {
return ref.read(authRepositoryProvider).currentUserEmail;
});
Ok I resolved my problem with this following. To be simplier I removed the singleton in my user_repository.dart. The problem was from my providers and I found a way to use the authUserChanges() method :
providers.dart :
final futureCurrentUserProvider = Provider<Future<AppUser?>>((ref) {
return UserRepository().getCurrentUser(ref.watch(emailChangeProvider));
});
final currentUserProvider = FutureProvider.autoDispose<AppUser?>((ref) => UserRepository().getCurrentUser(ref.watch(emailChangeProvider)));
final authChangeProvider = StreamProvider<User?>((ref) {
return ref.read(authRepositoryProvider).authUserChange;
});
final emailChangeProvider = Provider<String?>((ref) {
return ref.watch(authChangeProvider).value?.email;
});
Hello im trying to mock firebase messaging to get token but when i try to test i get some error,can someone help me to solve this error. This error occur only in testing and not in my emulator or mobile phone. Here is my setupFirebaseAuthMocks. Thank you
my test
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp();
}
void main() {
setupFirebaseAuthMocks();
late ProviderContainer container;
group('AuthenticationControllerTest -', () {
setUpAll(() async {
await Firebase.initializeApp();
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
registerThirdPartyServices();
});
tearDown(() {
unregisterThirdPartyServices();
//container.dispose();
});
});
Error
MissingPluginException(No implementation found for method Messaging#getToken on channel plugins.flutter.io/firebase_messaging)
here is the method im trying to call
Future<Result<Failure, bool>> registerUserFirebaseToken() async {
try {
log.i('Registering Firebase');
final fireBaseMessaging = FirebaseMessaging.instance;
final token = await fireBaseMessaging.getToken();
log.v('Firebase token: $token');
await api.post(
link: '${env.getValue(kAuthUrl)}users/auth/firebase',
body: {'token': token},
hasHeader: true,
);
return const Success(true);
} catch (e) {
return Error(Failure(message: 'Firebase registration went wrong, Please try again!', content: e.toString()));
}
}
For those having the same issue, there is an example of a Mock on the official firebase messaging Github
Depending on your Mockito's version, you may have to update this code a little bit.
Here is the Mock file I'm using with Mockito v5.3.2
// ignore_for_file: require_trailing_commas
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_core_platform_interface/firebase_core_platform_interface.dart';
import 'package:firebase_messaging_platform_interface/firebase_messaging_platform_interface.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
typedef Callback = Function(MethodCall call);
final MockFirebaseMessaging kMockMessagingPlatform = MockFirebaseMessaging();
Future<T> neverEndingFuture<T>() async {
// ignore: literal_only_boolean_expressions
while (true) {
await Future.delayed(const Duration(minutes: 5));
}
}
void setupFirebaseMessagingMocks() {
TestWidgetsFlutterBinding.ensureInitialized();
setupFirebaseCoreMocks();
// Mock Platform Interface Methods
// ignore: invalid_use_of_protected_member
when(kMockMessagingPlatform.delegateFor(app: anyNamed('app')))
.thenReturn(kMockMessagingPlatform);
// ignore: invalid_use_of_protected_member
when(kMockMessagingPlatform.setInitialValues(
isAutoInitEnabled: anyNamed('isAutoInitEnabled'),
)).thenReturn(kMockMessagingPlatform);
}
// Platform Interface Mock Classes
// FirebaseMessagingPlatform Mock
class MockFirebaseMessaging extends Mock
with MockPlatformInterfaceMixin
implements FirebaseMessagingPlatform {
MockFirebaseMessaging() {
TestFirebaseMessagingPlatform();
}
#override
bool get isAutoInitEnabled {
return super.noSuchMethod(Invocation.getter(#isAutoInitEnabled),
returnValue: true, returnValueForMissingStub: true) as bool;
}
#override
FirebaseMessagingPlatform delegateFor({FirebaseApp? app}) {
return super.noSuchMethod(
Invocation.method(#delegateFor, [], {#app: app}),
returnValue: TestFirebaseMessagingPlatform(),
returnValueForMissingStub: TestFirebaseMessagingPlatform(),
) as FirebaseMessagingPlatform;
}
#override
FirebaseMessagingPlatform setInitialValues({bool? isAutoInitEnabled}) {
return super.noSuchMethod(
Invocation.method(
#setInitialValues, [], {#isAutoInitEnabled: isAutoInitEnabled}),
returnValue: TestFirebaseMessagingPlatform(),
returnValueForMissingStub: TestFirebaseMessagingPlatform(),
) as FirebaseMessagingPlatform;
}
#override
Future<RemoteMessage?> getInitialMessage() {
return super.noSuchMethod(Invocation.method(#getInitialMessage, []),
returnValue: neverEndingFuture<RemoteMessage>(),
returnValueForMissingStub: neverEndingFuture<RemoteMessage>())
as Future<RemoteMessage?>;
}
#override
Future<void> deleteToken() {
return super.noSuchMethod(Invocation.method(#deleteToken, []),
returnValue: Future<void>.value(),
returnValueForMissingStub: Future<void>.value()) as Future<void>;
}
#override
Future<String?> getAPNSToken() {
return super.noSuchMethod(Invocation.method(#getAPNSToken, []),
returnValue: Future<String>.value(''),
returnValueForMissingStub: Future<String>.value('')) as Future<String?>;
}
#override
Future<String> getToken({String? vapidKey}) {
return super.noSuchMethod(
Invocation.method(#getToken, [], {#vapidKey: vapidKey}),
returnValue: Future<String>.value(''),
returnValueForMissingStub: Future<String>.value('')) as Future<String>;
}
#override
Future<void> setAutoInitEnabled(bool? enabled) {
return super.noSuchMethod(Invocation.method(#setAutoInitEnabled, [enabled]),
returnValue: Future<void>.value(),
returnValueForMissingStub: Future<void>.value()) as Future<void>;
}
#override
Stream<String> get onTokenRefresh {
return super.noSuchMethod(
Invocation.getter(#onTokenRefresh),
returnValue: const Stream<String>.empty(),
returnValueForMissingStub: const Stream<String>.empty(),
) as Stream<String>;
}
#override
Future<NotificationSettings> requestPermission(
{bool? alert = true,
bool? announcement = false,
bool? badge = true,
bool? carPlay = false,
bool? criticalAlert = false,
bool? provisional = false,
bool? sound = true}) {
return super.noSuchMethod(
Invocation.method(#requestPermission, [], {
#alert: alert,
#announcement: announcement,
#badge: badge,
#carPlay: carPlay,
#criticalAlert: criticalAlert,
#provisional: provisional,
#sound: sound
}),
returnValue: neverEndingFuture<NotificationSettings>(),
returnValueForMissingStub:
neverEndingFuture<NotificationSettings>())
as Future<NotificationSettings>;
}
#override
Future<void> subscribeToTopic(String? topic) {
return super.noSuchMethod(Invocation.method(#subscribeToTopic, [topic]),
returnValue: Future<void>.value(),
returnValueForMissingStub: Future<void>.value()) as Future<void>;
}
#override
Future<void> unsubscribeFromTopic(String? topic) {
return super.noSuchMethod(Invocation.method(#unsubscribeFromTopic, [topic]),
returnValue: Future<void>.value(),
returnValueForMissingStub: Future<void>.value()) as Future<void>;
}
}
class TestFirebaseMessagingPlatform extends FirebaseMessagingPlatform {
TestFirebaseMessagingPlatform() : super();
}
and here is the unit test itself
void main() {
setupFirebaseMessagingMocks();
setUpAll(() async {
await Firebase.initializeApp();
FirebaseMessagingPlatform.instance = kMockMessagingPlatform;
});
test('An example of test', () {
//...
when(kMockMessagingPlatform.getToken(vapidKey: anyNamed('vapidKey')))
.thenAnswer(
(_) => Future.value('DEVICE_ID'),
);
//...
});
}