I'm trying to write some unit test in my Flutter app. I'm using firebase and i write this function (that i want to test) :
import 'dart:core';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_database/firebase_database.dart';
class FirebaseUtils {
static final FirebaseAuth _auth = FirebaseAuth.instance;
static Exception _exception;
static Future<bool> signIn(String email, String password) async {
FirebaseUser user;
try{
user = await _auth.signInWithEmailAndPassword(email: email, password: password);
} catch (ex){
print(ex);
_exception = ex;
user = null;
}
return user!=null;
}
}
my test :
import "package:test/test.dart";
import 'package:toolpad/utils/firebase/firebase_utils.dart';
import 'dart:async';
void main() {
test("Sign In user from firebase (working)", () async {
bool result = await FirebaseUtils.signIn("test#gmail.com", "lollollol");
expect(result, equals(true));
});
}
When i launch the test that throw an exception :
MissingPluginException(No implementation found for method signInWithEmailAndPassword on channel plugins.flutter.io/firebase_auth)
ERROR: Expected: <true>
Actual: <false>
I have no idea how fix it, anyone have an idea ?
After lot of search it seems that it is not possible without specify an Firebase.initialize before.
So if u want test some functions linked to Firebase, u should use testWidget. That's going to test your app not juste function)
Related
i am trying to implement fcm in my xamarin.forms app. so i am planning to use dependency service on each platform to get the token and save it in my database. i found that this FirebaseInstanceId.Instance.Token is deprecated. so i have to use this instead var instanceIdResult = await FirebaseInstanceId.Instance.GetInstanceId().AsAsync<IInstanceIdResult>(); var token = instanceIdResult.Token;
the thing is that i am not figuring out how to put in the code. i tried this:
GetToken.cs in my xamarin.forms app:
public interface GetToken
{
Task <string> get_token();
}
and this in my xamarin.android
[assembly: Xamarin.Forms.Dependency(typeof(gettoken))]
namespace App8.Droid
{
internal class gettoken : GetToken
{
public Task<string> get_token()
{
Task<string> t = Task.Run(() => tokenToget());
return t;
}
async private Task<string> tokenToget()
{
var instanceIdResult = await FirebaseInstanceId.Instance.GetInstanceId().AsAsync<IInstanceIdResult>();
var token = instanceIdResult.Token;
return token;
}
}
and implemented the service this way in my mainactivity.xaml.cs
Task<string> token = DependencyService.Get<GetToken>().get_token();
System.Diagnostics.Debug.WriteLine($"Token: {token.Result}");
but the app stops. how can i use the code to return the token. note that when i don't use a task and just use it this way (use void as a return type and only call the depedency from the xamarin.forms as DependencyService.Get<GetToken>().get_token();)
async public void get_token()
{
var instanceIdResult = await FirebaseInstanceId.Instance.GetInstanceId().AsAsync<IInstanceIdResult>();
var token = instanceIdResult.Token;
Log.Debug("token", "Refreshed token: " + token);
}
it works. so the problem seems to be in the way i am trying to use the task or something like that. what should i do? thanks in advance.
declare your interface
public interface IToken
{
Task <string> GetToken();
}
then implement it
public class Token : IToken
{
public async Task<string> GetToken()
{
var instanceIdResult = await FirebaseInstanceId.Instance.GetInstanceId().AsAsync<IInstanceIdResult>();
var token = instanceIdResult.Token;
return token;
}
}
i am new to flutter and firebase development, so i really don't know how much will it cost me to keep fetching user data from firebase in every screen that i need them in, so i decided to fetch them once and store them in class MyUser static variables as follows:
in MyApp class:
bool isAuthenticated = false;
Future checkAuthenticity() async {
AuthService.getCurrentUser().then((user) async {
if (user != null) {
String myUid = await AuthService.getCurrentUID();
await MyUserController().getCurrentUserFromFirebase(myUid);
if (mounted)
setState(() {
isAuthenticated = true;
});
} else {
if (mounted)
setState(() {
isAuthenticated = false;
});
}
});
}
#override
Widget build(BuildContext context) {
home: isAuthenticated ? Home(passedSelectedIndex: 0) : Register(),
}
from the above code, this line await MyUserController().getCurrentUserFromFirebase(myUid); is as follows:
getCurrentUserFromFirebase(String uid) async {
await FirestoreService().getCurrentUserData(uid);
}
from the above code, this line await FirestoreService().getCurrentUserData(uid); is as follows:
Future getCurrentUserData(String uid) async {
try {
var userData = await FirebaseFirestore.instance.collection('users').doc(uid).get();
MyUser.fromData(userData.data());
} catch (e) {
if (e is PlatformException) {
return e.message;
}
return e.toString();
}
}
from the above code, this line MyUser.fromData(userData.data()); is a constructor in
MyUser class as follows:
class MyUser {
static String uid;
static String name;
static String username;
static String email;
static String userAvatarUrl;
static String location;
static String phoneNumber;
MyUser.fromData(Map<String, dynamic> data) {
uid = data['id'];
name = data['name'];
username = data['username'];
email = data['email'];
userAvatarUrl = data['userAvatarUrl'];
location = data['location'];
phoneNumber = data['phoneNumber'];
}
}
and to make use of all of the following, in each page that i need to load the current user data in, i use for example:
var userId = MyUser.uid
or to show the current user name i use Text('${MyUser.name}');
when i close the app completely and relaunch it again, it should check for authenticity, and complete executing the rest of the code in main() function.
so my questions are:
1) does this have any performance issues when we release the app?
2) does this will really will prevent unnecessary reads that i can consume in every page i need the data in ?
3) is there any better approach to prevent unnecessary reads from firebase, for example to save the current user data as strings and a profile image locally?
pardon me for prolonging the question, but i wanted to share the code itself.
any help would be much appreciated.
As a short answer,
You can make a class of SharedPreferences to store data as strings in key: value manner.
So anywhere you want you can get an instance of that class and reach it from anywhere in the app.
If you also declare some functions which will decode string to json you will get a ready user class instance in return of your function which will make it easier.
So when you want to save user info to Local Storage(SharedPreferences) you may use a function which will encode your User object to string and save it to SharedPreferences as below..
user.dart' as theUser; for conflict issues
class SharedPrefs {
static SharedPreferences _sharedPrefs;
init() async {
if (_sharedPrefs == null) {
_sharedPrefs = await SharedPreferences.getInstance();
}
}
dynamic get user=> _sharedPrefs.getString('user')!=null?theUser.User.fromString(_sharedPrefs.getString('user')):null;
set user(theUser.User user)=> _sharedPrefs.setString('user', jsonEncode(user));
String get accessToken=> _sharedPrefs.getString('access_token');
set accessToken(String accessToken)=> _sharedPrefs.setString('access_token', accessToken);
void removeString(String entry){
_sharedPrefs.remove(entry);
}
}
final sharedPrefs = SharedPrefs();
And in the app anywhere you can use it directly by typing sharedPrefs.user
I'm teaching myself Flutter by building a simple meal planner app. Part of what I want to do is to use a Firebase function for making calls to the API. I'm using the Spoonacular API, and I do not want to store the API key on the app itself, hence the Firebase backend.
I've set up a file called cloud_functions.dart, which I plan on using to make calls to my Firebase functions. The call to get the recipes is as follows:
Future<SearchRecipesComplexResponseBody> getRecipes() async {
HttpsCallable callable = getCallable('searchRecipes');
try {
final HttpsCallableResult<SearchRecipesComplexResponseBody> results = await callable({'number': 20, 'offset': 0});
print('Results: ');
print(results);
print('Results data:');
print(results.data);
return results.data;
} catch (e) {
print('Error: ');
print(e);
return null;
}
}
HttpsCallable getCallable(String callableName) {
FirebaseFunctions functions = FirebaseFunctions.instance;
if (kDebugMode) {
print('Running in debug mode');
functions.useFunctionsEmulator(origin: 'http://localhost:5001');
}
return functions.httpsCallable(callableName);
}
The code for SearchRecipesComplexResponseBody is as follows:
import 'package:meal_planner/models/recipe.dart';
import 'package:json_annotation/json_annotation.dart';
part 'search_recipes_complex_response_body.g.dart';
#JsonSerializable()
class SearchRecipesComplexResponseBody {
final int offset;
final int number;
final List<Recipe> results;
final int totalResults;
SearchRecipesComplexResponseBody({this.offset, this.number, this.results, this.totalResults});
factory SearchRecipesComplexResponseBody.fromJson(Map<String, dynamic> json) {
return _$SearchRecipesComplexResponseBodyFromJson(json);
}
}
The code for Recipe is as follows:
#JsonSerializable()
class Recipe {
final int id;
#JsonKey(includeIfNull: false)
final int calories;
#JsonKey(includeIfNull: false)
final String carbs;
#JsonKey(includeIfNull: false)
final String fat;
final String image;
final String imageType;
#JsonKey(includeIfNull: false)
final String protein;
final String title;
Recipe({#required this.id, this.calories, this.carbs, this.fat, this.image, this.imageType, this.protein, #required this.title});
factory Recipe.fromJson(Map<String, dynamic> json) {
return _$RecipeFromJson(json);
}
}
While I do get the data back that I'm expecting, there's something going on with the casting that I get this error when running the code:
type '_InternalLinkedHashMap<dynamic, dynamic>' is not a subtype of type 'Map<String, dynamic>'
When I went to debug the code, breaking on the print(results) line in the cloud_functions.dart file, I saw that the data does seem to match the format that I'm expecting
I've attempted to use the json_serializable utility to generate the JSON serialization code, but that didn't work either. I've tried removing the extraneous fields in the Recipe class to no avail.
I think the issue is something to do with the fact that I've got a property on the SearchRecipesComplexResponseBody that's a list of Recipes, but I can't seem to figure out what I'm doing wrong here. For all I know, I could be barking up the wrong tree. Does anyone have any ideas?
RESOURCES CHECKED:
Flutterfire Cloud Functions documentation - https://firebase.flutter.dev/docs/functions/usage/
Search on HttpCallableResult - https://duckduckgo.com/?q=flutterfire+httpcallableresult&ia=web
https://medium.com/codespace69/flutter-working-with-data-json-json-and-serialization-f90165b659d0
I figured it out
I updated the getRecipes function in cloud_functions.dart to be as follows:
Future<SearchRecipesComplexResponseBody> getRecipes() async {
HttpsCallable callable = getCallable('searchRecipes');
try {
final HttpsCallableResult results = await callable({'number': 20, 'offset': 0});
var convertedResult = Map<String, dynamic>.from(results.data);
SearchRecipesComplexResponseBody data = SearchRecipesComplexResponseBody.fromJson(convertedResult);
return data;
} catch (e) {
print('Error: ');
print(e);
return null;
}
}
I saw that I already had a fromJson function defined on my SearchRecipesComplexResponseBody class, but I hadn't been taking advantage of it. I needed to convert the response I got back from Firebase from an _InternalLinkedHashMap<dynamic, dynamic> to the Map<String, dynamic> type that fromJson uses.
I also needed to add anyMap: true inside my JsonSerializer attributes to get the nested list of Recipes in order for its fromJson. I'm not sure why that worked. Anyone have any thoughts?
You may use this to convert from _InternalLinkedHashMap to Map<String, dynamic>. This will get you overcome the error type '_InternalLinkedHashMap<dynamic, dynamic>' is not a subtype of type 'Map<String, dynamic>':
HttpsCallable callable = functions.httpsCallable('your-function');
final results = await callable();
final data = Map<String, dynamic>.from(results.data);
I face a problem where I can't get the current user ID from my firebase. I've already make that every new user will have their own collection based on the UID like this:
void addImagetoFirestore() async {
final CollectionReference imageData = Firestore.instance.collection(uid);
This is my DatabaseService class
class DatabaseService {
final String uid;
DatabaseService({this.uid});
String useruid;
final FirebaseAuth auth = FirebaseAuth.instance;
//Collection reference
final CollectionReference warrantyCollection =
Firestore.instance.collection('user id here');
List<Picture> _warrantyListFromSnapshot(QuerySnapshot snapshot) {
return snapshot.documents.map((doc) {
return Picture(
name: doc.data['product name'],
info: doc.data['info'],
url: doc.data['url'],
uid: doc.data['uid']);
}).toList();
}
Future updateUserData(String name) async {
return await warrantyCollection.document(uid).setData({'name': name});
}
Stream<List<Picture>> get picture {
return warrantyCollection.snapshots().map(_warrantyListFromSnapshot);
}
}
I've tried referring to this,
but it seem like I can't just make something like
final CollectionReference warrantyCollection = Firestore.instance.collection(useridmethod());
are there any other alternative that I can try?
Instead of getting uid as a parameter to class, fetch it in constructor because in your case final FirebaseAuth auth = FirebaseAuth.instance; is useless as I see.
try this;
DatabaseService(){
userId = auth.currentUser.uid;
}
I'm trying to access images in Firebase Storage and cache them locally on the device.
My current attempt uses flutter_cache_manager. The documentation states:
Most common file service will be an [HttpFileService], however one can also make something more specialized. For example you could fetch files from other apps or from local storage.
class HttpFileService implements FileService {
http.Client _httpClient;
HttpFileService({http.Client httpClient}) {
_httpClient = httpClient ?? http.Client();
}
#override
Future<FileServiceResponse> get(String url,
{Map<String, String> headers = const {}}) async {
final req = http.Request('GET', Uri.parse(url));
req.headers.addAll(headers);
final httpResponse = await _httpClient.send(req);
return HttpGetResponse(httpResponse);
}
}
I've tried to extend this class to process the URL for Firebase
class FirebaseHttpFileService extends HttpFileService {
#override
Future<FileServiceResponse> get(String url, {Map<String, String> headers = const {}}) async {
var ref = FirebaseStorage.instance.ref().child(url);
var _url = await ref.getDownloadURL() as String;
return super.get(_url);
}
}
And extend the BaseCacheManager using a template from the GitHub repo, replacing the file service with my new one.
class FirebaseCacheManager extends BaseCacheManager {
static const key = "firebaseCache";
static FirebaseCacheManager _instance;
factory FirebaseCacheManager() {
if (_instance == null) {
_instance = new FirebaseCacheManager._();
}
return _instance;
}
FirebaseCacheManager._() : super(key,
maxAgeCacheObject: Duration(days: 7),
maxNrOfCacheObjects: 20,
fileService: FirebaseHttpFileService());
Future<String> getFilePath() async {
var directory = await getTemporaryDirectory();
return p.join(directory.path, key);
}
}
But I get the following error:
setState() called after dispose(): _ImageState#50d41(lifecycle state: defunct, not mounted, stream: ImageStream#ac6d5(MultiFrameImageStreamCompleter#0c956, [2448×3264] # 1.0x, 3 listeners), pixels: null, loadingProgress: null, frameNumber: null, wasSynchronouslyLoaded: false)
I can process the URL before attempting to retrieve the file but that needlessly wastes time. I've also tried to use other packages like Flutter Cache Image but it seems to crash the app after a short amount of time.
Thanks for any pointers in the right direction!
This problem is actually tied to the errorWidget as seen in the issue here.
The code is working if the error widget is commented out in CachedNetworkImage.