как добавить CircularProgressIndicator с помощью поставщика с firebase

# #firebase #flutter #provider

Вопрос:

я прошел через код Google лаборатории в flutter firebase использовании provider пакет каждая вещь работает отлично как говорилось в codelab мне нравится, как они дизайн Authentication виджета функции передачи из этого, в приведенном ниже коде я хочу добавить CircularProgressIndicator на состояние ожидание, я не понимаю, где я могу добавить состояние isLoading равно true, то wait иначе dosomething с функциями принят Authentication виджет.

главная.дротик

 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();
  }
}
 

аутентификация.дротик

 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 amp; 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),
                    ],
                  ),
                ),
              ],
            ),
          ),
        ),
      ],
    );
  }
}
 

виджеты.дротик

 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   
 

Ответ №1:

Я отвечаю на свой вопрос, вы могли бы сделать это, добавив bool isLoading переменную в ApplicationState класс и установив ее значение на условной основе из любого метода ( signInWithEmailAndPassword ), присутствующего в ApplicationState виджете, и передав isLoading его в Authentication виджет и проверив условие в build методе, например, если isLoading true, то покажите CircularProgressIndicator проверку ниже кода для получения более подробной информации:

главная.дротик

     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();
      }
    }
 

аутентификация.дротик

     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 amp; 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),
                        ],
                      ),
                    ),
                  ],
                ),
              ),
            ),
          ],
        );
      }
    }