Day26 Flutter 的状态管理 Provider (五) Firebase Login

main,dart

import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

import 'components/auth_widget.dart';
import 'components/auth_widget_builder.dart';
import 'services/apple_sign_in_available.dart';
import 'services/auth_service.dart';
import 'services/auth_service_adapter.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  final appleSignInAvailable = await AppleSignInAvailable.check();
  runApp(MyApp(appleSignInAvailable: appleSignInAvailable));
}

class MyApp extends StatelessWidget {
  const MyApp(
      {this.initialAuthServiceType = AuthServiceType.firebase,
      this.appleSignInAvailable});

  final AuthServiceType initialAuthServiceType;
  final AppleSignInAvailable appleSignInAvailable;

  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        Provider<AppleSignInAvailable>.value(value: appleSignInAvailable),
        Provider<AuthService>(
          create: (_) => AuthServiceAdapter(
            initialAuthServiceType: initialAuthServiceType,
          ),
          dispose: (_, AuthService authService) => authService.dispose(),
        ),
      ],
      child: AuthWidgetBuilder(
          builder: (BuildContext context, AsyncSnapshot<User> userSnapshot) {
        return MaterialApp(
          theme: ThemeData(primarySwatch: Colors.indigo),
          home: AuthWidget(userSnapshot: userSnapshot),
        );
      }),
    );
  }
}

login_manager.dart

import 'dart:async';

import 'package:flutter/foundation.dart';
import 'package:meta/meta.dart';
import 'package:travel_note/services/auth_service.dart';

class LoginManager {
  LoginManager({@required this.auth, @required this.isLoading});

  final AuthService auth;
  final ValueNotifier<bool> isLoading;

  Future<void> loginWithEmailAndPassword(
      {@required String email, @required String password}) async {
    try {
      isLoading.value = true;
      return await auth.loginWithEmailAndPassword(email, password);
    } catch (e) {
      isLoading.value = false;
      rethrow;
    }
  }
}

body.dart

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:travel_note/components/no_account_text.dart';
import 'package:travel_note/components/social_media.dart';
import 'package:travel_note/components/social_media_manager.dart';
import 'package:travel_note/services/auth_service.dart';

import '../../../constants.dart';
import '../../../size_config.dart';
import '../login_manager.dart';
import 'login_form.dart';

