如何使用带有 firebase 的提供程序添加 CircularProgressIndicator

how to add CircularProgressIndicator using provider with firebase

我经历了 flutter firebasegoogle 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),
                        ],
                      ),
                    ),
                  ],
                ),
              ),
            ),
          ],
        );
      }
    }