添加依賴:
name: travel_note
description: A new Flutter application.
version: 1.0.0+1
environment:
sdk: ">=2.7.0 <3.0.0"
dependencies:
flutter:
sdk: flutter
material_design_icons_flutter: ^4.0.5655
cupertino_icons: ^1.0.0
firebase_auth: ^0.18.0+1
google_sign_in: ^4.5.4
flutter_facebook_auth: ^0.2.3
apple_sign_in: ^0.1.0
random_string: ^2.1.0
provider: ^4.3.2
# Add the dependency for the FlutterFire plugin for Google Analytics
firebase_analytics: ^5.0.2
firebase_core: ^0.5.0
dev_dependencies:
flutter_test:
sdk: flutter
flutter:
uses-material-design: true
assets:
- assets/images/
auth_service.dart
:
import 'dart:async';
import 'package:apple_sign_in/apple_sign_in.dart';
import 'package:meta/meta.dart';
@immutable
class User {
const User({
@required this.uid,
this.email,
this.photoURL,
this.displayName,
});
final String uid;
final String email;
final String photoURL;
final String displayName;
}
abstract class AuthService {
User currentUser();
Future<User> signInWithEmailAndPassword(String email, String password);
Future<User> createUserWithEmailAndPassword(String email, String password);
Future<void> sendPasswordResetEmail(String email);
Future<User> signInWithGoogle();
Future<User> signInWithFacebook();
Future<User> signInWithApple({List<Scope> scopes});
Future<void> signOut();
Stream<User> get onAuthStateChanged;
void dispose();
}
firebase_auth_service.dart
:
import 'package:apple_sign_in/apple_sign_in.dart';
import 'package:firebase_auth/firebase_auth.dart' as firebase_auth;
import 'package:flutter/services.dart';
import 'package:flutter_facebook_auth/flutter_facebook_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'auth_service.dart';
class FirebaseAuthService implements AuthService {
final firebase_auth.FirebaseAuth _firebaseAuth =
firebase_auth.FirebaseAuth.instance;
User _userFromFirebase(firebase_auth.User user) {
if (user == null) {
return null;
}
return User(
uid: user.uid,
email: user.email,
displayName: user.displayName,
photoURL: user.photoURL,
);
}
@override
Stream<User> get onAuthStateChanged {
return _firebaseAuth.authStateChanges().map(_userFromFirebase);
}
@override
Future<User> signInWithEmailAndPassword(String email, String password) async {
final authResult = await _firebaseAuth.signInWithEmailAndPassword(
email: email,
password: password,
);
return _userFromFirebase(authResult.user);
}
@override
Future<User> createUserWithEmailAndPassword(
String email, String password) async {
final authResult = await _firebaseAuth.createUserWithEmailAndPassword(
email: email,
password: password,
);
return _userFromFirebase(authResult.user);
}
@override
Future<void> sendPasswordResetEmail(String email) async {
await _firebaseAuth.sendPasswordResetEmail(email: email);
}
@override
Future<User> signInWithGoogle() async {
final GoogleSignIn googleSignIn = GoogleSignIn();
final GoogleSignInAccount googleUser = await googleSignIn.signIn();
if (googleUser != null) {
final GoogleSignInAuthentication googleAuth =
await googleUser.authentication;
if (googleAuth.accessToken != null && googleAuth.idToken != null) {
final authResult = await _firebaseAuth
.signInWithCredential(firebase_auth.GoogleAuthProvider.credential(
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 LoginResult loginResult = await FacebookAuth.instance.login();
final credential = firebase_auth.FacebookAuthProvider.credential(
loginResult.accessToken.token);
if (loginResult.accessToken != null &&
loginResult.accessToken.token != null) {
final authResult = await _firebaseAuth.signInWithCredential(
credential,
);
return _userFromFirebase(authResult.user);
} else {
throw PlatformException(
code: 'ERROR_ABORTED_BY_USER', message: 'Sign in aborted by user');
}
}
@override
Future<User> signInWithApple({List<Scope> scopes = const []}) async {
final AuthorizationResult result = await AppleSignIn.performRequests(
[AppleIdRequest(requestedScopes: scopes)]);
switch (result.status) {
case AuthorizationStatus.authorized:
final appleIdCredential = result.credential;
final oAuthProvider = firebase_auth.OAuthProvider('apple.com');
final credential = oAuthProvider.credential(
idToken: String.fromCharCodes(appleIdCredential.identityToken),
accessToken:
String.fromCharCodes(appleIdCredential.authorizationCode),
);
final authResult = await _firebaseAuth.signInWithCredential(credential);
final firebaseUser = authResult.user;
if (scopes.contains(Scope.fullName)) {
await firebaseUser.updateProfile(
displayName:
'${appleIdCredential.fullName.givenName} ${appleIdCredential.fullName.familyName}');
}
return _userFromFirebase(firebaseUser);
case AuthorizationStatus.error:
throw PlatformException(
code: 'ERROR_AUTHORIZATION_DENIED',
message: result.error.toString(),
);
case AuthorizationStatus.cancelled:
throw PlatformException(
code: 'ERROR_ABORTED_BY_USER',
message: 'Sign in aborted by user',
);
}
return null;
}
@override
User currentUser() {
return _userFromFirebase(_firebaseAuth.currentUser);
}
@override
void dispose() {}
@override
Future<void> signOut() async {
final GoogleSignIn googleSignIn = GoogleSignIn();
await googleSignIn.signOut();
await FacebookAuth.instance.logOut();
return _firebaseAuth.signOut();
}
}
mock_auth_service.dart
:
import 'dart:async';
import 'package:apple_sign_in/apple_sign_in.dart';
import 'package:flutter/services.dart';
import 'package:meta/meta.dart';
import 'package:random_string/random_string.dart' as random;
import 'auth_service.dart';
class MockAuthService implements AuthService {
MockAuthService({
this.startupTime = const Duration(milliseconds: 250),
this.responseTime = const Duration(seconds: 2),
}) {
Future<void>.delayed(responseTime).then((_) {
_add(null);
});
}
final Duration startupTime;
final Duration responseTime;
final Map<String, _UserData> _usersStore = <String, _UserData>{};
User _currentUser;
final StreamController<User> _onAuthStateChangedController =
StreamController<User>();
@override
Stream<User> get onAuthStateChanged => _onAuthStateChangedController.stream;
@override
User currentUser() {
return _currentUser;
}
@override
Future<User> createUserWithEmailAndPassword(
String email, String password) async {
await Future<void>.delayed(responseTime);
if (_usersStore.keys.contains(email)) {
throw PlatformException(
code: 'ERROR_EMAIL_ALREADY_IN_USE',
message: 'The email address is already registered. Sign in instead?',
);
}
final User user = User(uid: random.randomAlphaNumeric(32), email: email);
_usersStore[email] = _UserData(password: password, user: user);
_add(user);
return user;
}
@override
Future<User> signInWithEmailAndPassword(String email, String password) async {
await Future<void>.delayed(responseTime);
if (!_usersStore.keys.contains(email)) {
throw PlatformException(
code: 'ERROR_USER_NOT_FOUND',
message: 'The email address is not registered. Need an account?',
);
}
final _UserData _userData = _usersStore[email];
if (_userData.password != password) {
throw PlatformException(
code: 'ERROR_WRONG_PASSWORD',
message: 'The password is incorrect. Please try again.',
);
}
_add(_userData.user);
return _userData.user;
}
@override
Future<void> sendPasswordResetEmail(String email) async {}
@override
Future<void> signOut() async {
_add(null);
}
@override
Future<User> signInWithFacebook() async {
await Future<void>.delayed(responseTime);
final User user = User(uid: random.randomAlphaNumeric(32));
_add(user);
return user;
}
@override
Future<User> signInWithGoogle() async {
await Future<void>.delayed(responseTime);
final User user = User(uid: random.randomAlphaNumeric(32));
_add(user);
return user;
}
@override
Future<User> signInWithApple({List<Scope> scopes}) async {
await Future<void>.delayed(responseTime);
final User user = User(uid: random.randomAlphaNumeric(32));
_add(user);
return user;
}
@override
void dispose() {
_onAuthStateChangedController.close();
}
void _add(User user) {
_currentUser = user;
_onAuthStateChangedController.add(user);
}
}
class _UserData {
_UserData({@required this.password, @required this.user});
final String password;
final User user;
}
apple_sign_in_available.dart
:
import 'package:apple_sign_in/apple_sign_in.dart';
class AppleSignInAvailable {
AppleSignInAvailable(this.isAvailable);
final bool isAvailable;
static Future<AppleSignInAvailable> check() async {
return AppleSignInAvailable(await AppleSignIn.isAvailable());
}
}
auth_service_adapter.dart
:
import 'dart:async';
import 'package:apple_sign_in/apple_sign_in.dart';
import 'package:flutter/material.dart';
import 'package:meta/meta.dart';
import 'auth_service.dart';
import 'firebase_auth_service.dart';
import 'mock_auth_service.dart';
enum AuthServiceType { firebase, mock }
class AuthServiceAdapter implements AuthService {
AuthServiceAdapter({@required AuthServiceType initialAuthServiceType})
: authServiceTypeNotifier =
ValueNotifier<AuthServiceType>(initialAuthServiceType) {
_setup();
}
final FirebaseAuthService _firebaseAuthService = FirebaseAuthService();
final MockAuthService _mockAuthService = MockAuthService();
// Value notifier used to switch between [FirebaseAuthService] and [MockAuthService]
final ValueNotifier<AuthServiceType> authServiceTypeNotifier;
AuthServiceType get authServiceType => authServiceTypeNotifier.value;
AuthService get authService => authServiceType == AuthServiceType.firebase
? _firebaseAuthService
: _mockAuthService;
final StreamController<User> _onAuthStateChangedController =
StreamController<User>.broadcast();
StreamSubscription<User> _firebaseAuthSubscription;
StreamSubscription<User> _mockAuthSubscription;
@override
Stream<User> get onAuthStateChanged => _onAuthStateChangedController.stream;
@override
User currentUser() => authService.currentUser();
@override
Future<User> createUserWithEmailAndPassword(String email, String password) =>
authService.createUserWithEmailAndPassword(email, password);
@override
Future<User> signInWithEmailAndPassword(String email, String password) =>
authService.signInWithEmailAndPassword(email, password);
@override
Future<void> sendPasswordResetEmail(String email) =>
authService.sendPasswordResetEmail(email);
@override
Future<User> signInWithFacebook() => authService.signInWithFacebook();
@override
Future<User> signInWithGoogle() => authService.signInWithGoogle();
@override
Future<User> signInWithApple({List<Scope> scopes}) =>
authService.signInWithApple();
@override
Future<void> signOut() => authService.signOut();
@override
void dispose() {
_firebaseAuthSubscription?.cancel();
_mockAuthSubscription?.cancel();
_onAuthStateChangedController?.close();
_mockAuthService.dispose();
authServiceTypeNotifier.dispose();
}
void _setup() {
// Observable<User>.merge was considered here, but we need more fine grained control to ensure
// that only events from the currently active service are processed
_firebaseAuthSubscription =
_firebaseAuthService.onAuthStateChanged.listen((User user) {
if (authServiceType == AuthServiceType.firebase) {
_onAuthStateChangedController.add(user);
}
}, onError: (dynamic error) {
if (authServiceType == AuthServiceType.firebase) {
_onAuthStateChangedController.addError(error);
}
});
_mockAuthSubscription =
_mockAuthService.onAuthStateChanged.listen((User user) {
if (authServiceType == AuthServiceType.mock) {
_onAuthStateChangedController.add(user);
}
}, onError: (dynamic error) {
if (authServiceType == AuthServiceType.mock) {
_onAuthStateChangedController.addError(error);
}
});
}
}
constants.dart
:
新增
...
class Keys {
static const String logout = 'logout';
static const String alertDefault = 'alertDefault';
static const String alertCancel = 'alertCancel';
}