Ive recently replaced Provider from my project to riverpod which is used with firebase auth to provide auth state.
On my login page, i two textformfields for email and password, and i have the sign in button inside a consumer. Ive also added a theme toggle switch (dark/light mode using ThemeProvider Package) and this is where the problem occurs.
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:
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:
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. 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?
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