0

我最近将 Provider 从我的项目中替换为 Riverpod,它与 firebase auth 一起使用以提供身份验证状态。

在我的登录页面上,我有两个用于电子邮件和密码的文本表单字段,并且我在消费者内部有登录按钮。我还添加了一个主题切换开关(使用 ThemeProvider 包的暗/亮模式),这就是问题发生的地方。

每次切换开关时,整个页面都会重建并清除字段?我为页面尝试了有状态/无状态/消费者小部件,但仍然无法解决这个问题。还尝试添加密钥仍然重建。

这是我的 loginpage.dart:

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:hubx/services/providers.dart';
import 'package:hubx/shared/widgets/loading_widget.dart';
import 'package:hubx/shared/validator.dart';
import 'package:theme_provider/theme_provider.dart';

class LoginPage extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    final GlobalKey<FormState> _signInFormKey = GlobalKey<FormState>();

    final TextEditingController _emailController = TextEditingController();
    final TextEditingController _passwordController = TextEditingController();

    final FocusNode _emailFocusNode = FocusNode();
    final FocusNode _passwordFocusNode = FocusNode();

    bool _darkTheme = true;
    _darkTheme = (ThemeProvider.controllerOf(context).currentThemeId == 'dark');

    return GestureDetector(
      onTap: () {
        _emailFocusNode.unfocus();
        _passwordFocusNode.unfocus();
      },
      child: Scaffold(
        appBar: AppBar(
          title: Text('Login'),
        ),
        body: SafeArea(
          child: Scrollbar(
            child: Center(
              child: SingleChildScrollView(
                padding: EdgeInsets.all(16),
                child: Column(
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    Card(
                      child: SwitchListTile.adaptive(
                        title: Text('Dark Mode'),
                        value: _darkTheme,
                        onChanged: (val) {
                          (val)
                              ? ThemeProvider.controllerOf(context)
                                  .setTheme('dark')
                              : ThemeProvider.controllerOf(context)
                                  .setTheme('light');
                        },
                      ),
                    ),
                    Card(
                      child: Form(
                        key: _signInFormKey,
                        child: Padding(
                          padding: const EdgeInsets.all(16.0),
                          child: Column(
                            mainAxisSize: MainAxisSize.min,
                            children: [
                              Column(
                                children: [
                                  TextFormField(
                                    controller: _emailController,
                                    focusNode: _emailFocusNode,
                                    keyboardType: TextInputType.emailAddress,
                                    textInputAction: TextInputAction.next,
                                    autovalidateMode:
                                        AutovalidateMode.onUserInteraction,
                                    validator: (value) =>
                                        Validator.validateEmail(email: value!),
                                    decoration: InputDecoration(
                                        labelText: 'Email',
                                        prefixIcon: Icon(Icons.email_outlined)),
                                  ),
                                  SizedBox(height: 8),
                                  TextFormField(
                                    controller: _passwordController,
                                    focusNode: _passwordFocusNode,
                                    keyboardType: TextInputType.text,
                                    textInputAction: TextInputAction.go,
                                    autovalidateMode:
                                        AutovalidateMode.onUserInteraction,
                                    validator: (value) =>
                                        Validator.validatePassword(
                                      password: value!,
                                    ),
                                    obscureText: true,
                                    decoration: InputDecoration(
                                      labelText: 'Password',
                                      prefixIcon: Icon(Icons.password_outlined),
                                    ),
                                  ),
                                  SizedBox(height: 32),
                                ],
                              ),
                              Consumer(
                                builder: (context, ref, child) {
                                  final authService =
                                      ref.watch(authServiceProvider);
                                  if (!authService.isLoading) {
                                    return Container(
                                      width: double.maxFinite,
                                      child: ElevatedButton.icon(
                                        onPressed: () async {
                                          _emailFocusNode.unfocus();
                                          _passwordFocusNode.unfocus();
                                          if (_signInFormKey.currentState!
                                              .validate()) {
                                            final authService =
                                                ref.read(authServiceProvider);
                                            authService
                                                .signInWithEmailAndPassword(
                                              _emailController.text,
                                              _passwordController.text,
                                            )
                                                .then((value) async {
                                              if (value == 'Success') {
                                                Navigator
                                                    .pushNamedAndRemoveUntil(
                                                        context,
                                                        AppRoutes
                                                            .RootAuthWrapper,
                                                        (_) => false);
                                              } else {
                                                Fluttertoast.showToast(
                                                    msg: value);
                                              }
                                            });
                                          }
                                        },
                                        icon:
                                            FaIcon(FontAwesomeIcons.signInAlt),
                                        label: Padding(
                                          padding: const EdgeInsets.all(16.0),
                                          child: Text(
                                            'LOGIN',
                                            style: TextStyle(
                                              fontSize: 20,
                                              fontWeight: FontWeight.w500,
                                              letterSpacing: 2,
                                            ),
                                          ),
                                        ),
                                      ),
                                    );
                                  } else {
                                    return loadingWidget();
                                  }
                                },
                              ),
                            ],
                          ),
                        ),
                      ),
                    ),
                  ],
                ),
              ),
            ),
          ),
        ),
      ),
    );
  }
}

身份验证服务提供者:

final authServiceProvider = ChangeNotifierProvider.autoDispose<AuthService>(
  (ref) {
    return AuthService();
  },
);

AuthService.dart:

import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/foundation.dart';

class AuthService extends ChangeNotifier {
  bool isLoading = false;

  Future<String> signInWithEmailAndPassword(
    String email,
    String password,
  ) async {
    _setLoading(true);
    String res;
    try {
      final userCredential =
          await FirebaseAuth.instance.signInWithEmailAndPassword(
        email: email,
        password: password,
      );

      _setLoading(false);

      res = 'Success';
      return res;
    } on FirebaseAuthException catch (e) {
      _setLoading(false);
      res = e.message.toString();
      print("# Auth Sign In - Error => $e");
      return res;
    }
  }

  Future<void> signOut() async {
    await FirebaseAuth.instance.signOut();
  }

  void _setLoading(bool newValue) {
    isLoading = newValue;
    notifyListeners();
  }
}
4

1 回答 1

0

这与 Riverpod 无关,而是在 build 方法中初始化控制器的方式。当您更新主题构建方法时,将重新构建,事实上,您的所有颜色都会更改,因此所有继承主题(全部)的小部件都将重新构建。你说你试过有状态,你试过这个?

class LoginPage extends StatefulWidget {
  LoginPage({Key key}) : super(key: key);

  @override
  _LoginPageState createState() => _LoginPageState();
}

class _LoginPageState extends State<LoginPage> {
  final GlobalKey<FormState> _signInFormKey = GlobalKey<FormState>();

  final TextEditingController _emailController = TextEditingController();
  final TextEditingController _passwordController = TextEditingController();

  final FocusNode _emailFocusNode = FocusNode();
  final FocusNode _passwordFocusNode = FocusNode();

  @override
  void dispose() {
    _emailController.dispose();
    _passwordController.dispose();
    _emailFocusNode.dispose();
    _passwordFocusNode.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    bool _darkTheme = true;
    _darkTheme = (ThemeProvider.controllerOf(context).currentThemeId == 'dark');
    /// you could also wrap your switch button in a consumerWidget just to watch ThemeProvider change to avoid doing this here

    return GestureDetector(...);
  }
}

如果你想在 build 方法中初始化东西,你也可以使用像 flutter_hooks 这样的包,但我现在不推荐它,除非你了解一些有状态和继承的小部件的基础知识

于 2021-08-13T04:48:29.100 回答