Flutter and Firestore: NoSuchMethodError: The method '...' was called on null - dart

i have a login page in my flutter app to authenticate and register users. When i want to save the user email-address to firestore (after register), i get the error:
flutter: Error: NoSuchMethodError: The method 'saveRegisterUserData' was called on null.
Receiver: null
Tried calling: saveRegisterUserData(email: "test#test.de", userID: "EVDnVFMkYeYd291Yzu2j2r8HLpC2")'
i have created a seperate dart-file for the Firestore Functions. It look like this:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'dart:async';
abstract class BaseFire {
Future <void> saveRegisterUserData({String userID, String email});
}
class Fire implements BaseFire {
final Firestore firestore = new Firestore();
DocumentReference usersdocuments;
Future <void> saveRegisterUserData({String userID, String email}) async {
usersdocuments = Firestore.instance.collection("Users").document(userID);
Map<String, String> registerdata = <String, String>{
"Email": email,
};
usersdocuments.setData(registerdata).whenComplete(() {
return print("Document Added");
}).catchError((error) => print(error));
}
}
i import this file in my login page und execute this function:
import '../Firebase/firestore.dart';
class SignInPage extends StatefulWidget {
SignInPage({this.firestore});
final BaseFire firestore;
#override
State<StatefulWidget> createState() {
return _SignInPageState();
}
}
class _SignInPageState extends State<SignInPage> {
String _email;
String _password;
...
void submitRegister() async {
if (validateAndSave()) {
try {
String userId = await widget.auth.createUserWithEmailAndPassword(_email, _password);
await widget.firestore.saveRegisterUserData(userID: userId, email: _email);
} catch (error) {
print(error);
}
}
}
}
Where is my mistake? Thank you for our help

You have Automatic Class Member Variable Initialization at
SignInPage({this.firestore});
{} make the parameter optional and unless you truly mean to make your
firebase //variable
optional or have default value don't enclose it in {} just remove it. You will get any error message where you have used your SignInPage,just fix the parameter and hopefully things will work fine.

Related

'Future<bool> Function()' can't be assigned to the parameter type 'Future<bool>?'

I'm trying to implement an event callback directly in the constructor, but for some reason it does not compile and I do not understand what's the issue with my code.
abstract class Base {
final Future<bool>? onMagic;
Base({
this.onMagic
});
Future<void> doSomething() async {
if(onMagic != null) {
// does not work... why?
// final useMagic = await onMagic!();
onMagic?.then((magic) {
if(magic) print("TODO make something magical");
});
}
}
}
class RealMagic extends Base {
RealMagic() : super(
// Error: The argument type 'Future<bool> Function()' can't be assigned to the parameter type 'Future<bool>?'.
onMagic: () async => await _magic();
);
Future<bool> _magic() async {
return true;
}
}
I inlined the error above. If that's not possible which alternatives do I have to handle the optional callback?
The problem is with the type of the onMagic. It's not a Future<bool>, it should be a Future<bool> Function()?
abstract class Base {
final Future<bool> Function()? onMagic;
Base({this.onMagic});
Future<void> doSomething() async {
onMagic?.call().then((magic) {
if (magic) print("TODO make something magical");
});
}
}
class RealMagic extends Base {
RealMagic()
: super(
onMagic: () async => await _magic(),
);
static Future<bool> _magic() async { // Made this static so it can be accessed in the constructor
return true;
}
}

How to <T extend BaseClass> where BaseClass has a factory function?

Following is a simple class that provides a few helper functions for reading and writing data.
class BaseDAO<T> {
final String _modelName;
static late final StoreRef<int, Map<String, Object?>> _store;
BaseDAO(this._modelName) {
_store = intMapStoreFactory.store(_modelName);
}
Future<Database> get _db async => await AppDatabase().database;
Future<void> create(T object) async {
await _store.add(await _db, object.toJSON()); //The method 'toJSON' can't be unconditionally invoked because the receiver can be 'null'.
}
}
Now the issue with this is that the generic type T doesn't have toJSON function. I tried fixing this by writing an abstract class.
abstract class BaseModel {
Map<String, dynamic> toJSON();
factory BaseModel.fromJson(Map<String, dynamic> json);
}
and extending T with BaseModel. This presents all kinds of issues one of them being that I am unable to write an abstract class.
Any solution will be greatly appreciated.
In your case T object is empty. You need to extend it to some object which provides a method toMap().
Example:
class BaseDAO<T extends BaseModel> {
final String _modelName;
static late final StoreRef<int, Map<String, Object?>> _store;
BaseDAO(this._modelName) {
_store = intMapStoreFactory.store(_modelName);
}
Future<Database> get _db async => await AppDatabase().database;
Future<void> create(T object) async {
await _store.add(await _db, object.toMap()); // <- Dart see that this object extends to BaseModel and has a method `toMap()`,
}
}
abstract class BaseModel {
Map<String, dynamic> toMap();
}
class User extends BaseModel {
User({this.name});
final String? name;
#override
Map<String, dynamic> toMap() => {'name': name};
}
Future<void> create() async {
final user = User(name: 'Superman');
final base = BaseDAO('ModelName');
await base.create(user); // <- The user object will be added as `Map`.
}

Flutter - How to parse JSON data to a base class?

I'm wondering how can I parse a JSON data to a base class, because I'm trying to write, but get Unhandled Exception: type 'Future' is not a subtype of type 'WeatherModel' in type cast.
Here is my code:
abstract class BaseModel {
fromJson(Map<String, dynamic> json);
Map<String, dynamic> toJson();
}
import 'BaseModel.dart';
class WeatherModel extends BaseModel {
String success;
Result result;
Records records;
WeatherModel({this.success, this.result, this.records});
#override
fromJson(Map<String, dynamic> json) {
//...
}
#override
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
//... parse to Json
return data;
}
}
}
import 'dart:async';
import 'package:dio/dio.dart';
import 'package:flightinfo/model/BaseModel.dart';
class HttpUtils {
static Future<BaseModel> get(String url, Map<String, dynamic> params, BaseModel baseModel) async {
try {
print("url:$url,params:$params");
Response response = await Dio().get(url, queryParameters: params);
if (response != null && response.statusCode == 200) {
baseModel.fromJson(response.data);
print(baseModel);
return baseModel;
}
print(response);
} catch (exception) {
print(exception);
}
return null;
}
}
class WeatherRequest {
Future<WeatherModel> get() async {
return HttpUtils.get(_url, _params,new WeatherModel());
}
}
I think HttpUtils.get direct to BaseModel for extensibility. This is very common in Java, but in Dart I get a exception in WeatherRequest in below line:
return HttpUtils.get(_url, _params,new WeatherModel());
Unhandled Exception: type 'Future' is not a subtype of type 'WeatherModel' in type cast
In Dart, how to take care of this cast?
The error might be occurring because get() method of WeatherRequest returns a Future to WeatherModel but you have returned a Future<BaseModel> instead.
Try adding await to resolve Future converting it to BaseModel before returning.
return (await HttpUtils.get(_url, _params,new WeatherModel()) );
Also, as per this SO answer:
You can cast an object to its parent class, but you can't cast an object to any of its child class.
So implicit casting of BaseModel to WeatherModel may not work as expected. Instead, you can add a helper method to BaseModel like BaseModel.toWeatherModel and return a WeatherModel
abstract class BaseModel {
toWeatherModel(){
//...
}
}
Or modify get method of WeatherRequest to return Future<BaseModel>.
class WeatherRequest {
Future<BaseModel> get() async {
return (await HttpUtils.get(_url, _params,new WeatherModel()));
}
}

Flutter Shared Preferences Auth FIle

