如何使用带有 firebase 的提供程序添加 CircularProgressIndicator
how to add CircularProgressIndicator using provider with firebase
我经历了 flutter firebase
的 google code lab 使用 provider
包一切正常,正如代码实验室中提到的,我喜欢他们设计 Authentication
小部件传递函数的方式,在下面的代码中我想在等待状态下添加 CircularProgressIndicator,我不明白我在哪里添加 isLoading 的条件等于 true 然后 wait
否则 dosomething
函数传递 Authentication
小部件。
main.dart
import 'package:firebase_core/firebase_core.dart'; // new
import 'package:firebase_auth/firebase_auth.dart'; // new
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:provider/provider.dart'; // new
import 'src/authentication.dart'; // new
import 'src/widgets.dart';
void main() {
// Modify from here
runApp(
ChangeNotifierProvider(
create: (context) => ApplicationState(),
builder: (context, _) => App(),
),
);
// to here.
}
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Firebase Meetup',
theme: ThemeData(
buttonTheme: Theme.of(context).buttonTheme.copyWith(
highlightColor: Colors.deepPurple,
),
primarySwatch: Colors.deepPurple,
textTheme: GoogleFonts.robotoTextTheme(
Theme.of(context).textTheme,
),
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: HomePage(),
);
}
}
class HomePage extends StatelessWidget {
HomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Firebase Meetup'),
),
body: ListView(
children: <Widget>[
Image.asset('assets/codelab.png'),
SizedBox(height: 8),
IconAndDetail(Icons.calendar_today, 'October 30'),
IconAndDetail(Icons.location_city, 'San Francisco'),
// Add from here
Consumer<ApplicationState>(
builder: (context, appState, _) => Authentication(
email: appState.email,
loginState: appState.loginState,
startLoginFlow: appState.startLoginFlow,
verifyEmail: appState.verifyEmail,
signInWithEmailAndPassword: appState.signInWithEmailAndPassword,
cancelRegistration: appState.cancelRegistration,
registerAccount: appState.registerAccount,
signOut: appState.signOut,
),
),
// to here
Divider(
height: 8,
thickness: 1,
indent: 8,
endIndent: 8,
color: Colors.grey,
),
Header("What we'll be doing"),
Paragraph(
'Join us for a day full of Firebase Workshops and Pizza!',
),
],
),
);
}
}
class ApplicationState extends ChangeNotifier {
ApplicationState() {
init();
}
Future<void> init() async {
await Firebase.initializeApp();
FirebaseAuth.instance.userChanges().listen((user) {
if (user != null) {
_loginState = ApplicationLoginState.loggedIn;
} else {
_loginState = ApplicationLoginState.loggedOut;
}
notifyListeners();
});
}
ApplicationLoginState _loginState = ApplicationLoginState.loggedOut;
ApplicationLoginState get loginState => _loginState;
String? _email;
String? get email => _email;
void startLoginFlow() {
_loginState = ApplicationLoginState.emailAddress;
notifyListeners();
}
void verifyEmail(
String email,
void Function(FirebaseAuthException e) errorCallback,
) async {
try {
var methods = await FirebaseAuth.instance.fetchSignInMethodsForEmail(email);
if (methods.contains('password')) {
_loginState = ApplicationLoginState.password;
} else {
_loginState = ApplicationLoginState.register;
}
_email = email;
notifyListeners();
} on FirebaseAuthException catch (e) {
errorCallback(e);
}
}
void signInWithEmailAndPassword(
String email,
String password,
void Function(FirebaseAuthException e) errorCallback,
) async {
try {
await FirebaseAuth.instance.signInWithEmailAndPassword(
email: email,
password: password,
);
} on FirebaseAuthException catch (e) {
errorCallback(e);
}
}
void cancelRegistration() {
_loginState = ApplicationLoginState.emailAddress;
notifyListeners();
}
void registerAccount(String email, String displayName, String password,
void Function(FirebaseAuthException e) errorCallback) async {
try {
var credential = await FirebaseAuth.instance
.createUserWithEmailAndPassword(email: email, password: password);
await credential.user!.updateProfile(displayName: displayName);
} on FirebaseAuthException catch (e) {
errorCallback(e);
}
}
void signOut() {
FirebaseAuth.instance.signOut();
/// here is not notifylistener();
}
}
authentication.dart
import 'package:flutter/material.dart';
import 'widgets.dart';
enum ApplicationLoginState {
loggedOut,
emailAddress,
register,
password,
loggedIn,
}
class Authentication extends StatelessWidget {
const Authentication({
required this.loginState,
required this.email,
required this.startLoginFlow,
required this.verifyEmail,
required this.signInWithEmailAndPassword,
required this.cancelRegistration,
required this.registerAccount,
required this.signOut,
});
final ApplicationLoginState loginState;
final String? email;
final void Function() startLoginFlow;
final void Function(
String email,
void Function(Exception e) error,
) verifyEmail;
final void Function(
String email,
String password,
void Function(Exception e) error,
) signInWithEmailAndPassword;
final void Function() cancelRegistration;
final void Function(
String email,
String displayName,
String password,
void Function(Exception e) error,
) registerAccount;
final void Function() signOut;
@override
Widget build(BuildContext context) {
switch (loginState) {
case ApplicationLoginState.loggedOut:
return Row(
children: [
Padding(
padding: const EdgeInsets.only(left: 24, bottom: 8),
child: StyledButton(
onPressed: () {
startLoginFlow();
},
child: const Text('RSVP'),
),
),
],
);
case ApplicationLoginState.emailAddress:
return EmailForm(
callback: (email) =>
verifyEmail(email, (e) => _showErrorDialog(context, 'Invalid email', e)));
case ApplicationLoginState.password:
return PasswordForm(
email: email!,
login: (email, password) {
print("CONTEXT $context");
signInWithEmailAndPassword(
email, password, (e) => _showErrorDialog(context, 'Failed to sign in', e));
},
);
case ApplicationLoginState.register:
return RegisterForm(
email: email!,
cancel: () {
cancelRegistration();
},
registerAccount: (
email,
displayName,
password,
) {
registerAccount(email, displayName, password,
(e) => _showErrorDialog(context, 'Failed to create account', e));
},
);
case ApplicationLoginState.loggedIn:
return Row(
children: [
Padding(
padding: const EdgeInsets.only(left: 24, bottom: 8),
child: StyledButton(
onPressed: () {
signOut();
},
child: const Text('LOGOUT'),
),
),
],
);
default:
return Row(
children: const [
Text("Internal error, this shouldn't happen..."),
],
);
}
}
void _showErrorDialog(BuildContext context, String title, Exception e) {
print("CONTEXT $context");
showDialog<void>(
context: context,
builder: (context) {
return AlertDialog(
title: Text(
title,
style: const TextStyle(fontSize: 24),
),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
Text(
'${(e as dynamic).message}',
style: const TextStyle(fontSize: 18),
),
],
),
),
actions: <Widget>[
StyledButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text(
'OK',
style: TextStyle(color: Colors.deepPurple),
),
),
],
);
},
);
}
}
class EmailForm extends StatefulWidget {
const EmailForm({required this.callback});
final void Function(String email) callback;
@override
_EmailFormState createState() => _EmailFormState();
}
class _EmailFormState extends State<EmailForm> {
final _formKey = GlobalKey<FormState>(debugLabel: '_EmailFormState');
final _controller = TextEditingController();
@override
Widget build(BuildContext context) {
return Column(
children: [
const Header('Sign in with email'),
Padding(
padding: const EdgeInsets.all(8.0),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: TextFormField(
controller: _controller,
decoration: const InputDecoration(
hintText: 'Enter your email',
),
validator: (value) {
if (value!.isEmpty) {
return 'Enter your email address to continue';
}
return null;
},
),
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 30),
child: StyledButton(
onPressed: () async {
if (_formKey.currentState!.validate()) {
widget.callback(_controller.text);
}
},
child: const Text('NEXT'),
),
),
],
),
],
),
),
),
],
);
}
}
class RegisterForm extends StatefulWidget {
const RegisterForm({
required this.registerAccount,
required this.cancel,
required this.email,
});
final String email;
final void Function(String email, String displayName, String password) registerAccount;
final void Function() cancel;
@override
_RegisterFormState createState() => _RegisterFormState();
}
class _RegisterFormState extends State<RegisterForm> {
final _formKey = GlobalKey<FormState>(debugLabel: '_RegisterFormState');
final _emailController = TextEditingController();
final _displayNameController = TextEditingController();
final _passwordController = TextEditingController();
@override
void initState() {
super.initState();
_emailController.text = widget.email;
}
@override
Widget build(BuildContext context) {
return Column(
children: [
const Header('Create account'),
Padding(
padding: const EdgeInsets.all(8.0),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: TextFormField(
controller: _emailController,
decoration: const InputDecoration(
hintText: 'Enter your email',
),
validator: (value) {
if (value!.isEmpty) {
return 'Enter your email address to continue';
}
return null;
},
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: TextFormField(
controller: _displayNameController,
decoration: const InputDecoration(
hintText: 'First & last name',
),
validator: (value) {
if (value!.isEmpty) {
return 'Enter your account name';
}
return null;
},
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: TextFormField(
controller: _passwordController,
decoration: const InputDecoration(
hintText: 'Password',
),
obscureText: true,
validator: (value) {
if (value!.isEmpty) {
return 'Enter your password';
}
return null;
},
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: widget.cancel,
child: const Text('CANCEL'),
),
const SizedBox(width: 16),
StyledButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
widget.registerAccount(
_emailController.text,
_displayNameController.text,
_passwordController.text,
);
}
},
child: const Text('SAVE'),
),
const SizedBox(width: 30),
],
),
),
],
),
),
),
],
);
}
}
class PasswordForm extends StatefulWidget {
const PasswordForm({
required this.login,
required this.email,
});
final String email;
final void Function(String email, String password) login;
@override
_PasswordFormState createState() => _PasswordFormState();
}
class _PasswordFormState extends State<PasswordForm> {
final _formKey = GlobalKey<FormState>(debugLabel: '_PasswordFormState');
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
@override
void initState() {
super.initState();
_emailController.text = widget.email;
}
@override
Widget build(BuildContext context) {
return Column(
children: [
const Header('Sign in'),
Padding(
padding: const EdgeInsets.all(8.0),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: TextFormField(
controller: _emailController,
decoration: const InputDecoration(
hintText: 'Enter your email',
),
validator: (value) {
if (value!.isEmpty) {
return 'Enter your email address to continue';
}
return null;
},
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: TextFormField(
controller: _passwordController,
decoration: const InputDecoration(
hintText: 'Password',
),
obscureText: true,
validator: (value) {
if (value!.isEmpty) {
return 'Enter your password';
}
return null;
},
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
const SizedBox(width: 16),
StyledButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
widget.login(
_emailController.text,
_passwordController.text,
);
}
},
child: const Text('SIGN IN'),
),
const SizedBox(width: 30),
],
),
),
],
),
),
),
],
);
}
}
widgets.dart
import 'package:flutter/material.dart';
class Header extends StatelessWidget {
const Header(this.heading);
final String heading;
@override
Widget build(BuildContext context) => Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
heading,
style: const TextStyle(fontSize: 24),
),
);
}
class Paragraph extends StatelessWidget {
const Paragraph(this.content);
final String content;
@override
Widget build(BuildContext context) => Padding(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
child: Text(
content,
style: const TextStyle(fontSize: 18),
),
);
}
class IconAndDetail extends StatelessWidget {
const IconAndDetail(this.icon, this.detail);
final IconData icon;
final String detail;
@override
Widget build(BuildContext context) => Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
Icon(icon),
const SizedBox(width: 8),
Text(
detail,
style: const TextStyle(fontSize: 18),
)
],
),
);
}
class StyledButton extends StatelessWidget {
const StyledButton({required this.child, required this.onPressed});
final Widget child;
final void Function() onPressed;
@override
Widget build(BuildContext context) => OutlinedButton(
style: OutlinedButton.styleFrom(
side: const BorderSide(color: Colors.deepPurple)),
onPressed: onPressed,
child: child,
);
}
pubspec.yaml(发布时的版本号)
cloud_firestore: ^1.0.0 # new
firebase_auth: ^1.0.0 # new
google_fonts: ^2.0.0
provider: ^5.0.0
我回答了我的问题,您可以通过在 ApplicationState
class 中添加 bool isLoading
变量并通过任何方法(signInWithEmailAndPassword
)在条件基础上设置它的值来做到这一点出现在 ApplicationState
中并将 isLoading
传递给 Authentication
小部件并检查 build
方法中的条件,例如,如果 isLoading
true 则显示 CircularProgressIndicator
检查以下代码更多详情:
main.dart
import 'package:firebase_core/firebase_core.dart'; // new
import 'package:firebase_auth/firebase_auth.dart'; // new
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:provider/provider.dart'; // new
import 'src/authentication.dart'; // new
import 'src/widgets.dart';
void main() {
// Modify from here
runApp(
ChangeNotifierProvider(
create: (context) => ApplicationState(),
builder: (context, _) => App(),
),
);
// to here.
}
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Firebase Meetup',
theme: ThemeData(
buttonTheme: Theme.of(context).buttonTheme.copyWith(
highlightColor: Colors.deepPurple,
),
primarySwatch: Colors.deepPurple,
textTheme: GoogleFonts.robotoTextTheme(
Theme.of(context).textTheme,
),
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: HomePage(),
);
}
}
class HomePage extends StatelessWidget {
HomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Firebase Meetup'),
),
body: ListView(
children: <Widget>[
Image.asset('assets/codelab.png'),
SizedBox(height: 8),
IconAndDetail(Icons.calendar_today, 'October 30'),
IconAndDetail(Icons.location_city, 'San Francisco'),
// Add from here
Consumer<ApplicationState>(
builder: (context, appState, _) => Authentication(
isLoading: appState.isLoading,
email: appState.email,
loginState: appState.loginState,
startLoginFlow: appState.startLoginFlow,
verifyEmail: appState.verifyEmail,
signInWithEmailAndPassword: appState.signInWithEmailAndPassword,
cancelRegistration: appState.cancelRegistration,
registerAccount: appState.registerAccount,
signOut: appState.signOut,
),
),
// to here
Divider(
height: 8,
thickness: 1,
indent: 8,
endIndent: 8,
color: Colors.grey,
),
Header("What we'll be doing"),
Paragraph(
'Join us for a day full of Firebase Workshops and Pizza!',
),
],
),
);
}
}
class ApplicationState extends ChangeNotifier {
ApplicationState() {
init();
}
Future<void> init() async {
await Firebase.initializeApp();
FirebaseAuth.instance.userChanges().listen((user) {
if (user != null) {
_loginState = ApplicationLoginState.loggedIn;
} else {
_loginState = ApplicationLoginState.loggedOut;
}
notifyListeners();
});
}
ApplicationLoginState _loginState = ApplicationLoginState.loggedOut;
ApplicationLoginState get loginState => _loginState;
bool get isLoading => _isLoading;
String? _email;
String? get email => _email;
bool _isLoading = false;
void startLoginFlow() {
_loginState = ApplicationLoginState.emailAddress;
notifyListeners();
}
void verifyEmail(
String email,
void Function(FirebaseAuthException e) errorCallback,
) async {
try {
var methods = await FirebaseAuth.instance.fetchSignInMethodsForEmail(email);
if (methods.contains('password')) {
_loginState = ApplicationLoginState.password;
} else {
_loginState = ApplicationLoginState.register;
}
_email = email;
notifyListeners();
} on FirebaseAuthException catch (e) {
errorCallback(e);
}
}
void signInWithEmailAndPassword(
String email,
String password,
void Function(FirebaseAuthException e) errorCallback,
) async {
try {
if (!this.isLoading) {
this._isLoading = true;
notifyListeners();
}
await FirebaseAuth.instance.signInWithEmailAndPassword(
email: email,
password: password,
);
} on FirebaseAuthException catch (e) {
errorCallback(e);
this._isLoading = false;
notifyListeners();
}
}
void cancelRegistration() {
_loginState = ApplicationLoginState.emailAddress;
notifyListeners();
}
void registerAccount(String email, String displayName, String password,
void Function(FirebaseAuthException e) errorCallback) async {
try {
var credential = await FirebaseAuth.instance
.createUserWithEmailAndPassword(email: email, password: password);
await credential.user!.updateProfile(displayName: displayName);
} on FirebaseAuthException catch (e) {
errorCallback(e);
}
}
void signOut() {
FirebaseAuth.instance.signOut();
/// here is not notifylistener();
}
}
authentication.dart
import 'package:flutter/material.dart';
import 'widgets.dart';
enum ApplicationLoginState {
loggedOut,
emailAddress,
register,
password,
loggedIn,
}
class Authentication extends StatelessWidget {
const Authentication({
required this.isLoading,
required this.loginState,
required this.email,
required this.startLoginFlow,
required this.verifyEmail,
required this.signInWithEmailAndPassword,
required this.cancelRegistration,
required this.registerAccount,
required this.signOut,
});
final bool isLoading;
final ApplicationLoginState loginState;
final String? email;
final void Function() startLoginFlow;
final void Function(
String email,
void Function(Exception e) error,
) verifyEmail;
final void Function(
String email,
String password,
void Function(Exception e) error,
) signInWithEmailAndPassword;
final void Function() cancelRegistration;
final void Function(
String email,
String displayName,
String password,
void Function(Exception e) error,
) registerAccount;
final void Function() signOut;
@override
Widget build(BuildContext context) {
if (this.isLoading) {
return Stack(
children: [Center(child: CircularProgressIndicator(value: null))],
);
}
switch (loginState) {
case ApplicationLoginState.loggedOut:
return Row(
children: [
Padding(
padding: const EdgeInsets.only(left: 24, bottom: 8),
child: StyledButton(
onPressed: () {
startLoginFlow();
},
child: const Text('RSVP'),
),
),
],
);
case ApplicationLoginState.emailAddress:
return EmailForm(
callback: (email) =>
verifyEmail(email, (e) => _showErrorDialog(context, 'Invalid email', e)));
case ApplicationLoginState.password:
return PasswordForm(
email: email!,
login: (email, password) {
print("CONTEXT $context");
signInWithEmailAndPassword(
email, password, (e) => _showErrorDialog(context, 'Failed to sign in', e));
},
);
case ApplicationLoginState.register:
return RegisterForm(
email: email!,
cancel: () {
cancelRegistration();
},
registerAccount: (
email,
displayName,
password,
) {
registerAccount(email, displayName, password,
(e) => _showErrorDialog(context, 'Failed to create account', e));
},
);
case ApplicationLoginState.loggedIn:
return Row(
children: [
Padding(
padding: const EdgeInsets.only(left: 24, bottom: 8),
child: StyledButton(
onPressed: () {
signOut();
},
child: const Text('LOGOUT'),
),
),
],
);
default:
return Row(
children: const [
Text("Internal error, this shouldn't happen..."),
],
);
}
}
void _showErrorDialog(BuildContext context, String title, Exception e) {
print("CONTEXT $context");
showDialog<void>(
context: context,
builder: (context) {
return AlertDialog(
title: Text(
title,
style: const TextStyle(fontSize: 24),
),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
Text(
'${(e as dynamic).message}',
style: const TextStyle(fontSize: 18),
),
],
),
),
actions: <Widget>[
StyledButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text(
'OK',
style: TextStyle(color: Colors.deepPurple),
),
),
],
);
},
);
}
}
class EmailForm extends StatefulWidget {
const EmailForm({required this.callback});
final void Function(String email) callback;
@override
_EmailFormState createState() => _EmailFormState();
}
class _EmailFormState extends State<EmailForm> {
final _formKey = GlobalKey<FormState>(debugLabel: '_EmailFormState');
final _controller = TextEditingController();
@override
Widget build(BuildContext context) {
return Column(
children: [
const Header('Sign in with email'),
Padding(
padding: const EdgeInsets.all(8.0),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: TextFormField(
controller: _controller,
decoration: const InputDecoration(
hintText: 'Enter your email',
),
validator: (value) {
if (value!.isEmpty) {
return 'Enter your email address to continue';
}
return null;
},
),
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 30),
child: StyledButton(
onPressed: () async {
if (_formKey.currentState!.validate()) {
widget.callback(_controller.text);
}
},
child: const Text('NEXT'),
),
),
],
),
],
),
),
),
],
);
}
}
class RegisterForm extends StatefulWidget {
const RegisterForm({
required this.registerAccount,
required this.cancel,
required this.email,
});
final String email;
final void Function(String email, String displayName, String password) registerAccount;
final void Function() cancel;
@override
_RegisterFormState createState() => _RegisterFormState();
}
class _RegisterFormState extends State<RegisterForm> {
final _formKey = GlobalKey<FormState>(debugLabel: '_RegisterFormState');
final _emailController = TextEditingController();
final _displayNameController = TextEditingController();
final _passwordController = TextEditingController();
@override
void initState() {
super.initState();
_emailController.text = widget.email;
}
@override
Widget build(BuildContext context) {
return Column(
children: [
const Header('Create account'),
Padding(
padding: const EdgeInsets.all(8.0),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: TextFormField(
controller: _emailController,
decoration: const InputDecoration(
hintText: 'Enter your email',
),
validator: (value) {
if (value!.isEmpty) {
return 'Enter your email address to continue';
}
return null;
},
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: TextFormField(
controller: _displayNameController,
decoration: const InputDecoration(
hintText: 'First & last name',
),
validator: (value) {
if (value!.isEmpty) {
return 'Enter your account name';
}
return null;
},
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: TextFormField(
controller: _passwordController,
decoration: const InputDecoration(
hintText: 'Password',
),
obscureText: true,
validator: (value) {
if (value!.isEmpty) {
return 'Enter your password';
}
return null;
},
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: widget.cancel,
child: const Text('CANCEL'),
),
const SizedBox(width: 16),
StyledButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
widget.registerAccount(
_emailController.text,
_displayNameController.text,
_passwordController.text,
);
}
},
child: const Text('SAVE'),
),
const SizedBox(width: 30),
],
),
),
],
),
),
),
],
);
}
}
class PasswordForm extends StatefulWidget {
const PasswordForm({
required this.login,
required this.email,
});
final String email;
final void Function(String email, String password) login;
@override
_PasswordFormState createState() => _PasswordFormState();
}
class _PasswordFormState extends State<PasswordForm> {
final _formKey = GlobalKey<FormState>(debugLabel: '_PasswordFormState');
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
@override
void initState() {
super.initState();
_emailController.text = widget.email;
}
@override
Widget build(BuildContext context) {
return Column(
children: [
const Header('Sign in'),
Padding(
padding: const EdgeInsets.all(8.0),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: TextFormField(
controller: _emailController,
decoration: const InputDecoration(
hintText: 'Enter your email',
),
validator: (value) {
if (value!.isEmpty) {
return 'Enter your email address to continue';
}
return null;
},
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: TextFormField(
controller: _passwordController,
decoration: const InputDecoration(
hintText: 'Password',
),
obscureText: true,
validator: (value) {
if (value!.isEmpty) {
return 'Enter your password';
}
return null;
},
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
const SizedBox(width: 16),
StyledButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
widget.login(
_emailController.text,
_passwordController.text,
);
}
},
child: const Text('SIGN IN'),
),
const SizedBox(width: 30),
],
),
),
],
),
),
),
],
);
}
}
我经历了 flutter firebase
的 google code lab 使用 provider
包一切正常,正如代码实验室中提到的,我喜欢他们设计 Authentication
小部件传递函数的方式,在下面的代码中我想在等待状态下添加 CircularProgressIndicator,我不明白我在哪里添加 isLoading 的条件等于 true 然后 wait
否则 dosomething
函数传递 Authentication
小部件。
main.dart
import 'package:firebase_core/firebase_core.dart'; // new
import 'package:firebase_auth/firebase_auth.dart'; // new
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:provider/provider.dart'; // new
import 'src/authentication.dart'; // new
import 'src/widgets.dart';
void main() {
// Modify from here
runApp(
ChangeNotifierProvider(
create: (context) => ApplicationState(),
builder: (context, _) => App(),
),
);
// to here.
}
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Firebase Meetup',
theme: ThemeData(
buttonTheme: Theme.of(context).buttonTheme.copyWith(
highlightColor: Colors.deepPurple,
),
primarySwatch: Colors.deepPurple,
textTheme: GoogleFonts.robotoTextTheme(
Theme.of(context).textTheme,
),
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: HomePage(),
);
}
}
class HomePage extends StatelessWidget {
HomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Firebase Meetup'),
),
body: ListView(
children: <Widget>[
Image.asset('assets/codelab.png'),
SizedBox(height: 8),
IconAndDetail(Icons.calendar_today, 'October 30'),
IconAndDetail(Icons.location_city, 'San Francisco'),
// Add from here
Consumer<ApplicationState>(
builder: (context, appState, _) => Authentication(
email: appState.email,
loginState: appState.loginState,
startLoginFlow: appState.startLoginFlow,
verifyEmail: appState.verifyEmail,
signInWithEmailAndPassword: appState.signInWithEmailAndPassword,
cancelRegistration: appState.cancelRegistration,
registerAccount: appState.registerAccount,
signOut: appState.signOut,
),
),
// to here
Divider(
height: 8,
thickness: 1,
indent: 8,
endIndent: 8,
color: Colors.grey,
),
Header("What we'll be doing"),
Paragraph(
'Join us for a day full of Firebase Workshops and Pizza!',
),
],
),
);
}
}
class ApplicationState extends ChangeNotifier {
ApplicationState() {
init();
}
Future<void> init() async {
await Firebase.initializeApp();
FirebaseAuth.instance.userChanges().listen((user) {
if (user != null) {
_loginState = ApplicationLoginState.loggedIn;
} else {
_loginState = ApplicationLoginState.loggedOut;
}
notifyListeners();
});
}
ApplicationLoginState _loginState = ApplicationLoginState.loggedOut;
ApplicationLoginState get loginState => _loginState;
String? _email;
String? get email => _email;
void startLoginFlow() {
_loginState = ApplicationLoginState.emailAddress;
notifyListeners();
}
void verifyEmail(
String email,
void Function(FirebaseAuthException e) errorCallback,
) async {
try {
var methods = await FirebaseAuth.instance.fetchSignInMethodsForEmail(email);
if (methods.contains('password')) {
_loginState = ApplicationLoginState.password;
} else {
_loginState = ApplicationLoginState.register;
}
_email = email;
notifyListeners();
} on FirebaseAuthException catch (e) {
errorCallback(e);
}
}
void signInWithEmailAndPassword(
String email,
String password,
void Function(FirebaseAuthException e) errorCallback,
) async {
try {
await FirebaseAuth.instance.signInWithEmailAndPassword(
email: email,
password: password,
);
} on FirebaseAuthException catch (e) {
errorCallback(e);
}
}
void cancelRegistration() {
_loginState = ApplicationLoginState.emailAddress;
notifyListeners();
}
void registerAccount(String email, String displayName, String password,
void Function(FirebaseAuthException e) errorCallback) async {
try {
var credential = await FirebaseAuth.instance
.createUserWithEmailAndPassword(email: email, password: password);
await credential.user!.updateProfile(displayName: displayName);
} on FirebaseAuthException catch (e) {
errorCallback(e);
}
}
void signOut() {
FirebaseAuth.instance.signOut();
/// here is not notifylistener();
}
}
authentication.dart
import 'package:flutter/material.dart';
import 'widgets.dart';
enum ApplicationLoginState {
loggedOut,
emailAddress,
register,
password,
loggedIn,
}
class Authentication extends StatelessWidget {
const Authentication({
required this.loginState,
required this.email,
required this.startLoginFlow,
required this.verifyEmail,
required this.signInWithEmailAndPassword,
required this.cancelRegistration,
required this.registerAccount,
required this.signOut,
});
final ApplicationLoginState loginState;
final String? email;
final void Function() startLoginFlow;
final void Function(
String email,
void Function(Exception e) error,
) verifyEmail;
final void Function(
String email,
String password,
void Function(Exception e) error,
) signInWithEmailAndPassword;
final void Function() cancelRegistration;
final void Function(
String email,
String displayName,
String password,
void Function(Exception e) error,
) registerAccount;
final void Function() signOut;
@override
Widget build(BuildContext context) {
switch (loginState) {
case ApplicationLoginState.loggedOut:
return Row(
children: [
Padding(
padding: const EdgeInsets.only(left: 24, bottom: 8),
child: StyledButton(
onPressed: () {
startLoginFlow();
},
child: const Text('RSVP'),
),
),
],
);
case ApplicationLoginState.emailAddress:
return EmailForm(
callback: (email) =>
verifyEmail(email, (e) => _showErrorDialog(context, 'Invalid email', e)));
case ApplicationLoginState.password:
return PasswordForm(
email: email!,
login: (email, password) {
print("CONTEXT $context");
signInWithEmailAndPassword(
email, password, (e) => _showErrorDialog(context, 'Failed to sign in', e));
},
);
case ApplicationLoginState.register:
return RegisterForm(
email: email!,
cancel: () {
cancelRegistration();
},
registerAccount: (
email,
displayName,
password,
) {
registerAccount(email, displayName, password,
(e) => _showErrorDialog(context, 'Failed to create account', e));
},
);
case ApplicationLoginState.loggedIn:
return Row(
children: [
Padding(
padding: const EdgeInsets.only(left: 24, bottom: 8),
child: StyledButton(
onPressed: () {
signOut();
},
child: const Text('LOGOUT'),
),
),
],
);
default:
return Row(
children: const [
Text("Internal error, this shouldn't happen..."),
],
);
}
}
void _showErrorDialog(BuildContext context, String title, Exception e) {
print("CONTEXT $context");
showDialog<void>(
context: context,
builder: (context) {
return AlertDialog(
title: Text(
title,
style: const TextStyle(fontSize: 24),
),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
Text(
'${(e as dynamic).message}',
style: const TextStyle(fontSize: 18),
),
],
),
),
actions: <Widget>[
StyledButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text(
'OK',
style: TextStyle(color: Colors.deepPurple),
),
),
],
);
},
);
}
}
class EmailForm extends StatefulWidget {
const EmailForm({required this.callback});
final void Function(String email) callback;
@override
_EmailFormState createState() => _EmailFormState();
}
class _EmailFormState extends State<EmailForm> {
final _formKey = GlobalKey<FormState>(debugLabel: '_EmailFormState');
final _controller = TextEditingController();
@override
Widget build(BuildContext context) {
return Column(
children: [
const Header('Sign in with email'),
Padding(
padding: const EdgeInsets.all(8.0),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: TextFormField(
controller: _controller,
decoration: const InputDecoration(
hintText: 'Enter your email',
),
validator: (value) {
if (value!.isEmpty) {
return 'Enter your email address to continue';
}
return null;
},
),
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 30),
child: StyledButton(
onPressed: () async {
if (_formKey.currentState!.validate()) {
widget.callback(_controller.text);
}
},
child: const Text('NEXT'),
),
),
],
),
],
),
),
),
],
);
}
}
class RegisterForm extends StatefulWidget {
const RegisterForm({
required this.registerAccount,
required this.cancel,
required this.email,
});
final String email;
final void Function(String email, String displayName, String password) registerAccount;
final void Function() cancel;
@override
_RegisterFormState createState() => _RegisterFormState();
}
class _RegisterFormState extends State<RegisterForm> {
final _formKey = GlobalKey<FormState>(debugLabel: '_RegisterFormState');
final _emailController = TextEditingController();
final _displayNameController = TextEditingController();
final _passwordController = TextEditingController();
@override
void initState() {
super.initState();
_emailController.text = widget.email;
}
@override
Widget build(BuildContext context) {
return Column(
children: [
const Header('Create account'),
Padding(
padding: const EdgeInsets.all(8.0),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: TextFormField(
controller: _emailController,
decoration: const InputDecoration(
hintText: 'Enter your email',
),
validator: (value) {
if (value!.isEmpty) {
return 'Enter your email address to continue';
}
return null;
},
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: TextFormField(
controller: _displayNameController,
decoration: const InputDecoration(
hintText: 'First & last name',
),
validator: (value) {
if (value!.isEmpty) {
return 'Enter your account name';
}
return null;
},
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: TextFormField(
controller: _passwordController,
decoration: const InputDecoration(
hintText: 'Password',
),
obscureText: true,
validator: (value) {
if (value!.isEmpty) {
return 'Enter your password';
}
return null;
},
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: widget.cancel,
child: const Text('CANCEL'),
),
const SizedBox(width: 16),
StyledButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
widget.registerAccount(
_emailController.text,
_displayNameController.text,
_passwordController.text,
);
}
},
child: const Text('SAVE'),
),
const SizedBox(width: 30),
],
),
),
],
),
),
),
],
);
}
}
class PasswordForm extends StatefulWidget {
const PasswordForm({
required this.login,
required this.email,
});
final String email;
final void Function(String email, String password) login;
@override
_PasswordFormState createState() => _PasswordFormState();
}
class _PasswordFormState extends State<PasswordForm> {
final _formKey = GlobalKey<FormState>(debugLabel: '_PasswordFormState');
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
@override
void initState() {
super.initState();
_emailController.text = widget.email;
}
@override
Widget build(BuildContext context) {
return Column(
children: [
const Header('Sign in'),
Padding(
padding: const EdgeInsets.all(8.0),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: TextFormField(
controller: _emailController,
decoration: const InputDecoration(
hintText: 'Enter your email',
),
validator: (value) {
if (value!.isEmpty) {
return 'Enter your email address to continue';
}
return null;
},
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: TextFormField(
controller: _passwordController,
decoration: const InputDecoration(
hintText: 'Password',
),
obscureText: true,
validator: (value) {
if (value!.isEmpty) {
return 'Enter your password';
}
return null;
},
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
const SizedBox(width: 16),
StyledButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
widget.login(
_emailController.text,
_passwordController.text,
);
}
},
child: const Text('SIGN IN'),
),
const SizedBox(width: 30),
],
),
),
],
),
),
),
],
);
}
}
widgets.dart
import 'package:flutter/material.dart';
class Header extends StatelessWidget {
const Header(this.heading);
final String heading;
@override
Widget build(BuildContext context) => Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
heading,
style: const TextStyle(fontSize: 24),
),
);
}
class Paragraph extends StatelessWidget {
const Paragraph(this.content);
final String content;
@override
Widget build(BuildContext context) => Padding(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
child: Text(
content,
style: const TextStyle(fontSize: 18),
),
);
}
class IconAndDetail extends StatelessWidget {
const IconAndDetail(this.icon, this.detail);
final IconData icon;
final String detail;
@override
Widget build(BuildContext context) => Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
Icon(icon),
const SizedBox(width: 8),
Text(
detail,
style: const TextStyle(fontSize: 18),
)
],
),
);
}
class StyledButton extends StatelessWidget {
const StyledButton({required this.child, required this.onPressed});
final Widget child;
final void Function() onPressed;
@override
Widget build(BuildContext context) => OutlinedButton(
style: OutlinedButton.styleFrom(
side: const BorderSide(color: Colors.deepPurple)),
onPressed: onPressed,
child: child,
);
}
pubspec.yaml(发布时的版本号)
cloud_firestore: ^1.0.0 # new
firebase_auth: ^1.0.0 # new
google_fonts: ^2.0.0
provider: ^5.0.0
我回答了我的问题,您可以通过在 ApplicationState
class 中添加 bool isLoading
变量并通过任何方法(signInWithEmailAndPassword
)在条件基础上设置它的值来做到这一点出现在 ApplicationState
中并将 isLoading
传递给 Authentication
小部件并检查 build
方法中的条件,例如,如果 isLoading
true 则显示 CircularProgressIndicator
检查以下代码更多详情:
main.dart
import 'package:firebase_core/firebase_core.dart'; // new
import 'package:firebase_auth/firebase_auth.dart'; // new
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:provider/provider.dart'; // new
import 'src/authentication.dart'; // new
import 'src/widgets.dart';
void main() {
// Modify from here
runApp(
ChangeNotifierProvider(
create: (context) => ApplicationState(),
builder: (context, _) => App(),
),
);
// to here.
}
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Firebase Meetup',
theme: ThemeData(
buttonTheme: Theme.of(context).buttonTheme.copyWith(
highlightColor: Colors.deepPurple,
),
primarySwatch: Colors.deepPurple,
textTheme: GoogleFonts.robotoTextTheme(
Theme.of(context).textTheme,
),
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: HomePage(),
);
}
}
class HomePage extends StatelessWidget {
HomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Firebase Meetup'),
),
body: ListView(
children: <Widget>[
Image.asset('assets/codelab.png'),
SizedBox(height: 8),
IconAndDetail(Icons.calendar_today, 'October 30'),
IconAndDetail(Icons.location_city, 'San Francisco'),
// Add from here
Consumer<ApplicationState>(
builder: (context, appState, _) => Authentication(
isLoading: appState.isLoading,
email: appState.email,
loginState: appState.loginState,
startLoginFlow: appState.startLoginFlow,
verifyEmail: appState.verifyEmail,
signInWithEmailAndPassword: appState.signInWithEmailAndPassword,
cancelRegistration: appState.cancelRegistration,
registerAccount: appState.registerAccount,
signOut: appState.signOut,
),
),
// to here
Divider(
height: 8,
thickness: 1,
indent: 8,
endIndent: 8,
color: Colors.grey,
),
Header("What we'll be doing"),
Paragraph(
'Join us for a day full of Firebase Workshops and Pizza!',
),
],
),
);
}
}
class ApplicationState extends ChangeNotifier {
ApplicationState() {
init();
}
Future<void> init() async {
await Firebase.initializeApp();
FirebaseAuth.instance.userChanges().listen((user) {
if (user != null) {
_loginState = ApplicationLoginState.loggedIn;
} else {
_loginState = ApplicationLoginState.loggedOut;
}
notifyListeners();
});
}
ApplicationLoginState _loginState = ApplicationLoginState.loggedOut;
ApplicationLoginState get loginState => _loginState;
bool get isLoading => _isLoading;
String? _email;
String? get email => _email;
bool _isLoading = false;
void startLoginFlow() {
_loginState = ApplicationLoginState.emailAddress;
notifyListeners();
}
void verifyEmail(
String email,
void Function(FirebaseAuthException e) errorCallback,
) async {
try {
var methods = await FirebaseAuth.instance.fetchSignInMethodsForEmail(email);
if (methods.contains('password')) {
_loginState = ApplicationLoginState.password;
} else {
_loginState = ApplicationLoginState.register;
}
_email = email;
notifyListeners();
} on FirebaseAuthException catch (e) {
errorCallback(e);
}
}
void signInWithEmailAndPassword(
String email,
String password,
void Function(FirebaseAuthException e) errorCallback,
) async {
try {
if (!this.isLoading) {
this._isLoading = true;
notifyListeners();
}
await FirebaseAuth.instance.signInWithEmailAndPassword(
email: email,
password: password,
);
} on FirebaseAuthException catch (e) {
errorCallback(e);
this._isLoading = false;
notifyListeners();
}
}
void cancelRegistration() {
_loginState = ApplicationLoginState.emailAddress;
notifyListeners();
}
void registerAccount(String email, String displayName, String password,
void Function(FirebaseAuthException e) errorCallback) async {
try {
var credential = await FirebaseAuth.instance
.createUserWithEmailAndPassword(email: email, password: password);
await credential.user!.updateProfile(displayName: displayName);
} on FirebaseAuthException catch (e) {
errorCallback(e);
}
}
void signOut() {
FirebaseAuth.instance.signOut();
/// here is not notifylistener();
}
}
authentication.dart
import 'package:flutter/material.dart';
import 'widgets.dart';
enum ApplicationLoginState {
loggedOut,
emailAddress,
register,
password,
loggedIn,
}
class Authentication extends StatelessWidget {
const Authentication({
required this.isLoading,
required this.loginState,
required this.email,
required this.startLoginFlow,
required this.verifyEmail,
required this.signInWithEmailAndPassword,
required this.cancelRegistration,
required this.registerAccount,
required this.signOut,
});
final bool isLoading;
final ApplicationLoginState loginState;
final String? email;
final void Function() startLoginFlow;
final void Function(
String email,
void Function(Exception e) error,
) verifyEmail;
final void Function(
String email,
String password,
void Function(Exception e) error,
) signInWithEmailAndPassword;
final void Function() cancelRegistration;
final void Function(
String email,
String displayName,
String password,
void Function(Exception e) error,
) registerAccount;
final void Function() signOut;
@override
Widget build(BuildContext context) {
if (this.isLoading) {
return Stack(
children: [Center(child: CircularProgressIndicator(value: null))],
);
}
switch (loginState) {
case ApplicationLoginState.loggedOut:
return Row(
children: [
Padding(
padding: const EdgeInsets.only(left: 24, bottom: 8),
child: StyledButton(
onPressed: () {
startLoginFlow();
},
child: const Text('RSVP'),
),
),
],
);
case ApplicationLoginState.emailAddress:
return EmailForm(
callback: (email) =>
verifyEmail(email, (e) => _showErrorDialog(context, 'Invalid email', e)));
case ApplicationLoginState.password:
return PasswordForm(
email: email!,
login: (email, password) {
print("CONTEXT $context");
signInWithEmailAndPassword(
email, password, (e) => _showErrorDialog(context, 'Failed to sign in', e));
},
);
case ApplicationLoginState.register:
return RegisterForm(
email: email!,
cancel: () {
cancelRegistration();
},
registerAccount: (
email,
displayName,
password,
) {
registerAccount(email, displayName, password,
(e) => _showErrorDialog(context, 'Failed to create account', e));
},
);
case ApplicationLoginState.loggedIn:
return Row(
children: [
Padding(
padding: const EdgeInsets.only(left: 24, bottom: 8),
child: StyledButton(
onPressed: () {
signOut();
},
child: const Text('LOGOUT'),
),
),
],
);
default:
return Row(
children: const [
Text("Internal error, this shouldn't happen..."),
],
);
}
}
void _showErrorDialog(BuildContext context, String title, Exception e) {
print("CONTEXT $context");
showDialog<void>(
context: context,
builder: (context) {
return AlertDialog(
title: Text(
title,
style: const TextStyle(fontSize: 24),
),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
Text(
'${(e as dynamic).message}',
style: const TextStyle(fontSize: 18),
),
],
),
),
actions: <Widget>[
StyledButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text(
'OK',
style: TextStyle(color: Colors.deepPurple),
),
),
],
);
},
);
}
}
class EmailForm extends StatefulWidget {
const EmailForm({required this.callback});
final void Function(String email) callback;
@override
_EmailFormState createState() => _EmailFormState();
}
class _EmailFormState extends State<EmailForm> {
final _formKey = GlobalKey<FormState>(debugLabel: '_EmailFormState');
final _controller = TextEditingController();
@override
Widget build(BuildContext context) {
return Column(
children: [
const Header('Sign in with email'),
Padding(
padding: const EdgeInsets.all(8.0),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: TextFormField(
controller: _controller,
decoration: const InputDecoration(
hintText: 'Enter your email',
),
validator: (value) {
if (value!.isEmpty) {
return 'Enter your email address to continue';
}
return null;
},
),
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 30),
child: StyledButton(
onPressed: () async {
if (_formKey.currentState!.validate()) {
widget.callback(_controller.text);
}
},
child: const Text('NEXT'),
),
),
],
),
],
),
),
),
],
);
}
}
class RegisterForm extends StatefulWidget {
const RegisterForm({
required this.registerAccount,
required this.cancel,
required this.email,
});
final String email;
final void Function(String email, String displayName, String password) registerAccount;
final void Function() cancel;
@override
_RegisterFormState createState() => _RegisterFormState();
}
class _RegisterFormState extends State<RegisterForm> {
final _formKey = GlobalKey<FormState>(debugLabel: '_RegisterFormState');
final _emailController = TextEditingController();
final _displayNameController = TextEditingController();
final _passwordController = TextEditingController();
@override
void initState() {
super.initState();
_emailController.text = widget.email;
}
@override
Widget build(BuildContext context) {
return Column(
children: [
const Header('Create account'),
Padding(
padding: const EdgeInsets.all(8.0),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: TextFormField(
controller: _emailController,
decoration: const InputDecoration(
hintText: 'Enter your email',
),
validator: (value) {
if (value!.isEmpty) {
return 'Enter your email address to continue';
}
return null;
},
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: TextFormField(
controller: _displayNameController,
decoration: const InputDecoration(
hintText: 'First & last name',
),
validator: (value) {
if (value!.isEmpty) {
return 'Enter your account name';
}
return null;
},
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: TextFormField(
controller: _passwordController,
decoration: const InputDecoration(
hintText: 'Password',
),
obscureText: true,
validator: (value) {
if (value!.isEmpty) {
return 'Enter your password';
}
return null;
},
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: widget.cancel,
child: const Text('CANCEL'),
),
const SizedBox(width: 16),
StyledButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
widget.registerAccount(
_emailController.text,
_displayNameController.text,
_passwordController.text,
);
}
},
child: const Text('SAVE'),
),
const SizedBox(width: 30),
],
),
),
],
),
),
),
],
);
}
}
class PasswordForm extends StatefulWidget {
const PasswordForm({
required this.login,
required this.email,
});
final String email;
final void Function(String email, String password) login;
@override
_PasswordFormState createState() => _PasswordFormState();
}
class _PasswordFormState extends State<PasswordForm> {
final _formKey = GlobalKey<FormState>(debugLabel: '_PasswordFormState');
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
@override
void initState() {
super.initState();
_emailController.text = widget.email;
}
@override
Widget build(BuildContext context) {
return Column(
children: [
const Header('Sign in'),
Padding(
padding: const EdgeInsets.all(8.0),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: TextFormField(
controller: _emailController,
decoration: const InputDecoration(
hintText: 'Enter your email',
),
validator: (value) {
if (value!.isEmpty) {
return 'Enter your email address to continue';
}
return null;
},
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: TextFormField(
controller: _passwordController,
decoration: const InputDecoration(
hintText: 'Password',
),
obscureText: true,
validator: (value) {
if (value!.isEmpty) {
return 'Enter your password';
}
return null;
},
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
const SizedBox(width: 16),
StyledButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
widget.login(
_emailController.text,
_passwordController.text,
);
}
},
child: const Text('SIGN IN'),
),
const SizedBox(width: 30),
],
),
),
],
),
),
),
],
);
}
}