I'm doing small project with FLUTTER/DART and RXDART.
Here is part of the code:
class RegisterBloc with UserValidator {
final _username = BehaviorSubject<String>();
final _email = BehaviorSubject<String>();
final _password = BehaviorSubject<String>();
Function(String) get setUsername => _username.sink.add;
Stream<String> get username => _username.stream.transform(usernameValidatorTransformer);
Function(String) get setEmail => _email.sink.add;
Stream<String> get email => _email.stream.transform(emailValidatorTransformer);
Function(String) get setPassword => _password.sink.add;
Stream<String> get password => _password.stream.transform(passwordValidatorTransformer);
Stream<bool> get validForm => CombineLatestStream.combine3<String, String, String, bool>(_username.stream, _email.stream, _password.stream, (u, e, p) => true);
submit() async {
final form = RegisterForm(
username: _username.value,
email: _email.value,
password: _password.value
);
}
dispose() {
_username.close();
_email.close();
_password.close();
}
}
The Problem is validForm Stream emit null data and combiner never get called, even all streams already emit at least 1 value.
What worst, this only happen on Android (mine v.8.1.0), the codes work just as expected on iOS 11.
In case anyone wondering, i use StreamBuilder widget to update widget data.
Do i missing something here? any configuration need to be set prior or else.
Here my Development Environment:
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, v1.2.1, on Mac OS X 10.13.6 17G6030, locale en-ID)
[✓] Android toolchain - develop for Android devices (Android SDK version 28.0.3)
[✓] iOS toolchain - develop for iOS devices (Xcode 10.1)
[✓] Android Studio (version 3.3)
[✓] Connected device (1 available)
RxDart: 0.21.0
Android Device: 8.1.0
I did a project like this and it's working correctly, everything in ur bloc class is ok, i guess the problem is in ur validator class, if u shared ur validation class maybe i could help u more,
Here my codes
Its the bloc class :
class Bloc with Validator {
final _emailController = BehaviorSubject<String>();
final _passwordController = BehaviorSubject<String>();
final _textController = BehaviorSubject<String>();
// add to stream
Function(String) get reciveText => _textController.sink.add;
Function(String) get changeEmail => _emailController.sink.add;
Function(String) get changePassword => _passwordController.sink.add;
// access to stream
Stream<String> get emailvalidation =>
_emailController.stream.transform(validateEmail);
Stream<String> get passwordvalidation =>
_passwordController.stream.transform(validatePassword);
Stream<bool> get submitValid =>
Rx.combineLatest2(emailvalidation, passwordvalidation, (a, b) => true);
Stream<String> get accesstext =>
_textController.stream.transform(validateText);
submit() {
final validemail = _emailController.value;
final validpass = _passwordController.value;
reciveText('user name : $validemail \n pass : $validpass ');
print('all valid');
}
And its validation codes :
final validateEmail = StreamTransformer<String, String>.fromHandlers(
handleData: (data, sink) {
if (data.contains('#') && data.contains('.')) {
sink.add(data);
} else {
sink.addError('Enter Valid emaill');
}
},
);
final validatePassword = StreamTransformer<String, String>.fromHandlers(
handleData: (data, sink) {
if (data.length > 3) {
sink.add(data);
} else {
sink.addError('Password is too short');
}
},
);
by the way u can see my codes that are working fine in my GitHub, maybe it help u to see what is going wrong in ur project.
https://github.com/Manticodes/flutter_loginscreen_blocpattern
I hope it help u to solve ur problem.
Related
I got this error when i call verifyPhoneNumber method:
My code works fine in android. Only in IOS it throws me this error. I checked a lot of issues on stackoverflow and github and have made sure that my GoogleService-Info.plist is in the right folder. I imported GoogleService-Info.plist through Xcode only and it is also added in CopyBundleResources section in Xcode-> Runner -> BuildPhases. Also a lot of places I saw that I need to add REVERSED_CLIENT_ID in URL Types in Xcode. Done this and also checked Info.plist that my REVERSED_CLIENT_ID is added correctly.
I have done everything but still don't know what is causing the issue.
My code for mobile auth:
import 'package:firebase_auth/firebase_auth.dart';
import 'package:[**package_name**]/functions/typedef.dart';
import 'package:[**package_name**]/helpers/log_helper.dart';
class MobileAuth{
MobileAuth(
this.mobileNumber,
this.afterCodeSent,
this.verificationCompleted,
this.verificationFailed,
this.autoTimeout,
this.ifOtpFailed,
this.afterOtpSend);
final String mobileNumber;
final AfterCodeSent afterCodeSent;
final PhoneAuthVerificationCompleted verificationCompleted;
final PhoneAuthVerificationFailed verificationFailed;
final AuthFailedMessage autoTimeout;
final AuthFailedMessage ifOtpFailed;
final AfterOtpSend afterOtpSend;
FirebaseAuth auth = FirebaseAuth.instance;
/// to sent the code to the given mobile number
void initialAuth() async{
await auth.verifyPhoneNumber(
phoneNumber: mobileNumber,
verificationCompleted: (PhoneAuthCredential credential) {
verificationCompleted(credential);
},
verificationFailed: (FirebaseAuthException e) {
verificationFailed(e);
},
codeSent: (String verificationId, int? resendToken) {
afterCodeSent(verificationId, resendToken ?? 0);
},
timeout: const Duration(seconds: 15),
codeAutoRetrievalTimeout: (String verificationId) {
autoTimeout(verificationId);
},
);
}
/// check otp by submitting to the auth api
void verifyOtp(String smsCode, String verificationId) async{
// Create a PhoneAuthCredential with the code
PhoneAuthCredential credential = PhoneAuthProvider.credential(verificationId: verificationId, smsCode: smsCode);
// Sign the user in (or link) with the credential
try{
UserCredential userCredential = await auth.signInWithCredential(credential);
LogHelper.log("After sending OTP: ${userCredential.user?.uid}");
afterOtpSend(userCredential);
}catch(e){
LogHelper.log("After sending OTP ${e.toString()}");
ifOtpFailed(e.toString());
}
}
void resendVerificationCode(int token) {
auth.verifyPhoneNumber(
forceResendingToken: token,
phoneNumber: mobileNumber,
verificationCompleted: (PhoneAuthCredential credential) {
verificationCompleted(credential);
},
verificationFailed: (FirebaseAuthException e) {
verificationFailed(e);
},
codeSent: (String verificationId, int? resendToken) {
afterCodeSent(verificationId , resendToken ?? 0);
},
timeout: const Duration(seconds: 60),
codeAutoRetrievalTimeout: (String verificationId) {
autoTimeout(verificationId);
},
);
}
}
And i call this in my statefull widget init method like this:
#override
void initState(){
String mobileNumber = "${widget.countryCode} ${widget.phoneNumber}";
mobileAuth = MobileAuth(mobileNumber, afterCodeSent,
verificationCompleted, verificationFailed, autoTimeout, ifOtpFailed, afterOtpSend);
sendOtpToPhone();
}
void sendOtpToPhone(){
mobileAuth.initialAuth();
}
The code works fine in android debug and release mode, so I only have this issue in IOS. Crashes when I call sendOtpToPhone(); -> mobileAuth.initialAuth(); -> auth.verifyPhoneNumber();
I have know for sure that auth.verifyPhoneNumber(); is only causing the error, because if I comment that segment of code then no crash is happening.
Also have tried flutter clean and i am using firebase_auth: 3.3.16 . Also tried it with 3.3.14.
Please assist as I have an issue with xamarin UITest , the EnterText method is not sending any keys to Android version 10 devices. the Repl() shows that the text is entered but on the actual device nothing is being inserted .The code works fine on Android version 9 and below enter image description here
There is a bug with xamarin ui test, see https://github.com/microsoft/appcenter/issues/1451
I implemented the following as a workaround:
protected bool OnAndroid => AppManager.Platform == Platform.Android;
protected bool OniOS => AppManager.Platform == Platform.iOS;
public void SetEntryText(Query entry, string entryName, string value)
{
if (OnAndroid)
{
app.Query(e => e.Marked(entryName).Invoke("setText", value));
}
else if (OniOS)
{
app.ClearText(entry);
app.EnterText(entry, value);
}
}
I wrote a flutter login with facebook app.
It works correctly on Android.
On iOS simulator and on real iPhone device it does not work. Both having iOS 13.2.
pub spec.yaml file
firebase_auth: ^0.6.6
flutter_facebook_login: ^1.1.1
flutter_auth_buttons: ^0.3.1
main.dart file
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter_facebook_login/flutter_facebook_login.dart';
import 'package:flutter_auth_buttons/flutter_auth_buttons.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _MyAppState();
}
}
class _MyAppState extends State<MyApp> {
FirebaseAuth _auth = FirebaseAuth.instance;
bool isLogged = false;
FirebaseUser myUser;
Future<FirebaseUser> _loginWithFacebook() async {
var facebookLogin = new FacebookLogin();
var result = await facebookLogin.logInWithReadPermissions(['email']);
debugPrint(result.status.toString());
if (result.status == FacebookLoginStatus.loggedIn) {
FirebaseUser user =
await _auth.signInWithFacebook(accessToken: result.accessToken.token);
return user;
}
return null;
}
void _login() {
_loginWithFacebook().then((response) {
if (response != null) {
myUser = response;
setState(() {
isLogged = true;
});
}
});
}
void _logout() async {
await _auth.signOut().then((response) {
setState(() {
isLogged = false;
});
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Login App',
home: Scaffold(
appBar: AppBar(
title: Text(isLogged ? 'Profile Page' : 'Login App'),
actions: <Widget>[
IconButton(
onPressed: _logout,
icon: Icon(Icons.power_settings_new),
)
],
),
body: Center(
child: isLogged
? Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Name: ' + myUser.displayName),
Image.network(myUser.photoUrl),
],
)
: FacebookSignInButton(
onPressed: _login,
),
),
),
);
}
}
I added the plist file in the Xcode project correctly. And configured the iOS version of the Facebook developer correctly based on the following steps:
https://developers.facebook.com/docs/facebook-login/ios
I changed the project workspace settings in Xcode between Legacy build and new build system. I still get error.
I tried to add the following to the pod file, and still get error
pod 'FBSDKCoreKit', '~> 4.44'
And even all proposed solutions in here
How to fix build error with FBSDKLoginKit in Xcode
The error that I get is
No known class method for selector 'dictionary:setObject:forKey:'
I also tried to change the Firebase version and the Facebook Flutter login version in pub spec file (between 1.1.1 and 1.2.0 because I do not want to use AndroidX) but still getting the error.
Note that I updated Xcode to latest version, and error was not fixed.
I changed the iOS deployment version in Xcode for all pods to version 8. And error not fixed!
UPDATE (JULY 2020)
You can use a new package that works for both Android and iOS:
flutter_login_facebook
If you want to use the flutter_facebook_login, below is a solution:
UPDATE (APRIL 2020)
Add flutter_facbook_login version 3.0.0 from pub.dev to your flutter project
Create your app on Facebook developers and configure your flutter app accordingly
Change your iOS target version to 9 in Xcode
Open in terminal the Podfile.lock which is located in the iOS folder of your flutter project and change all facebook api versions to 5.8.0. If the Podfile.lock does not exist, run your app once (so the file is created) and after it fires an error edit Podfile.lock then run it again. If your app still fails, run pod install manually form terminal.
There are two problems:
the compatibility between Facebook Login API and Firebase.
iOS update to version 13.x.
In pubspec.yaml file updated the firebase version to latest version.
Also, I am using version 1.2.0 for facebook login but still 1.1.1 works.
firebase_auth: ^0.14.0+5
flutter_facebook_login: ^1.2.0
flutter_auth_buttons: ^0.3.1
I opened Runner.xcworkspace in Xcode (located in iOS folder inside flutter project), then File->WorkSpace settings, and selected New Build System option.
For all pods, change iOS deployment version to 8.0 (or above).
Now the application builds successfully and runs on iOS. But I get CancelledByUser each time I click on the Login with Facebook button.
This is a bug when having iOS update 13.x running the facebook login api.
I found a turnaround that made the login works.
final facebookLogin = new FacebookLogin();
facebookLogin.loginBehavior = FacebookLoginBehavior.webViewOnly;
You can directly use my function as it works on both android and iOS and also registers the user in firebase console.
I have simply opening the login page in the WebView and when the user register himself, I am storing the credential in the firebase and then printing his name .
final FacebookLogin fblogin = FacebookLogin();
FirebaseAuth auth = FirebaseAuth.instance;
Future loginwihFacebook() async {
fblogin.loginBehavior = FacebookLoginBehavior.webViewOnly;
final result = await fblogin.logInWithReadPermissions(["email"]);
switch (result.status) {
case FacebookLoginStatus.loggedIn:
final token = result.accessToken.token;
await auth.signInWithCredential(FacebookAuthProvider.credential(token));
var url =
'https://graph.facebook.com/v2.12/me?fields=name,first_name,picture,last_name,email&access_token=$token';
var graphapiresponse = await http.get(url);
var profileData = jsonDecode(graphapiresponse.body);
var name = profileData["name"];
print(name);
break;
case FacebookLoginStatus.cancelledByUser:
print("cancelled by user");
break;
case FacebookLoginStatus.error:
print("error");
break;
}
}
This is my service:
Future ads(token, adsLimit) {
return _netUtil.post(BASE_URL + "/getuserads", body: {"token": token, "quantity": adsLimit}).then(
(dynamic res) {
return res;
});
}
And I'm trying to use it like so:
var myToken = prefs.getString('token');
var adsLimit = prefs.getInt('adsLimit') ?? 10;
this.api.ads(myToken, adsLimit).then((res) async {
var ads = res['ads'];
print('New Ads $ads');
// _showBigPictureNotification(ads);
});
Even doing:
this.api.ads(myToken, 10).then((res) async {
the same error still comes up. What's the fix?
Flutter 0.9.3-pre.14 • channel master • https://github.com/flutter/flutter.git
Framework • revision 449e3c2a0a (5 days ago) • 2018-09-20 19:46:50 -0700
Engine • revision a8890fdccd
Tools • Dart 2.1.0-dev.5.0.flutter-46ec629096
body is probably a Map<String, String>, so you need to convert the int to a String. It's a good idea to give your formal parameters types.
Future<void> ads(String token, int adsLimit) {
return _netUtil.post(
BASE_URL + '/getuserads',
body: {
'token': token,
'quantity': '$adsLimit',
},
);
}
If you want to catch that error at compile type, give the body map a type like this:
body: <String, String>{
'token': token,
'quantity': adsLimit,
},
I used the below method to get the app name and packageName but I need Bundle id for iPhone users.
I want to share an app link.
I did it in android but on iPhone, I need bundle id.
Future<Null> _initPackageInfo() async {
final PackageInfo info = await PackageInfo.fromPlatform();
setState(() {
_packageInfo = info;
packageName = info.packageName;
appName = info.appName;
buildNumber = info.buildNumber;
});
}
To find the project name manually, you can look in AndroidManifest.xml or in Info.plist.
Android
In Android the package name is in the AndroidManifest:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
...
package="com.example.appname">
iOS
In iOS the package name is the bundle identifier in Info.plist:
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
which is found in Runner.xcodeproj/project.pbxproj:
PRODUCT_BUNDLE_IDENTIFIER = com.example.appname;
See also
How to change package name in flutter?
In iOS portion of a Flutter project Product Bundle Identifier is in project.pbxproj file in path:
[your-flutter-project-dir]\ios\Runner.xcodeproj\project.pbxproj
and that is specified as following:
PRODUCT_BUNDLE_IDENTIFIER = com.app.flutter.example;
Note in that this value is same as Android Package Name in Flutter projects.
If you just need to get the IOS bundle ID manually, here is how
In Android Studio select the root folder (ex. flutte_name)
In the taskbar go to Tools>>Flutter>>Open IOS Modules in Xcode
In Xcode open Runner and under Identity/Bundle Identifier there is your ID
You probably want to update it to a custom name rather than com.example.appName anyways so you can check out this package called change_app_name on pub.dev here https://pub.dev/packages/change_app_package_name
Super simple I just did it myself. Add the package to your pubspec file and than in a terminal in the root folder type in "flutter pub run change_app_package_name:main com.company.app" change the last part to whatever you want and it will update your whole project with the new name you chose
what i see you have already get the package name by info.packageName; and appName info.appName;
try request to http://itunes.apple.com/search?media=software&country={countryID}&term={appName}
you can test by curl instead of browser ( this will return file.txt )
curl --location -g --request GET 'http://itunes.apple.com/search?media=software&country=id&term=appName' \
--header 'Accept: application/json' | jq
in other solution some people can get it from
// if doesnt work try with these lookup
itunes.apple.com/lookup?bundleId=com.apple.Pages
| jq // these for make response beutifull
response json
....
....
bundleId: '212312xxxxxx'
trackViewUrl: 'https://apps.apple.com/id/app/appName/id212312xxxxxx'
....
....
i use these concept to self update from backend ( SplashScreen / check app version ) will popUP alert forced user to go to the market place.
Use get_version package . It's the easiest way
Installing :
dependencies:
get_version: any
Usage:
String projectAppID;
// Platform messages may fail, so we use a try/catch PlatformException.
try {
projectAppID = await GetVersion.appID;
} on PlatformException {
projectAppID = 'Failed to get app ID.';
}
You can use it as String inside anything you want like Text widget etc ...
Another extract of get_version in a small application :
import 'package:get_version/get_version.dart';
class _MyAppState extends State<MyApp> {
String _projectAppID = '';
#override
initState() {
super.initState();
initPlatformState();
}
// Platform messages are asynchronous, so we initialize in an async method.
initPlatformState() async {
String projectAppID;
try {
projectAppID = await GetVersion.appID;
} catch (e) {
projectAppID = 'Failed to get app ID.';
}
setState(() {
_projectAppID = projectAppID;
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: ListTile(
leading: new Icon(Icons.info),
title: const Text('App ID'),
subtitle: new Text(_projectAppID),
),
),
);
}
}
Output :