main,dart
:
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'components/auth_widget.dart';
import 'components/auth_widget_builder.dart';
import 'services/apple_sign_in_available.dart';
import 'services/auth_service.dart';
import 'services/auth_service_adapter.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
final appleSignInAvailable = await AppleSignInAvailable.check();
runApp(MyApp(appleSignInAvailable: appleSignInAvailable));
}
class MyApp extends StatelessWidget {
const MyApp(
{this.initialAuthServiceType = AuthServiceType.firebase,
this.appleSignInAvailable});
final AuthServiceType initialAuthServiceType;
final AppleSignInAvailable appleSignInAvailable;
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
Provider<AppleSignInAvailable>.value(value: appleSignInAvailable),
Provider<AuthService>(
create: (_) => AuthServiceAdapter(
initialAuthServiceType: initialAuthServiceType,
),
dispose: (_, AuthService authService) => authService.dispose(),
),
],
child: AuthWidgetBuilder(
builder: (BuildContext context, AsyncSnapshot<User> userSnapshot) {
return MaterialApp(
theme: ThemeData(primarySwatch: Colors.indigo),
home: AuthWidget(userSnapshot: userSnapshot),
);
}),
);
}
}
login_manager.dart
:
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:meta/meta.dart';
import 'package:travel_note/services/auth_service.dart';
class LoginManager {
LoginManager({@required this.auth, @required this.isLoading});
final AuthService auth;
final ValueNotifier<bool> isLoading;
Future<void> loginWithEmailAndPassword(
{@required String email, @required String password}) async {
try {
isLoading.value = true;
return await auth.loginWithEmailAndPassword(email, password);
} catch (e) {
isLoading.value = false;
rethrow;
}
}
}
body.dart
:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:travel_note/components/no_account_text.dart';
import 'package:travel_note/components/social_media.dart';
import 'package:travel_note/components/social_media_manager.dart';
import 'package:travel_note/services/auth_service.dart';
import '../../../constants.dart';
import '../../../size_config.dart';
import '../login_manager.dart';
import 'login_form.dart';
class Body extends StatelessWidget {
@override
Widget build(BuildContext context) {
final AuthService auth = Provider.of<AuthService>(context, listen: false);
return ChangeNotifierProvider<ValueNotifier<bool>>(
create: (_) => ValueNotifier<bool>(false),
child: Consumer<ValueNotifier<bool>>(
builder: (_, ValueNotifier<bool> isLoading, __) => MultiProvider(
providers: [
Provider<LoginManager>(
create: (_) => LoginManager(auth: auth, isLoading: isLoading),
),
Provider<SocialMediaManager>(
create: (_) =>
SocialMediaManager(auth: auth, isLoading: isLoading),
),
],
child: SingleChildScrollView(
child: Padding(
padding: EdgeInsets.symmetric(
horizontal: getProportionateScreenWidth(25)),
child: Column(
children: [
Text(
"Welcome to Travel Note",
style: headingStyle,
),
VerticalSpacing(of: 16),
Text(
'Log in with your email and password \nor continue with social media',
textAlign: TextAlign.left,
style: contentStyle,
),
VerticalSpacing(of: 25),
Consumer<LoginManager>(
builder: (_, LoginManager manager, __) => LoginForm.set(
isLoading: isLoading.value,
manager: manager,
),
),
VerticalSpacing(of: 25),
Consumer<SocialMediaManager>(
builder: (_, SocialMediaManager manager, __) =>
SocialMedia.set(
isLoading: isLoading.value,
manager: manager,
),
),
VerticalSpacing(of: 25),
NoAccountText(),
VerticalSpacing(of: 25),
],
),
),
),
),
),
);
}
}
login_form.dart
:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
import 'package:travel_note/components/default_button.dart';
import 'package:travel_note/components/form_error.dart';
import 'package:travel_note/components/platform_exception_alert_dialog.dart';
import 'package:travel_note/screens/forgot_password/forgot_password_screen.dart';
import 'package:travel_note/screens/login/login_manager.dart';
import '../../../constants.dart';
import '../../../size_config.dart';
class LoginForm extends StatefulWidget {
final LoginManager manager;
final bool isLoading;
LoginForm.set({Key key, this.isLoading, this.manager}) : super(key: key);
@override
_LoginFormState createState() => _LoginFormState.set(isLoading, manager);
}
class _LoginFormState extends State<LoginForm> {
_LoginFormState.set(this.isLoading, this.manager);
final LoginManager manager;
final bool isLoading;
final _formKey = GlobalKey<FormState>();
String email;
String password;
bool remember = false;
final List<String> errors = [];
void addError({String error}) {
if (!errors.contains(error))
setState(() {
errors.add(error);
});
}
void removeError({String error}) {
if (errors.contains(error))
setState(() {
errors.remove(error);
});
}
@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
children: [
buildEmailFormField(),
VerticalSpacing(of: 25),
buildPasswordFormField(),
VerticalSpacing(of: 25),
Row(
children: [
Checkbox(
value: remember,
activeColor: kPrimaryColor,
onChanged: (value) {
setState(() {
remember = value;
});
},
),
Text("Remember me"),
Spacer(),
GestureDetector(
onTap: () {
Navigator.pushNamed(context, ForgotPasswordScreen.routeName);
},
child: Text(
"Forgot Password",
style: TextStyle(decoration: TextDecoration.underline),
),
)
],
),
FormError(errors: errors),
VerticalSpacing(of: 25),
DefaultButton(
text: "Login",
press: isLoading
? null
: () {
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
_loginWithEmailAndPassword(context, email, password);
}
},
),
],
),
);
}
TextFormField buildPasswordFormField() {
return TextFormField(
obscureText: true,
onSaved: (newValue) => password = newValue,
onChanged: (value) {
if (value.isNotEmpty) {
removeError(error: kPasswordNullError);
}
if (value.length >= 8) {
removeError(error: kShortPasswordError);
}
},
validator: (value) {
if (value.isEmpty) {
addError(error: kPasswordNullError);
removeError(error: kShortPasswordError);
return "";
} else if (value.length < 8) {
addError(error: kShortPasswordError);
return "";
}
return null;
},
decoration: InputDecoration(
labelText: "Password",
hintText: "Enter your password",
floatingLabelBehavior: FloatingLabelBehavior.always,
suffixIcon: Icon(
MdiIcons.fromString("lock-outline"),
),
),
);
}
TextFormField buildEmailFormField() {
return TextFormField(
keyboardType: TextInputType.emailAddress,
onSaved: (newValue) => email = newValue,
onChanged: (value) {
if (value.isNotEmpty) {
removeError(error: kEmailNullError);
}
if (emailValidatorRegExp.hasMatch(value)) {
removeError(error: kInvalidEmailError);
}
},
validator: (value) {
if (value.isEmpty) {
addError(error: kEmailNullError);
removeError(error: kInvalidEmailError);
return "";
} else if (!emailValidatorRegExp.hasMatch(value)) {
addError(error: kInvalidEmailError);
return "";
}
return null;
},
decoration: InputDecoration(
labelText: "Email",
hintText: "Enter your email",
floatingLabelBehavior: FloatingLabelBehavior.always,
suffixIcon: Icon(
MdiIcons.fromString("email-outline"),
),
),
);
}
Future<void> _loginWithEmailAndPassword(
BuildContext context, String email, String password) async {
try {
await manager.loginWithEmailAndPassword(email: email, password: password);
} on PlatformException catch (e) {
if (e.code != 'ERROR_ABORTED_BY_USER') {
_showLoginError(context, e);
}
}
}
Future<void> _showLoginError(
BuildContext context, PlatformException exception) async {
await PlatformExceptionAlertDialog(
title: 'Login failed',
exception: exception,
).show(context);
}
}
其他註冊頁、忘記密碼頁,動作都類似,接下來可以先自己嘗試看看