class Body extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final AuthService auth = Provider.of<AuthService>(context, listen: false);
    return ChangeNotifierProvider<ValueNotifier<bool>>(
      create: (_) => ValueNotifier<bool>(false),
      child: Consumer<ValueNotifier<bool>>(
        builder: (_, ValueNotifier<bool> isLoading, __) => MultiProvider(
          providers: [
            Provider<LoginManager>(
              create: (_) => LoginManager(auth: auth, isLoading: isLoading),
            ),
            Provider<SocialMediaManager>(
              create: (_) =>
                  SocialMediaManager(auth: auth, isLoading: isLoading),
            ),
          ],
          child: SingleChildScrollView(
            child: Padding(
              padding: EdgeInsets.symmetric(
                  horizontal: getProportionateScreenWidth(25)),
              child: Column(
                children: [
                  Text(
                    "Welcome to Travel Note",
                    style: headingStyle,
                  ),
                  VerticalSpacing(of: 16),
                  Text(
                    'Log in with your email and password \nor continue with social media',
                    textAlign: TextAlign.left,
                    style: contentStyle,
                  ),
                  VerticalSpacing(of: 25),
                  Consumer<LoginManager>(
                    builder: (_, LoginManager manager, __) => LoginForm.set(
                      isLoading: isLoading.value,
                      manager: manager,
                    ),
                  ),
                  VerticalSpacing(of: 25),
                  Consumer<SocialMediaManager>(
                    builder: (_, SocialMediaManager manager, __) =>
                        SocialMedia.set(
                      isLoading: isLoading.value,
                      manager: manager,
                    ),
                  ),
                  VerticalSpacing(of: 25),
                  NoAccountText(),
                  VerticalSpacing(of: 25),
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }
}

login_form.dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
import 'package:travel_note/components/default_button.dart';
import 'package:travel_note/components/form_error.dart';
import 'package:travel_note/components/platform_exception_alert_dialog.dart';
import 'package:travel_note/screens/forgot_password/forgot_password_screen.dart';
import 'package:travel_note/screens/login/login_manager.dart';

import '../../../constants.dart';
import '../../../size_config.dart';

class LoginForm extends StatefulWidget {
  final LoginManager manager;
  final bool isLoading;

  LoginForm.set({Key key, this.isLoading, this.manager}) : super(key: key);

  @override
  _LoginFormState createState() => _LoginFormState.set(isLoading, manager);
}

class _LoginFormState extends State<LoginForm> {
  _LoginFormState.set(this.isLoading, this.manager);

  final LoginManager manager;
  final bool isLoading;
  final _formKey = GlobalKey<FormState>();
  String email;
  String password;
  bool remember = false;
  final List<String> errors = [];

  void addError({String error}) {
    if (!errors.contains(error))
      setState(() {
        errors.add(error);
      });
  }

  void removeError({String error}) {
    if (errors.contains(error))
      setState(() {
        errors.remove(error);
      });
  }

  @override
  Widget build(BuildContext context) {
    return Form(
      key: _formKey,
      child: Column(
        children: [
          buildEmailFormField(),
          VerticalSpacing(of: 25),
          buildPasswordFormField(),
          VerticalSpacing(of: 25),
          Row(
            children: [
              Checkbox(
                value: remember,
                activeColor: kPrimaryColor,
                onChanged: (value) {
                  setState(() {
                    remember = value;
                  });
                },
              ),
              Text("Remember me"),
              Spacer(),
              GestureDetector(
                onTap: () {
                  Navigator.pushNamed(context, ForgotPasswordScreen.routeName);
                },
                child: Text(
                  "Forgot Password",
                  style: TextStyle(decoration: TextDecoration.underline),
                ),
              )
            ],
          ),
          FormError(errors: errors),
          VerticalSpacing(of: 25),
          DefaultButton(
            text: "Login",
            press: isLoading
                ? null
                : () {
                    if (_formKey.currentState.validate()) {
                      _formKey.currentState.save();
                      _loginWithEmailAndPassword(context, email, password);
                    }
                  },
          ),
        ],
      ),
    );
  }

  TextFormField buildPasswordFormField() {
    return TextFormField(
      obscureText: true,
      onSaved: (newValue) => password = newValue,
      onChanged: (value) {
        if (value.isNotEmpty) {
          removeError(error: kPasswordNullError);
        }
        if (value.length >= 8) {
          removeError(error: kShortPasswordError);
        }
      },
      validator: (value) {
        if (value.isEmpty) {
          addError(error: kPasswordNullError);
          removeError(error: kShortPasswordError);
          return "";
        } else if (value.length < 8) {
          addError(error: kShortPasswordError);
          return "";
        }
        return null;
      },
      decoration: InputDecoration(
        labelText: "Password",
        hintText: "Enter your password",
        floatingLabelBehavior: FloatingLabelBehavior.always,
        suffixIcon: Icon(
          MdiIcons.fromString("lock-outline"),
        ),
      ),
    );
  }

  TextFormField buildEmailFormField() {
    return TextFormField(
      keyboardType: TextInputType.emailAddress,
      onSaved: (newValue) => email = newValue,
      onChanged: (value) {
        if (value.isNotEmpty) {
          removeError(error: kEmailNullError);
        }
        if (emailValidatorRegExp.hasMatch(value)) {
          removeError(error: kInvalidEmailError);
        }
      },
      validator: (value) {
        if (value.isEmpty) {
          addError(error: kEmailNullError);
          removeError(error: kInvalidEmailError);
          return "";
        } else if (!emailValidatorRegExp.hasMatch(value)) {
          addError(error: kInvalidEmailError);
          return "";
        }
        return null;
      },
      decoration: InputDecoration(
        labelText: "Email",
        hintText: "Enter your email",
        floatingLabelBehavior: FloatingLabelBehavior.always,
        suffixIcon: Icon(
          MdiIcons.fromString("email-outline"),
        ),
      ),
    );
  }

  Future<void> _loginWithEmailAndPassword(
      BuildContext context, String email, String password) async {
    try {
      await manager.loginWithEmailAndPassword(email: email, password: password);
    } on PlatformException catch (e) {
      if (e.code != 'ERROR_ABORTED_BY_USER') {
        _showLoginError(context, e);
      }
    }
  }

  Future<void> _showLoginError(
      BuildContext context, PlatformException exception) async {
    await PlatformExceptionAlertDialog(
      title: 'Login failed',
      exception: exception,
    ).show(context);
  }
}

其他注册页、忘记密码页,动作都类似,接下来可以先自己尝试看看


<<:  鼠年全马铁人挑战 WEEK 34:负载性能测试 - Gatling (上)

>>:  Day35 - 「登愣登愣,登愣登登登」~ 隐挑战 Day11

面向服务的架构 (SOA)、Web 服务和微服务

-面向服务的架构 (SOA) 面向服务的架构 (SOA) 可以通过 Web 服务或微服务来实现。W...

亏损也是获利的一环

巴菲特有句名言:「规则一,永远不要输钱。规则二:别忘记规则一。」 https://www.youtu...

#8-选单华丽开起来!超简单!(animation-delay)

昨天做完了会动的汉堡动画传送门 今天来开关侧边栏吧! 当然只要控制 width0 —> 100...

【Day 30】总结

终於写完三十天,原本中途二十天就想放弃了,每天嚷嚷可不可以不要继续了。 原以为自己做的东西很多,很够...

2D transform Continued

大家好,我是西瓜,你现在看到的是 2021 iThome 铁人赛『如何在网页中绘制 3D 场景?从 ...