简体   繁体   English

Flutter Riverpod,如何避免使用消费者重建整个页面

[英]Flutter Riverpod, how avoid rebuilding whole page using consumer

Ive recently replaced Provider from my project to riverpod which is used with firebase auth to provide auth state.我最近将 Provider 从我的项目替换为 riverpod,它与 firebase auth 一起使用以提供 auth state。

On my login page, i two textformfields for email and password, and i have the sign in button inside a consumer.在我的登录页面上,我有两个用于 email 和密码的文本表单字段,并且我在消费者内部有登录按钮。 Ive also added a theme toggle switch (dark/light mode using ThemeProvider Package) and this is where the problem occurs.我还添加了一个主题切换开关(使用 ThemeProvider 包的暗/亮模式),这就是问题所在。

Everytime the switch is toggled, the whole page rebuilds and clears the fields?每次切换开关时,整个页面都会重建并清除字段? ive tried stateful/stateless/consumer widget for the page and still cant getting around this.我为页面尝试了有状态/无状态/消费者小部件,但仍然无法解决这个问题。 also tried adding keys still rebuilds.还尝试添加密钥仍然重建。

heres my loginpage.dart:这是我的登录页面。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();
                                  }
                                },
                              ),
                            ],
                          ),
                        ),
                      ),
                    ),
                  ],
                ),
              ),
            ),
          ),
        ),
      ),
    );
  }
}

authServiceProvider:授权服务提供者:

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

AuthService.dart:授权服务.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();
  }
}

This has nothing to do with Riverpod but the way you initialize your controllers inside the build method.这与 Riverpod 无关,而是在构建方法中初始化控制器的方式。 When you update your theme build method will rebuild, thats a fact, all your color changes so all widgets thath inheret Theme (all) will rebuild.当您更新主题时,构建方法将重建,这是事实,您所有的颜色都会发生变化,因此所有继承主题(全部)的小部件都将重建。 You say you tried stateful, you tried this?你说你试过stateful,你试过这个?

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

You could also use a package like flutter_hooks if you want to initialzie stuff in build method, but I wouldn't recommend it right now until you understand some basics of stateful and inhereted widgets如果你想在构建方法中初始化东西,你也可以使用像 flutter_hooks 这样的 package,但在你了解有状态和继承的小部件的一些基础知识之前,我现在不推荐它

Riverpod's consumer supports to be like a selector (in provider package), you need only use the code below, and your consumer will listen to changes on the specific field: Riverpod 的消费者支持像一个选择器(在提供者包中),你只需要使用下面的代码,你的消费者将监听特定字段的变化:

ref.watch(yourProvider.select((state) => state.whatDoYouWantToListen));

and you need to wrap only specific widgets, which you want to rebuild.并且您只需要包装要重建的特定小部件。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Flutter Riverpod 和 Firebase - Flutter Riverpod and Firebase Flutter 创建实体后的 Riverpod 重定向 - Flutter Riverpod redirection after entity creation Flutter Firestore Riverpod 获取数据问题 - Flutter Firestore Riverpod Getting Data Issue Flutter Riverpod Firebase currentUser Provider 未更新 - Flutter Riverpod Firebase currentUser Provider not updated 如何在不重建的情况下渲染 Firebase 中的图像? - How to render image from Firebase without rebuilding? 如何使用 Firebase 主机在 Flutter 中设置 404 未找到页面 - how to setup 404 Not found page in Flutter using Firebase Hosting 如果没有 function 代码更改,如何使用 Codepipeline 部署 AWS Lambda 而无需重建它? - How to deploy AWS Lambda using Codepipeline without rebuilding it if there are no function code changes? 如何在 flutter 中禁用带有页面视图的滑动操作 - How to disable swipe action with page view in flutter 当 state 更改时,有状态小部件不重建子小部件(颤振) - Stateful widget not rebuilding child widget when state changes (Flutter) 如何从 flutter 中的 firebase 中的多个文档中检索整个嵌套数据 - How to retrieve whole nested data form multiple document from firebase in flutter
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM