[英]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.