I'm trying to write an auth file, with a list of finals with shared preferences values in it. I could import that auth file in my other files and i could get like the name or email without importing shared preferences in every file.
It would probably look way smoother and cleaner.
I thought something like this would have worked but it didn't
/// ------------Auth------------ ///
final email = getEmail();
getEmail() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
return prefs.getString('email');
}
Does anybody have any idea how to do this?
Greetings,
Jente
I assume you want to use the method in multiple files. The problem with your code is that the getEmail method is marked async that means it will have to return a Future. Think about it like this, when you mark a method as async it means it will return something (or finish executing) in the near future. When ? Well you don't know exactly when, so you'll need to get "notified" when the method is "done", that's why you'll use a Future. Something like this:
Future<String> getEmail() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
return prefs.getString('email');
}
class ThisIsTheClassWhereYouWantToUseTheFunction {
//let's say you have a method in your class called like the one below (it can be any other name)
void _iNeedTheEmailToPrintIt() {
//this is the way you can call the method in your classes, this class is just an example.
getEmail().then((thisIsTheResult){ // here you "register" to get "notifications" when the getEmail method is done.
print("This is the email $thisIsTheResult");
});
}
}
you can define a class Auth or much better a scoped_model.
Here's a class implementation
class Auth {
get email() {
final SharedPreferences prefs = await SharedPreferences.getInstance();
return prefs.getString('email');
}
set email(String em) {
final SharedPreferences prefs = await SharedPreferences.getInstance();
pref.setString('email', em);
}
}
and now you can call it in your widgets :)
Try this;
make dart file (Filename and Class Name ShareUtils)
add follow Code
import 'package:shared_preferences/shared_preferences.dart';
import 'dart:async';
class ShareUtils {
static ShareUtils _instance;
SharedPreferences ShareSave;
factory ShareUtils() => _instance ?? new ShareUtils._();
ShareUtils._();
void Instatce() async {
ShareSave = await SharedPreferences.getInstance();
}
Future<bool> set(key, value) async {
return ShareSave.setString(key, value);
}
Future<String> get(key) async {
return ShareSave.getString(key);
}
}
2.Add main.dart
class MyApp extends StatelessWidget {
static ShareUtils shareUtils;
#override
Widget build(BuildContext context) {
ThemeData mainTheme = new ThemeData(
);
shareUtils = new ShareUtils();
shareUtils.Instatce();
MaterialApp mainApp = new MaterialApp(
title: "Your app",
theme: mainTheme,
home: new SplashPage(),
debugShowCheckedModeBanner: true,
routes: <String, WidgetBuilder>{
"RegisterPage": (BuildContext context) => new RegisterPage(),
"HomePage": (BuildContext context) => new HomePage(),
},
);
return mainApp;
}
}
3.SET
void UserInfo(code, token) async{
await MyApp.shareUtils.set("token", token);
await MyApp.shareUtils.set("code", code);
await Navigator.of(context).pushNamed("HomePage");
}
4.GET
Future NextPage() async {
MyApp.shareUtils.get("token").then((token) {
print(token);
if (token == null || token == "") {
Navigator.of(context).popAndPushNamed("RegisterPage");
} else {
Navigator.of(context).popAndPushNamed("HomePage");
}
});
}
Hope to help.

How to handle TypeORM entity field unique validation error in NestJS?

I've set a custom unique validator decorator on my TypeORM entity field email. NestJS has dependency injection, but the service is not injected.
The error is:
TypeError: Cannot read property 'findByEmail' of undefined
Any help on implementing a custom email validator?
user.entity.ts:
#Column()
#Validate(CustomEmail, {
message: "Title is too short or long!"
})
#IsEmail()
email: string;
My CustomEmail validator is
import {ValidatorConstraint, ValidatorConstraintInterface,
ValidationArguments} from "class-validator";
import {UserService} from "./user.service";
#ValidatorConstraint({ name: "customText", async: true })
export class CustomEmail implements ValidatorConstraintInterface {
constructor(private userService: UserService) {}
async validate(text: string, args: ValidationArguments) {
const user = await this.userService.findByEmail(text);
return !user;
}
defaultMessage(args: ValidationArguments) {
return "Text ($value) is too short or too long!";
}
}
I know I could set unique in the Column options
#Column({
unique: true
})
but this throws a mysql error and the ExceptionsHandler that crashes my app, so I can't handle it myself...
Thankx!
I can propose 2 different approaches here, the first one catches the constraint violation error locally without additional request, and the second one uses a global error filter, catching such errors in the entire application. I personally use the latter.
Local no-db request solution
No need to make additional database request. You can catch the error violating the unique constraint and throw any HttpException you want to the client. In users.service.ts:
public create(newUser: Partial<UserEntity>): Promise<UserEntity> {
return this.usersRepository.save(newUser).catch((e) => {
if (/(email)[\s\S]+(already exists)/.test(e.detail)) {
throw new BadRequestException(
'Account with this email already exists.',
);
}
return e;
});
}
Which will return:
Global error filter solution
Or even create a global QueryErrorFilter:
#Catch(QueryFailedError)
export class QueryErrorFilter extends BaseExceptionFilter {
public catch(exception: any, host: ArgumentsHost): any {
const detail = exception.detail;
if (typeof detail === 'string' && detail.includes('already exists')) {
const messageStart = exception.table.split('_').join(' ') + ' with';
throw new BadRequestException(
exception.detail.replace('Key', messageStart),
);
}
return super.catch(exception, host);
}
}
Then in main.ts:
async function bootstrap() {
const app = await NestFactory.create(/**/);
/* ... */
const { httpAdapter } = app.get(HttpAdapterHost);
app.useGlobalFilters(new QueryErrorFilter(httpAdapter));
/* ... */
await app.listen(3000);
}
bootstrap();
This will give generic $table entity with ($field)=($value) already exists. error message. Example:
I have modified my code. I am checking the uniqueness of username/email in the user service (instead of a custom validator) and return an HttpExcetion in case the user is already inserted in the DB.
The easiest solution!
#Entity()
export class MyEntity extends BaseEntity{
#Column({unique:true}) name:string;
}
export abstract class BaseDataService<T> {
constructor(protected readonly repo: Repository<T>) {}
private async isUnique(t: any) {
const uniqueColumns = this.repo.metadata.uniques.map(
(e) => e.givenColumnNames[0]
);
for (const u of uniqueColumns) {
const count = await this.repo.count({ where: { [u]: ILike(t[u]) } });
if (count > 0) {
throw new UnprocessableEntityException(`${u} must be unique!`);
}
}
}
async save(body: DeepPartial<T>) {
await this.isUnique(body);
try {
return await this.repo.save(body);
} catch (err) {
throw new UnprocessableEntityException(err.message);
}
}
async update(id: number, updated: QueryDeepPartialEntity<T>) {
await this.isUnique(updated)
try {
return await this.repo.update(id, updated);
} catch (err) {
throw new UnprocessableEntityException(err.message);
}
}
}
An approach that works for modern version of NestJS which is based in Daniel Kucal's answer and actually returns the error to the frontend when calling the JSON API is the following:
import {
Catch,
ArgumentsHost,
BadRequestException,
HttpException,
} from '#nestjs/common';
import { BaseExceptionFilter } from '#nestjs/core';
import { QueryFailedError } from 'typeorm';
type ExceptionType = { detail: string; table: string };
#Catch(QueryFailedError)
export class QueryErrorFilter extends BaseExceptionFilter<
HttpException | ExceptionType
> {
public catch(exception: ExceptionType, host: ArgumentsHost): void {
const { detail = null } = exception || {};
if (
!detail ||
typeof detail !== 'string' ||
// deepcode ignore AttrAccessOnNull: <False positive>
!detail.includes('already exists')
) {
return super.catch(exception, host);
} // else
/**
* this regex transform the message `(phone)=(123)` to a more intuitive `with phone: "123"` one,
* the regex is long to prevent mistakes if the value itself is ()=(), for example, (phone)=(()=())
*/
const extractMessageRegex =
/\((.*?)(?:(?:\)=\()(?!.*(\))(?!.*\))=\()(.*?)\)(?!.*\)))(?!.*(?:\)=\()(?!.*\)=\()((.*?)\))(?!.*\)))/;
const messageStart = `${exception.table.split('_').join(' ')} with`;
/** prevent Regex DoS, doesn't treat messages longer than 200 characters */
const exceptionDetail =
exception.detail.length <= 200
? exception.detail.replace(extractMessageRegex, 'with $1: "$3"')
: exception.detail;
super.catch(
new BadRequestException(exceptionDetail.replace('Key', messageStart)),
host,
);
}
}
Also, not forgetting main.ts:
async function bootstrap() {
const app = await NestFactory.create(/**/);
/* ... */
const { httpAdapter } = app.get(HttpAdapterHost);
app.useGlobalFilters(new QueryErrorFilter(httpAdapter));
/* ... */
await app.listen(3000);
}
bootstrap();

Resources