I'm creating my first Flutter app. The app should show among other things on some places on a map. For the map I use google_maps_flutter (https://pub.dev/packages/google_maps_flutter).
Everything works fine on Android. As soon as I start the app on IOS and click on "Map" in my app, the app crashes without stacktrace. Android Studio just says "Lost connection to Device".
I have the SDK for Android and IOS active on the Google Cloud Platform and have added the key in the appDelegate.swift in my project.
I dont get it. Does anyone know what the problem could be? Thanks a lot for your help <3
This is my Class, which returns the Scaffold with Google Maps Widget:
class WaterMapScreen extends StatefulWidget {
#override
State<WaterMapScreen> createState() => _WaterMapScreenState();
}
class _WaterMapScreenState extends State<WaterMapScreen> {
GetIt getIt = GetIt.instance;
WaterService waterService;
List<Section> sectionList;
Location _location = new Location();
StreamController<FavSection> _streamController =
StreamController<FavSection>();
Set<Polygon> polygonSet = new Set();
UserService userService;
FavSection _selectedSection;
_WaterMapScreenState() {
waterService = getIt.get<WaterService>();
userService = getIt.get<UserService>();
}
initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.arrow_back_outlined, color: Colors.orange),
onPressed: () {
Navigator.pop(context);
}),
title: Text(
"Entdecken",
style: TextStyle(fontWeight: FontWeight.bold, color: Colors.orange),
),
),
body: Stack(children: <Widget>[
getGoogleMapWithPolygons(),
CustomSlidingPanel(stream: _streamController.stream),
]),
);
}
FutureBuilder getGoogleMapWithPolygons() {
return FutureBuilder(
future: fetchSectionListWithFavorites(),
builder: (BuildContext context, AsyncSnapshot sectionListAsync) {
switch (sectionListAsync.connectionState) {
case ConnectionState.none:
case ConnectionState.waiting:
return Center(
child: CircularProgressIndicator(color: Colors.orange));
default:
if (sectionListAsync.hasError) {
print('Error: ${sectionListAsync.error}');
return new Center(child: Text('Es ist ein Fehler aufgetreten'));
} else {
return FutureBuilder(
future: _location.getLocation(),
builder:
(BuildContext context, AsyncSnapshot locationAsync) {
switch (locationAsync.connectionState) {
case ConnectionState.none:
case ConnectionState.waiting:
return Center(
child: CircularProgressIndicator(
color: Colors.orange));
default:
return GoogleMap(
initialCameraPosition: CameraPosition(
target: LatLng(locationAsync.data.latitude,
locationAsync.data.longitude),
zoom: 10),
myLocationButtonEnabled: true,
mapType: MapType.normal,
myLocationEnabled: true,
polygons: sectionListAsync.data
.where((item) =>
item.section.gewaesserTyp == "SEE" ||
item.section.gewaesserTyp == "WEIER")
.map<Polygon>(
(item) => Polygon(
polygonId: PolygonId(item.section.id),
points: item.section.paths
.map<LatLng>((path) =>
LatLng(path.lat, path.lng))
.toList(),
fillColor: Colors.orange.withOpacity(0.5),
strokeColor: Colors.deepOrange,
strokeWidth: 5,
consumeTapEvents: true,
onTap: () {
_streamController
.add(_selectedSection = item);
},
),
)
.toSet(),
polylines: sectionListAsync.data
.where((item) =>
item.section.gewaesserTyp == "BACH" ||
item.section.gewaesserTyp == "FLUSS")
.map<Polyline>(
(item) => Polyline(
polylineId: PolylineId(item.section.id),
points: item.section.paths
.map<LatLng>((path) =>
LatLng(path.lat, path.lng))
.toList(),
color: Colors.deepOrange,
width: 5,
consumeTapEvents: true,
onTap: () {
_streamController
.add(_selectedSection = item);
},
),
)
.toSet(),
onTap: (LatLng latLng) {
_streamController.add(_selectedSection = null);
},
);
}
});
}
}
});
}
This is my appDelegate.swift:
import UIKit
import Flutter
import GoogleMaps
#UIApplicationMain
#objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GMSServices.provideAPIKey("MyTopSecretApiKey")
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
My pubspec.yaml:
name: fishr
description: Mobile Fishing Guide
version: 1.0.0+5
environment:
sdk: ">=2.2.2 <3.0.0"
dependencies:
shared_preferences: ^2.0.6
flutter:
sdk: flutter
cupertino_icons: ^1.0.2
font_awesome_flutter: ^9.0.0
flutter_bloc: ^7.0.0
http: ^0.13.1
meta: ^1.1.6
equatable: ^2.0.0
get_it: ^7.1.3
google_maps_flutter: ^2.0.3
location: ^4.1.1
url_launcher: ^6.0.3
expansion_tile_card: ^2.0.0
settings_ui: ^1.0.0
simple_animations: ^3.1.1
popover: ^0.2.2
charts_flutter: ^0.10.0
syncfusion_flutter_charts: ^19.1.64
sliding_up_panel: ^2.0.0+1
flutter_speed_dial: ^3.0.5
flutter_native_splash: ^1.1.8+4
flutter_launcher_icons: ^0.9.0
lint: ^1.5.3
introduction_screen: ^2.1.0
buy_me_a_coffee_widget: ^2.0.0-nullsafety.0
material_floating_search_bar: ^0.3.4
dev_dependencies:
mockito: ^5.0.10
# test_coverage: ^0.4.2
bloc_test: ^8.0.0
flutter_test:
sdk: flutter
flutter_icons:
android: "launcher_icon"
ios: true
image_path: "assets/icon/icon.png"
flutter_native_splash:
color: "#ffffff"
image: "assets/icon/splash_regular.png"
color_dark: "#262626"
image_dark: "assets/icon/splash_dark.png"
android: true
ios: true
flutter:
uses-material-design: true
assets:
- assets/images/
- assets/icon/
- assets/water/
It was an Permission Issue. Adding the following permissions in the ios/info.plist solved the problem :)
<!-- Permission options for the `location` group -->
<key>NSLocationWhenInUseUsageDescription</key>
<string>Need location when in use</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Always and when in use!</string>
<key>NSLocationUsageDescription</key>
<string>Older devices need location.</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>Can I have location always?</string>
//Update your AppDelegate file as below
import UIKit
import Flutter
import GoogleMaps
#UIApplicationMain
#objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GMSServices.provideAPIKey("YOUR MAP API KEY")
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
Related
i am beginner in flutter. I try to make an app which should notify users on scheduled time. I decided to use flutter_local_notification.
(I am trying to implement this only IOS as first step so that is why code blocks related with android commented in.)
class NotificationHelper {
FlutterLocalNotificationsPlugin
flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
initializeNotification() async {
//tz.initializeTimeZones();
final IOSInitializationSettings initializationSettingsIOS =
IOSInitializationSettings(
requestSoundPermission: false,
requestBadgePermission: false,
requestAlertPermission: false,
onDidReceiveLocalNotification: onDidReceiveLocalNotification
);
// final Android InitializationSettings initializationSettingsAndroid =
// Android InitializationSettings("appicon");
final InitializationSettings initializationSettings =
InitializationSettings(
iOS: initializationSettingsIOS,
);
await flutterLocalNotificationsPlugin.initialize(
initializationSettings,
onSelectNotification: selectNotification);
}
onDidReceiveLocalNotification(
int id, String? title, String? body, String? payload) async {
// display a dialog with the notification details, tap ok to go to another page
// showDialog(
// //context: context,
// builder: (BuildContext context) => CupertinoAlertDialog(
// title: Text(title),
// content: Text(body),
// actions: [
// CupertinoDialogAction(
// isDefaultAction: true,
// child: Text('Ok'),
// onPressed: () async {
// Navigator.of(context, rootNavigator: true).pop();
// await Navigator.push(
// context,
// MaterialPageRoute(
// builder: (context) => SecondScreen(payload),
// ),
// );
// },
// )
// ],
// ),
// );
}
Future selectNotification(String? payload) async {
if (payload != null) {
print('notification payload: $payload');
} else {
print("Notification Done");
}
}
void requestIOSPermissions() {
flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
IOSFlutterLocalNotificationsPlugin>()
?.requestPermissions(
alert: true,
badge: true,
sound: true,
);
}
displayNotification({required String title, required String body}) async {
print("doing test");
const AndroidNotificationDetails androidPlatformChannelSpecifics =
AndroidNotificationDetails('your channel id', 'your channel name',
channelDescription: 'your channel description',
importance: Importance.max,
priority: Priority.high,
ticker: 'ticker');
const NotificationDetails platformChannelSpecifics =
NotificationDetails(android: androidPlatformChannelSpecifics);
flutterLocalNotificationsPlugin.show(
12345,
"A Notification From My Application",
"This notification was sent using Flutter Local Notifcations Package",
platformChannelSpecifics,
payload: 'data');
}
}
and i call the initializeNotification() function on my main.dart like this:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
NotificationHelper().requestIOSPermissions();
NotificationHelper().initializeNotification();
runApp(const MyApp());
}
and after user logged in,
I just try to see my notification so i call displayNotification() on a button like this:
IconButton(
icon: const Icon(Icons.settings),
onPressed: () {
NotificationHelper().displayNotification(title: 'title', body: 'hede');
},
color: Colors.white,
iconSize: 25,
),
and also i call this code in AppDelegate.swift
import UIKit
import Flutter
#UIApplicationMain
#objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
}
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
when application opens it successfully asks permissions for send notifications. After it allowed i just try to call displayNotification() but it doesn't work. I cannot see any notification on my simulator.
environment:
sdk: ">=2.15.1 <3.0.0"
flutter_local_notifications: ^9.3.3
What should i do ? Thank you for you all your answers.
I think notifications doesn't work currently on İOS simulator. You should try real ios device
When I send a notification from Firebase Cloud Messaging, the notification badge shows up on the app icon, but there is no code to remove it.
The count of notifications do not matter so I don't have to have any notification counter or whatever, so all I need to do is clear the notification badge when the app is opened.
How can I accomplish this without rewriting my entire code for notifications (I barely got it working)?
This is the code:
_fcm.getToken().then((token) {
print("The token is: " + token);
setState(() {
tokenSave = token;
});
});
_fcm.requestPermission(
sound: true,
badge: true,
alert: true,
provisional: false,
);
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
RemoteNotification notification = message.notification;
AndroidNotification android = message.notification?.android;
if (notification != null && android != null) {
flutterLocalNotificationsPlugin.show(
notification.hashCode,
notification.title,
notification.body,
NotificationDetails(
android: AndroidNotificationDetails(
channel.id,
channel.name,
channel.description,
color: Colors.blue,
playSound: true,
icon: null,
),
),
);
}
});
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
print("onMessageOpenedApp event");
RemoteNotification notification = message.notification;
AndroidNotification android = message.notification?.android;
if (notification != null && android != null) {
showDialog(
context: context,
builder: (_) {
return AlertDialog(
title: Text(notification.title),
content: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [Text(notification.body)],
),
),
);
});
}
});
}
Add the following function to your AppDelegate.swift, under the application() function:
override func applicationDidBecomeActive(_ application: UIApplication) {
application.applicationIconBadgeNumber = 0;
}
I've had the same problem before. My workaround was using the App badger package
With that package you can do the following in your main.dart or any other entrypoint of your application:
FlutterAppBadger.removeBadge();
or
FlutterAppBadger.updateBadgeCount(0);
When I triggers the pickimage function, it pops up the gallery view to select an image. And when I choose an image, the app returns to app view. But the pickimage function doesn't return, and hangs forever.
Here's my code:
import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:google_ml_kit/google_ml_kit.dart';
import 'package:image_picker/image_picker.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Text Scanning Demo'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
late String result;
File? _image;
InputImage? inputImage;
final ImagePicker picker = ImagePicker();
Future pickImageFromGallery() async {
print("starting get image");
final XFile? pickedFile = await picker.pickImage(source: ImageSource.gallery);
//final pickedFile = await picker.pickImage(source: ImageSource.gallery);
print("getting image.....");
setState(() {
if (pickedFile != null) {
print("file not null");
_image = File(pickedFile.path);
inputImage = InputImage.fromFilePath(pickedFile.path);
imageToText(inputImage);
} else {
print('No image selected.');
}
});
}
Future captureImageFromCamera() async {
final pickedFile = await picker.pickImage(source: ImageSource.camera);
setState(() {
if (pickedFile != null) {
_image = File(pickedFile.path);
inputImage = InputImage.fromFilePath(pickedFile.path);
imageToText(inputImage);
} else {
print('No image selected.');
}
});
}
Future imageToText(inputImage) async {
print("starting");
result = '';
final textDetector = GoogleMlKit.vision.textDetector();
print("loaded textDetector");
final RecognisedText recognisedText = await textDetector.processImage(inputImage);
print("loaded recognisedText");
setState(() {
String text = recognisedText.text;
for (TextBlock block in recognisedText.blocks) {
//each block of text/section of text
final String text = block.text;
print("block of text: ");
print(text);
for (TextLine line in block.lines) {
//each line within a text block
for (TextElement element in line.elements) {
//each word within a line
result += element.text + " ";
}
}
}
result += "\n\n";
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
width: 100,
child: TextField(
textAlign: TextAlign.center,
style: TextStyle(color: Colors.black),
),
),
Container(
height: 70,
width: 150,
child: TextButton(
style: TextButton.styleFrom(
primary: Colors.blue,
),
onPressed: () {
pickImageFromGallery();
},
child: Text('Pick Image'),
))
],
),
),
// This trailing comma makes auto-formatting nicer for build methods.
);
}
}
Here's my pubspec.yaml
name: text_scanning
description: A new Flutter project.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 1.0.0+1
environment:
sdk: ">=2.14.0-0 <3.0.0"
dependencies:
flutter:
sdk: flutter
font_awesome_flutter: ^9.0.0
splash_screen_view: ^3.0.0
cupertino_icons: ^1.0.2
google_ml_kit: ^0.7.3
image_picker: ^0.8.1+1
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^1.0.0
flutter:
uses-material-design: true
Here's podfile
# Uncomment this line to define a global platform for your project
platform :ios, '10.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
flutter_ios_podfile_setup
target 'Runner' do
use_frameworks!
use_modular_headers!
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
pod 'GoogleMLKit/TextRecognition'
pod 'Firebase'
end
post_install do |installer|
# add these lines:
installer.pods_project.build_configurations.each do |config|
config.build_settings["EXCLUDED_ARCHS[sdk=*]"] = "armv7"
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = $iOSVersion
end
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
# add these lines:
target.build_configurations.each do |config|
if Gem::Version.new($iOSVersion) > Gem::Version.new(config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'])
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = $iOSVersion
end
end
end
end
Here's screenshot of my Info from xcode:
[note the privacy for gallery and camera][1]
I am at my wits end here, I followed every documentation, and it just doesn't even produce any error, and just hangs there.
[1]: https://i.stack.imgur.com/XC6aP.png
Are you running this code on an iOS Simulator on an ARM based Mac (M1 / M1 Pro)?
If so, then the problem probably stems from a current bug in the iOS Simulator which only seems to reproduce when running with Rosetta translation (read more here, and here).
In your case, the pubspec.yaml file states google_ml_kit as a dependency, which forces the app to run using Rosetta, which in turn breaks the image_picker plugin.
Apple will probably fix this some time in the future, but in the meanwhile, you can workaround this issue by either running your app on a physical iOS device, or using an iOS Simulator with iOS version < 14.
To do so:
Open up Xcode
Go to Preferences.
Select the "Components" tab.
Mark the simulators you want.
Press "Check and Install Now".
Then:
Open up the Simulator app.
Select File > New Simulator
Select your device of choice, and make sure to select an iOS version < 14.
I'm using flutter webview to present the payment url in my app using the following class:
class YourWebView extends StatelessWidget {
String url;
bool isFinshed = false;
YourWebView(this.url);
final Completer<WebViewController> _controller =
Completer<WebViewController>();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('اكمال عملية الدفع..'),
leading: new IconButton(
icon: new Icon(Icons.close),
onPressed: () {
if(isFinshed) {
Provider.of<MatchProvider>(context, listen: false)
.getMyComingMatches();
Navigator.of(context).popUntil((route) => route.isFirst);
} else {
Navigator.pop(context);
}
}),
),
body: Builder(builder: (BuildContext context) {
return WebView(
initialUrl: Uri.encodeFull(url),
javascriptMode: JavascriptMode.unrestricted,
onWebViewCreated: (WebViewController webViewController) {
_controller.complete(webViewController);
},
debuggingEnabled: true,
onPageFinished: (String url) {
SystemChannels.textInput.invokeMethod('TextInput.hide');
if (url.contains("tap/check?tap_id")) {
isFinshed = true;
}
print('Page finished loading: $url');
},
gestureRecognizers: null,
// gestureNavigationEnabled: false
);
}));
}
The url looks like:
https://xxxxxx.com/tap/check?tap_id=chg_TS05162021120xxxxxxx
Everything is working on Android, but on IOS i get a blank screen and i see this error in xcode debug logs :
WebPageProxy::didFailProvisionalLoadForFrame
I have tried to run another urls on the webview and it was working, but the payment url isn't, even though it's working on Android or other browsers.
I think you maybe tried to load a http link in webview instead of https. In that case you should add the following in your info.plist file.
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key><true/>
</dict>
I am not familiar with Flutter, but on iOS, if the system's process for rendering the web content terminated, the webview would be blank, the flutter plugin is handling the situation in here, you may have to check the error and do a reload.
I'm building a Flutter app with Android Studio (a Time Tracker, following a course on Udemy) and I am at the stage where I have created a sign-in page, that allows me to sign in using either Google, Facebook, email or 'going anonymous'. I'm using version 2.0.1 of the flutter_facebook_login plugin, since the latest version, version 3.0.0, generates lots of errors related to Cocoapods. Version 2.0.1 resolves all of those errors.
I'm doing all authentication using Flutter's firebase_auth package, so that a unique user ID can be generated, to control what is seen by each user. The sign-in process is split into two different pages. There's an 'auth.dart' page that handles all of the authorisation work, with Firebase, Google and Facebook etc. It looks like this:
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:flutter_facebook_login/flutter_facebook_login.dart';
import 'package:google_sign_in/google_sign_in.dart';
class User {
User({#required this.uid});
final String uid;
}
abstract class AuthBase {
Stream<User> get onAuthStateChanged;
Future<User> currentUser();
Future<User> signInAnonymously();
Future<User> signInWithGoogle();
Future<User> signInWithFacebook();
Future<void> signOut();
}
class Auth implements AuthBase {
final _firebaseAuth = FirebaseAuth.instance;
User _userFromFirebase(FirebaseUser user) {
if (user == null) {
return null;
}
return User(uid: user.uid);
}
#override
Stream<User> get onAuthStateChanged {
return _firebaseAuth.onAuthStateChanged.map(_userFromFirebase);
}
#override
Future<User> currentUser() async {
final user = await _firebaseAuth.currentUser();
return _userFromFirebase(user);
}
#override
Future<User> signInAnonymously() async {
final authResult = await _firebaseAuth.signInAnonymously();
return _userFromFirebase(authResult.user);
}
#override
Future<User> signInWithGoogle() async {
final googleSignIn = GoogleSignIn();
final googleAccount = await googleSignIn.signIn();
if (googleAccount != null) {
final googleAuth = await googleAccount.authentication;
if (googleAuth.accessToken != null && googleAuth.idToken != null) {
final authResult = await _firebaseAuth.signInWithCredential(
GoogleAuthProvider.getCredential(
idToken: googleAuth.idToken,
accessToken: googleAuth.accessToken,
),
);
return _userFromFirebase(authResult.user);
} else {
throw PlatformException(
code: 'ERROR_MISSING_GOOGLE_AUTH_TOKEN',
message: 'Missing Google Auth Token',
);
}
} else {
throw PlatformException(
code: 'ERROR_ABORTED_BY_USER',
message: 'Sign in aborted by user',
);
}
}
#override
Future<User> signInWithFacebook() async {
final facebookLogin = FacebookLogin();
final result = await facebookLogin.logInWithReadPermissions(
['public_profile'],
);
if (result.accessToken != null) {
final authResult = await _firebaseAuth
.signInWithCredential(FacebookAuthProvider.getCredential(
accessToken: result.accessToken.token,
));
return _userFromFirebase(authResult.user);
} else {
throw PlatformException(
code: 'ERROR_ABORTED_BY_USER',
message: 'Sign in aborted by user',
);
}
}
#override
Future<void> signOut() async {
final googleSignIn = GoogleSignIn();
await googleSignIn.signOut();
final facebookLogin = FacebookLogin();
await facebookLogin.logOut();
await _firebaseAuth.signOut();
}
}
Then, the sign-in page, with all of the buttons and interactions with Google and Facebook etc. looks like this:
import 'package:flutter/material.dart';
import 'package:time_tracker_flutter_course/app/sign_in/sign_in_button.dart';
import 'package:time_tracker_flutter_course/app/sign_in/social_sign_in_button.dart';
import 'package:time_tracker_flutter_course/services/auth.dart';
class SignInPage extends StatelessWidget {
SignInPage({#required this.auth});
final AuthBase auth;
Future<void> _signInAnonymously() async {
try {
await auth.signInAnonymously();
} catch (e) {
print(e.toString());
}
}
Future<void> _signInWithGoogle() async {
try {
await auth.signInWithGoogle();
} catch (e) {
print(e.toString());
}
}
Future<void> _signInWithFacebook() async {
try {
await auth.signInWithFacebook();
} catch (e) {
print(e.toString());
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Time Tracker'),
elevation: 2.0,
),
body: _buildContent(),
backgroundColor: Colors.grey[200],
);
}
Widget _buildContent() {
return Padding(
padding: EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Text(
'Sign In',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 32.0,
fontWeight: FontWeight.w600,
),
),
SizedBox(height: 48.0),
SocialSignInButton(
assetName: 'images/google-logo.png',
text: 'Sign in with Google',
textColor: Colors.black87,
color: Colors.white,
onPressed: _signInWithGoogle,
),
SizedBox(height: 8.0),
SocialSignInButton(
assetName: 'images/facebook-logo.png',
text: 'Sign in with Facebook',
textColor: Colors.white,
color: Color(0xFF334D92),
onPressed: _signInWithFacebook,
),
SizedBox(height: 8.0),
SignInButton(
text: 'Sign in with email',
textColor: Colors.white,
color: Colors.teal[700],
onPressed: () {},
),
SizedBox(height: 8.0),
Text(
'or',
style: TextStyle(fontSize: 14.0, color: Colors.black87),
textAlign: TextAlign.center,
),
SizedBox(height: 8.0),
SignInButton(
text: 'Go anonymous',
textColor: Colors.black,
color: Colors.lime[300],
onPressed: _signInAnonymously,
),
],
),
);
}
}
All this code and methodology works perfectly in most cases, which includes:
Android simulator with anonymous login, Google AND Facebook
iOS simulator with anonymous login and Google ONLY
When I try and log in with the Facebook method on the iOS simulator in Android Studio, that's where I run into problems. In the Android Studio console, an error is 'spat out':
flutter: PlatformException(ERROR_ABORTED_BY_USER, Sign in aborted by user, null)
You'll see from the first block of code (the 'auth.dart' code) that this error is just a generic one that I have built in - I haven't been specific with it at all.
I don't believe the issue is with the flutter_facebook_login plugin, since it still works for Android, unless the plug-in has problems that are unique to iOS. I think there's an issue with the iOS set-up for Facebook, even though I have followed the instructions to the letter, including with Xcode.
Can someone help me to understand what might be causing this error, and how I can sort it? It is the only thing in the set-up that you can see that isn't working at the moment, across both simulator platforms.
I had the same issue, I think it is an issue of facebook api with ios beta version.
I found a work around. This is only a work around not the actual solution. It works for me and I hope this helps you:-
The work around checking when the status goes to FacebookLoginStatus.cancelledByUser, then using the below
facebookLogin.loginBehavior = FacebookLoginBehavior.webViewOnly;
It will force flutter to open facebook auth in webview and then you can get it working.
Have a look at the full method
Future signInWithFaceBook() async{
var facebookLogin = new FacebookLogin();
var result = await facebookLogin.logInWithReadPermissions(['email', 'public_profile']);
switch (result.status) {
case FacebookLoginStatus.loggedIn:
print(result.accessToken.token);
// Add your route to home page here after sign In
break;
case FacebookLoginStatus.cancelledByUser:
// In your case the program flow will go here as it as a bug with the api I suppose
facebookLogin.loginBehavior = FacebookLoginBehavior.webViewOnly;
// Once the code comes here the above line will force flutter to open facebook auth in a webview
result = await facebookLogin.logInWithReadPermissions(['email', 'public_profile']);
if(result.status==FacebookLoginStatus.loggedIn){
FirebaseUser user = (await _auth.signInWithCredential(FacebookAuthProvider.getCredential(accessToken: result.accessToken.token)
)
).user;
final FirebaseUser currentUser = await _auth.currentUser();
assert(user.uid == currentUser.uid);
// Add your home page here
}
print('CANCELED BY USER');
break;
case FacebookLoginStatus.error:
print(result.errorMessage);
break;
}
}
Update your auth.dart Code with one line of code from the below .It works.
#override
Future <User> signInWithFacebook() async {
final facebookLogin = FacebookLogin();
facebookLogin.LoginBehavior = FacebookLoginBehavior.webViewOnly;
final result = await facebookLogin.logInWithReadPermissions(['public_profile'],);
if (result.accessToken != null) {
final authResult = await _firebaseAuth
.signInWithCredential(FacebookAuthProvider.getCredential(
accessToken: result.accessToken.token,
)
);
return _userFromFirebase(authResult.user);
} else {
throw PlatformException(
code: 'ERROR_ABORTED_BY_USER',
message: 'Sign in aborted by user',
);
}}