Flutter - Stream error not persisting (using bloc pattern) - flutter

So I am trying to make a signup form in flutter using streams and the bloc pattern.
The problem is that the error added to the sink for one my streams does not seem to persist?
The submitValid getter does not seem to consider the error added to the passwordretype stream, this causes the login button to be clickable when it shouldn't
Steps to reproduce:
Fill out a valid email
Fill out a valid password (password field)
Fill out a valid password (password retype field)
Delete everything from the retype field
Delete everything from the password field.
Type a valid password in the password field and the login button appears clickable even though the retype field is empty.
Here's my code along with a screenshot.
Bloc.dart
import 'dart:async';
import 'package:firebase_auth/firebase_auth.dart';
import 'validators.dart';
import 'dart:io';
import 'package:rxdart/rxdart.dart';
class Bloc extends Object with Validators {
final _email = BehaviorSubject<String>();
final _password = BehaviorSubject<String>();
final _passwordretype = BehaviorSubject<String>();
final _isSignedIn = BehaviorSubject<bool>();
//Add data to stream
Stream<String> get email => _email.stream.transform(validateEmail);
Stream<String> get password => _password.stream.transform(validatePassword);
Stream<String> get passwordretype=> _passwordretype.stream.transform(validatePasswordRetype)
.doOnData((String c){
if(0 != _password.value.compareTo(c)){
_passwordretype.addError("Passwords do not match");
}
});
Stream<bool> get signInStatus => _isSignedIn.stream;
Stream<bool> get submitValid =>
Rx.combineLatest3(email, password, passwordretype, (e, p, r) => true);
//Change data
Function(String) get changeEmail => _email.sink.add;
Function(String) get changePassword => _password.sink.add;
Function(String) get changePasswordRetype => _passwordretype.sink.add;
Function(bool) get showProgressBar => _isSignedIn.add;
register() async {
final FirebaseAuth _auth = FirebaseAuth.instance;
try {
showProgressBar(true);
final FirebaseUser user = (await _auth.createUserWithEmailAndPassword(
email: _email.value,
password: _password.value,
))
.user;
if (user != null) {
// setState(() {
// _success = true;
// _userEmail = user.email;
// Navigator.of(context).pushNamedAndRemoveUntil(
// '/home', (Route<dynamic> route) => false);
//
//// Navigator.of(context).pushReplacementNamed('/home');
//
// });
} else {
// Scaffold.of(context).showSnackBar(SnackBar(
// content: Text("Error occured, please try again later"),
// ));
// _success = false;
}
} catch (err) {
_isSignedIn.addError(err);
print(err);
// setState(() {
// _showLoading = false;
// _error = true;
// });
}
}
dispose() {
_email.drain();
_email.close();
_password.drain();
_password.close();
_passwordretype.drain();
_passwordretype.close();
}
}
SignupScreen.dart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:lendy/src/blocs/bloc.dart';
import 'package:lendy/src/blocs/provider.dart';
class SignupScreen extends StatelessWidget {
// final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
// final TextEditingController _emailController = TextEditingController();
// final TextEditingController _passwordController = TextEditingController();
// final TextEditingController _passwordController2 = TextEditingController();
final FirebaseAuth _auth = FirebaseAuth.instance;
// final GoogleSignIn _googleSignIn = GoogleSignIn();
String _userID = "";
bool _success;
String _userEmail;
#override
Widget build(BuildContext context) {
final bloc = Provider.of(context);
return Scaffold(
appBar: AppBar(
title: Text("Signup"),
),
body: Container(
child: Column(
children: <Widget>[
emailField(bloc),
passwordField(bloc),
passwordFieldRe(bloc),
SizedBox(
height: 10.0,
),
button(bloc)
],
),
),
);
}
Widget emailField(Bloc bloc) {
return StreamBuilder(
stream: bloc.email,
builder: (context, snapshot) {
return TextField(
onChanged: bloc.changeEmail,
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
hintText: 'Enter email address',
labelText: 'Email-address',
errorText: snapshot.error),
);
},
);
}
Widget passwordField(Bloc bloc) {
return StreamBuilder(
stream: bloc.password,
builder: (context, snapshot) {
return TextField(
onChanged: bloc.changePassword,
decoration: InputDecoration(
hintText: 'Enter password',
labelText: 'Password',
errorText: snapshot.error),
);
});
}
Widget passwordFieldRe(Bloc bloc) {
return StreamBuilder(
stream: bloc.passwordretype,
builder: (context, snapshot) {
return TextField(
onChanged: bloc.changePasswordRetype,
decoration: InputDecoration(
hintText: 'Retype password',
labelText: 'Password',
errorText: snapshot.error),
);
});
}
Widget button(Bloc bloc) {
return StreamBuilder(
stream: bloc.submitValid,
builder: (context, snapshot) {
return RaisedButton(
child: Text('Register'),
color: Colors.blue,
//if true
onPressed: snapshot.hasData
? () {
// bloc.showProgressBar(true);
bloc.register();
}
: null);
},
);
}
Widget buttons(Bloc bloc) {
return StreamBuilder(
stream: bloc.submitValid,
builder: (context, snapshot1) {
return StreamBuilder(
stream: bloc.signInStatus,
builder: (context, snapshot2) {
if (!snapshot2.hasData || snapshot2.hasError) {
return Column(
children: <Widget>[
RaisedButton(
child: Text('Register'),
color: Colors.blue,
onPressed: snapshot1.hasData
? () {
bloc.register();
}
: null,
),
snapshot2.hasError ? Text("ee") : Container()
],
);
} else {
return CircularProgressIndicator();
}
},
);
},
);
}
Widget submitButton(Bloc bloc) {
return StreamBuilder(
stream: bloc.signInStatus,
builder: (context, snapshot) {
if (snapshot.hasError || !snapshot.hasData) {
return buttons(bloc);
} else {
return CircularProgressIndicator();
}
});
}
/
Validators.dart
import 'dart:async';
class Validators {
final validateEmail = StreamTransformer<String, String>.fromHandlers(
handleData: (email, sink){
if (RegExp(
r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+#[a-zA-Z0-9]+\.[a-zA-Z]+")
.hasMatch(email)){
sink.add(email);
} else {
sink.addError('Please enter a valid email address.');
}
}
);
final validatePassword = StreamTransformer<String, String>.fromHandlers(
handleData: (password, sink){
if (password.length > 3){
sink.add(password);
} else {
sink.addError('Password is too short.');
}
}
);
final validatePasswordRetype = StreamTransformer<String, String>.fromHandlers(
handleData: (password, sink){
print("HANDLE DATA");
if (password.length > 3){
sink.add(password);
} else {
sink.addError('Password is too short.');
}
}
);
}
screenshot

For the submitValid Stream you are using the BehaviorSubjects instead of use the stream.transforms, so for that reason, your submitValid doesn't receive the errors. Replace your submitValid with the next code:
Stream<bool> get submitValid => Rx.combineLatest3(email, password, passwordretype, (e, p, r) => true);

Found a similar issue here
I changed my submitValid function like so:
Stream<bool> get submitValid =>
Rx.combineLatest3(email, password, passwordretype, (e, p, r) {
if (p == _password.value && r == _passwordretype.value){
return true;
} else {
return false;
}
});
And changed onPressed in my button Widget like so. It only renders the clickable button if the snapshot returns true.
Widget button(Bloc bloc) {
return StreamBuilder(
stream: bloc.submitValid,
builder: (context, snapshot) {
return RaisedButton(
child: Text('Register'),
color: Colors.blue,
onPressed: snapshot.hasData && snapshot.data
? () {
bloc.register();
}
: null);
},
);
}

Related

StateError (Bad state: cannot get a field on a DocumentSnapshotPlatform which does not exist)

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:mumu/utils/navigator/exports.dart';
import 'group_tile.dart';
class ChatsList extends StatefulWidget {
const ChatsList({Key? key}) : super(key: key);
#override
State<ChatsList> createState() => _ChatsListState();
}
class _ChatsListState extends State<ChatsList> {
String groupName = "";
String username = "";
String email = "";
Stream? groups;
bool isLoading = false;
User? user = FirebaseAuth.instance.currentUser;
#override
void initState() {
gettingUserDetails();
super.initState();
}
String getGroupId(String res) {
return res.substring(0, res.indexOf("_"));
}
String getGroupName(String res) {
return res.substring(res.indexOf("_") + 1);
}
gettingUserDetails() async {
final userDetails = await FirebaseFirestore.instance
.collection("users")
.doc(user?.uid)
.get();
await userDetails.data()?["username"].then((value) {
setState(() {
username = value;
});
});
await userDetails.data()?["email_id"].then((value) {
setState(() {
email = value;
});
});
// setState(() {
// widget.username = username;
// widget.email = email;
// });
//getting the list of snapshots in our stream
final userSnapshot = FirebaseFirestore.instance
.collection("users")
.doc(user?.uid)
.snapshots();
setState(() {
groups = userSnapshot;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: groupList(),
floatingActionButton: FloatingActionButton(
onPressed: () {
popUpDialog(context);
},
child: Icon(Icons.add),
),
);
}
createGroup(String username, String id, String groupName) async {
DocumentReference groupDocumentReference =
await FirebaseFirestore.instance.collection("groups").add({
"groupName": groupName,
"groupIcon": "",
"admin": "${id}_$username",
"members": [],
"groupId": "",
"recentMessage": "",
"recentMessageSender": "",
});
//updating the members
await groupDocumentReference.update({
"members": FieldValue.arrayUnion(["${user?.uid}_$username"]),
"groupId": groupDocumentReference.id,
});
//updating groups in users collection
DocumentReference userDocumentReference =
FirebaseFirestore.instance.collection("users").doc(user?.uid);
return await userDocumentReference.update({
"groups":
FieldValue.arrayUnion(["${groupDocumentReference.id}_$groupName"]),
});
}
popUpDialog(BuildContext context) {
showDialog(
barrierDismissible: false,
context: context,
builder: (context) {
return StatefulBuilder(
builder: (context, setState) {
return AlertDialog(
title: const Text(
'Create a group',
textAlign: TextAlign.left,
),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
isLoading == true
? const Center(
child: CircularProgressIndicator(),
)
: TextField(
onChanged: (value) {
setState(() {
groupName = value;
});
},
decoration: InputDecoration(labelText: "Chat name"),
)
],
),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text("Cancel")),
ElevatedButton(
onPressed: () async {
if (groupName != null) {
setState(() {
isLoading = true;
});
}
createGroup(username,
FirebaseAuth.instance.currentUser!.uid, groupName);
isLoading = false;
Navigator.of(context).pop();
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text("Group created successfully"),
backgroundColor: Colors.green,
));
},
child: Text("Create"))
],
);
},
);
},
);
}
groupList() {
return StreamBuilder(
stream: groups,
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.hasData &&
snapshot.data['groups'] != null && //<-- Here is shows the bad state error
snapshot.data["groups"].length != 0) {
return ListView.builder(
itemCount: snapshot.data["groups"].length,
itemBuilder: (BuildContext context, int index) {
int reverseIndex = snapshot.data["groups"].length - index - 1;
return GroupTile(
username: snapshot.data["username"],
groupId: getGroupId(snapshot.data["groups"][reverseIndex]),
groupName: getGroupName(snapshot.data["groups"][reverseIndex]),
);
},
);
} else if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
} else {
return Text("no messages");
}
},
);
}
}
Here is my cloud_firestore collection:
I was making a group chat app and got an error StateError (Bad state: cannot get a field on a DocumentSnapshotPlatform which does not exist) . Here I was taking snapshot from the users collection and storing that in the "groups" stream. and then I was using the stream in the StreamBuilder of groupsList() method. Here it can't access the users collection from the firebase. Please help me guys.

Flutter Multi Select throws setState() called after dispose():

I have following 2 drop down in my code
one is drodown and other one is multi select drop down
dropdownButtonFormField(
value: dropdownValueLvl,
decoration: InputDecoration(
prefixIcon: Icon(Icons.leaderboard_outlined),
),
isExpanded: true,
hint: Text('Location'),
onChanged: (String value) {
// This is called when the user selects an item.
// dropdownValueLvl = value;
if (mounted) {
setState(() {
dropdownValueLvl = value;
_selectedRoles;
});
}
},
validator: (String value) {
if (value?.isEmpty ?? true) {
return 'Please select Location ';
}
return null;
},
items: _locddl.map((item) {
return DropdownMenuItem<String>(
value: item.id,
child: Text(item.locationyName),
);
}).toList(),
),
FutureBuilder(
future:
Provider.of<Salesmans>(context, listen: false)
.getUser(),
builder: (BuildContext context,
AsyncSnapshot<Salesman> snapshot) {
if (snapshot.connectionState ==
ConnectionState.done) {
return MultiSelectField<DdlValues>(
//key: _multiSelectKey,
// initialValue: _listRoles
// .where((element) => _editeSalesman.ddlRoles
// .contains((element).id))
// .toList(),
initialValue: _selectedRoles,
buttonText: 'Select Roles',
title: 'Select Roles',
stream: _listRoles,
onConfirm: (values) {
_selectedRoles = values;
});
} else {
return CircularProgressIndicator();
}
}),
All crud operation are performed properly but If I validate form and select Normal drop down and then select value from multi select drop down, It throws setState() called after dispose():
MultiSelectField.dart as follow
class MultiSelectField<V extends DdlValues> extends StatefulWidget {
final List<V> stream;
final List<V> initialValue;
final FormFieldSetter<List<V>> onConfirm;
final String buttonText;
final String title;
const MultiSelectField({
Key key,
#required this.stream,
#required this.initialValue,
#required this.onConfirm,
#required this.buttonText,
#required this.title,
}) : super(key: key);
#override
_MultiSelectFieldState createState() => _MultiSelectFieldState<V>();
}
class _MultiSelectFieldState<V extends DdlValues>
extends State<MultiSelectField<V>> {
#override
Widget build(BuildContext context) {
// return StreamBuilder(
// stream: widget.stream,
// builder: (BuildContext context, AsyncSnapshot<List<V>> snapshot) {
print(widget.initialValue);
print("Mainwid Key" + widget.key.toString());
//MultiSelectDialogField
return MultiSelectDialogField(
// key: widget.key,
buttonText: Text(widget.buttonText),
title: Text(widget.title),
items: widget.stream
.map((item) => MultiSelectItem(item, item.name))
.toList(),
initialValue: widget.initialValue,
onConfirm: widget.onConfirm,
listType: MultiSelectListType.LIST,
validator: (List value) {
if (value?.isEmpty ?? true) {
return 'Please select Roles ';
}
return null;
},
);
// });
}
}
Earlier in My code, If I validate then following save was called
Future<void> _saveForm() async {
final isValid = _form.currentState.validate();
FocusScope.of(context).unfocus(); // This line was causing issue
bool isSuccess = true;
if (!isValid) {
return;
}
// save code
}
If Validate form and select Dropdown value and then Multi select value was throwing setState() called after dispose():
I changed code as below and now its working
Future<void> _saveForm() async {
final isValid = _form.currentState.validate();
bool isSuccess = true;
if (!isValid) {
return;
}
FocusScope.of(context).unfocus(); // move this line to after if condition
// save code
}

Handle callback response from Post method with bloc

I am calling login api on button click, I am able to get response from server but on clicking on button ,i am not able to received response.How can i receive the response and handle it .I am using BLoC pattern for this. Here is the code,
import 'package:flutter/material.dart';
import '../blocs/bloc.dart';
import '../blocs/provider.dart';
import '../models/login_response.dart';
class LoginScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Provider(
child: new Scaffold(
body: Container(
child: LoginForm(),
),
),
);
}
}
class LoginForm extends StatefulWidget {
// since its a stateful widget we need to create state for it.
const LoginForm({Key key}) : super(key: key);
#override
_LoginFormState createState() => _LoginFormState();
}
class _LoginFormState extends State<LoginForm> {
#override
Widget build(BuildContext context) {
return Form(
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(top: 50),
),
// Start creating widget here.
emailField(),
passwordField(),
Container(margin: EdgeInsets.only(top: 25.0)),
submitButton()
],
),
);
}
Widget emailField() {
return StreamBuilder(
stream: bloc.email,
builder: (context, snapshot) {
return TextField(
onChanged: bloc.changeEmail,
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
hintText: 'you#example.com',
labelText: 'Email Address',
errorText: snapshot.error
),
);
}
);
}
Widget passwordField() {
return StreamBuilder(
stream: bloc.password,
builder: (context, snapshot) {
return TextField(
onChanged: bloc.changePassword,
obscureText: true,
decoration: InputDecoration(
labelText: 'Please enter your password',
hintText: 'Password',
errorText: snapshot.error
),
);
},
);
}
Widget submitButton() {
return StreamBuilder(
stream: bloc.submitValid,
builder: (context, snapshot) {
return RaisedButton(
onPressed:() => showWidgetForNetworkCall(context),
// onPressed: () {
// // Do submit button action.
// showWidgetForNetworkCall(context);
// // callLoginApi();
// },
child: const Text('Login'),
textColor: Colors.white,
color: Colors.blueAccent,
);
},
);
}
// Loading Widget
Widget _buildLoadingWidget() {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("Loading data from API...", textDirection: TextDirection.ltr), CircularProgressIndicator()
],
),
);
}
// // Error Widget
Widget _buildErrorWidget(String error) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("Loading error data from API...", textDirection: TextDirection.ltr), CircularProgressIndicator()
],
),
);
}
// show server data
showServerData() {
print(" Servr >>>>>> Data : ");
}
Widget showWidgetForNetworkCall(BuildContext context) {
bloc.loginSubmit();
return StreamBuilder(
stream: bloc.loginSubject.stream,
builder: (context, AsyncSnapshot<LoginResponse>snapshot){
if (snapshot.hasData) {
return showServerData();
} else if (snapshot.hasError) {
return _buildErrorWidget(snapshot.error);
} else {
return _buildLoadingWidget();
}
},
);
}
}
This is my login_screen.dart. And my bloc class for api call is:
postData() async {
LoginResponse response = await _repository.postData(_loginResource);
_subject.sink.add(response);
}
code of bloc
import 'dart:async';
import 'package:rxdart/rxdart.dart';
import 'validators.dart';
import '../models/login_response.dart';
import '../repository/login_repository.dart';
import '../resources/login_resource.dart';
class Bloc extends Object with Validators {
final LoginRepository _repository = LoginRepository();
final BehaviorSubject<LoginResponse> _subject =
BehaviorSubject<LoginResponse>();
LoginResource _loginResource = LoginResource();
final _email = BehaviorSubject<String>(); // Declaring variable as private
final _password = BehaviorSubject<String>(); // Declaring variable as private
// Add data to stream (Its like setter)
Stream<String> get email => _email.stream.transform(validateEmail);
Stream<String> get password =>
_password.stream.transform(validatePassword);
Stream<bool> get submitValid => Observable.combineLatest2(email, password, (e, p) => true);
// Change data. For retrieveing email value.
Function(String) get changeEmail => _email.sink.add;
Function(String) get changePassword => _password.sink.add;
loginSubmit() {
_loginResource.email = "bar1";
_loginResource.password = "bar2";
postData();
}
postData() async {
LoginResponse response = await _repository.postData(_loginResource);
_subject.sink.add(response);
}
dispose() {
_email.close();
_password.close();
_subject.close();
}
BehaviorSubject<LoginResponse> get loginSubject => _subject;
}
final bloc = Bloc();

how to fix the error : The getter 'email' was called on null. Receiver: null Tried calling: email

While trying to retreive authentication data from firebase, I am facing the error :
The getter 'email' was called on null.
Receiver: null
Tried calling: email
even if this is no error in my code.
when the user sign in he will be redirected to home page:
Here is the code i am using home.dart (in which there is sidebar):
import 'package:flutter/material.dart';
import 'package:testa/bloc/navigation_bloc/navigation_bloc.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:testa/sidebar/sidebar.dart';
import 'package:testa/sidebar/sidebar_layout.dart';
class HomePage extends StatelessWidget with NavigationStates {
const HomePage({Key key,#required this.user}) : super(key: key);
final FirebaseUser user;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home ${user.email}'),
),
body: StreamBuilder<DocumentSnapshot>(
stream: Firestore.instance
.collection('/users')
.document(user.uid)
.snapshots(),
builder:
(BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else if (snapshot.hasData) {
return checkRole(snapshot.data);
}
return LinearProgressIndicator();
},
),
);
}
Center checkRole(DocumentSnapshot snapshot) {
if (snapshot.data == null) {
return Center(
child: Text('no data set in the userId document in firestore'),
);
}
if (snapshot.data['role'] == 'admin') {
return adminPage(snapshot);
} else {
return userPage(snapshot);
}
}
Center adminPage(DocumentSnapshot snapshot) {
return Center(
child: Text('${snapshot.data['role']} ${snapshot.data['name']}'));
}
Center userPage(DocumentSnapshot snapshot) {
return Center(child: Text(snapshot.data['name']));
}
}
Here is the code of sign_in.dart if it might help even if it seems there is no issue there
import 'package:firebase_auth/firebase_auth.dart';
import 'package:testa/pages/home.dart';
import 'package:flutter/material.dart';
import 'package:testa/sidebar/sidebar_layout.dart';
import 'package:testa/sidebar/sidebar.dart';
class LoginPage extends StatefulWidget {
#override
_LoginPageState createState() => new _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
String _email, _password;
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(),
body: Form(
key: _formKey,
child: Column(
children: <Widget>[
TextFormField(
validator: (input) {
if(input.isEmpty){
return 'Provide an email';
}
},
decoration: InputDecoration(
labelText: 'Email'
),
onSaved: (input) => _email = input,
),
TextFormField(
validator: (input) {
if(input.length < 6){
return 'Longer password please';
}
},
decoration: InputDecoration(
labelText: 'Password'
),
onSaved: (input) => _password = input,
obscureText: true,
),
RaisedButton(
onPressed: signIn,
child: Text('Sign in'),
),
],
)
),
);
}
void signIn() async {
if(_formKey.currentState.validate()){
_formKey.currentState.save();
try{
FirebaseUser user = (await FirebaseAuth.instance.signInWithEmailAndPassword(email: _email, password: _password)).user;
Navigator.push(context, MaterialPageRoute(builder: (context) => SideBarLayout(user: user)));
}catch(e){
print(e.message);
}
}
}
}
when i change SideBarLayout by HomePage, it works fine, otherwise, i have an error under the first 'user' (context) => HomePage(user: user,))); and it says (the named parameter 'user' is not defined
void signIn() async {
if(_formKey.currentState.validate()){
_formKey.currentState.save();
try{
FirebaseUser user = (await FirebaseAuth.instance.signInWithEmailAndPassword(email: _email, password: _password)).user;
Navigator.push(context, MaterialPageRoute(builder: (context) => HomePage(user: user,)));
}catch(e){
print(e.message);
}
}
}
SideBarLayout.dart code:
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'sidebar.dart';
import 'package:testa/bloc/navigation_bloc/navigation_bloc.dart';
class SideBarLayout extends StatelessWidget {
const SideBarLayout({Key key, this.user}) : super(key: key);
final FirebaseUser user;
#override
Widget build(BuildContext context) {
return Scaffold(
body: BlocProvider<NavigationBloc>(
create: (context) => NavigationBloc(),
child: Stack(
children: <Widget>[
BlocBuilder<NavigationBloc, NavigationStates> (
builder: (context, navigationState) {
return navigationState as Widget;
},
),
SideBar(),
],
),
),
);
}
}
Probably this error is caused by invalid email/password and the FirebaseAuth is returning null and without checking you are Navigating to the next page and hence it throws an error.
Make sure you are providing correct credentials.
Before navigating to the Homepage check if the user is null or not?
FirebaseUser user = (await FirebaseAuth.instance.signInWithEmailAndPassword(email: _email, password: _password)).user;
if(user!=null){
// Navigate to Homepage
}
Your user data is not being passed to the HomePgae, first convert your HomePgae to StatfullWidget
Then make sure to add a constructor
Check this how to pass data to a StatfullWidget
Passing data to StatefulWidget and accessing it in it's state in Flutter

The textfield cursor does not work correctly

The textfield cursor does not work correctly when I retrieve data from the database and display it in a textfield and then try to edit it.
I would like the position of the cursor to be respected each time I return the validate result of the change method.
Full code below.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:rxdart/rxdart.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'TextField Test',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
#override
createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
TextEditingController _nameController = TextEditingController();
TextEditingController _emailController = TextEditingController();
ApplicationBloc _bloc;
#override
void initState() {
super.initState();
_bloc = new ApplicationBloc();
_bloc.initData();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('TextField Test'),
),
body: ListView(
shrinkWrap: true,
children: <Widget>[_buildName(), _buildEmail(), _buildSubmit()],
),
);
}
Widget _buildName() {
return StreamBuilder(
stream: _bloc.name,
builder: (context, snapshot) {
if (snapshot.hasData) {
_nameController.text = snapshot.data;
}
return TextField(
controller: _nameController,
onChanged: _bloc.changeName,
keyboardType: TextInputType.text,
decoration: InputDecoration(
labelText: "Name",
errorText: snapshot.error,
),
);
},
);
}
Widget _buildEmail() {
return StreamBuilder(
stream: _bloc.email,
builder: (context, snapshot) {
if (snapshot.hasData) {
_emailController.text = snapshot.data;
}
return TextField(
controller: _emailController,
onChanged: _bloc.changeEmail,
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
labelText: "Email",
errorText: snapshot.error,
),
);
},
);
}
Widget _buildSubmit() {
return StreamBuilder(
stream: _bloc.submit,
builder: (context, snapshot) {
return RaisedButton(
onPressed: (!snapshot.hasData || !snapshot.data)
? null
: () {
_bloc.submitForm();
_nameController.text = '';
_emailController.text = '';
},
child: Text('Submit!'),
);
},
);
}
}
class ApplicationBloc {
BehaviorSubject<String> _nameController =
BehaviorSubject<String>(seedValue: '');
Observable<String> get name => _nameController.stream.transform(validateName);
Function(String) get changeName => _nameController.sink.add;
BehaviorSubject<String> _emailController =
BehaviorSubject<String>(seedValue: '');
Observable<String> get email =>
_emailController.stream.transform(validateEmail);
Function(String) get changeEmail => _emailController.sink.add;
Observable<bool> get submit => Observable.combineLatest2(
name, email, (e, e1) => e.isNotEmpty && e1.isNotEmpty);
initData() {
// This data from database
_nameController.sink.add('Test123');
_emailController.sink.add('test#email.com');
}
submitForm() {
//Send to api and wait
//Reset values
Future.delayed(const Duration(seconds: 1));
}
final validateName =
StreamTransformer<String, String>.fromHandlers(handleData: (name, sink) {
if (name.isEmpty || name.length > 4) {
sink.add(name);
} else if (name.isNotEmpty) {
sink.addError('Invalid Name!');
}
});
final validateEmail =
StreamTransformer<String, String>.fromHandlers(handleData: (email, sink) {
String p =
r'^(([^<>()[\]\\.,;:\s#\"]+(\.[^<>()[\]\\.,;:\s#\"]+)*)|(\".+\"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$';
RegExp regExp = new RegExp(p);
if (email.isEmpty || (email.length > 4 && regExp.hasMatch(email))) {
sink.add(email);
} else {
sink.addError('Invalid email!');
}
});
//dispose/close all the streams when we call dispose() method
void dispose() {
_nameController.close();
_emailController.close();
}
}
I tried a code that changes the cursor position to final but its not work properly when i try edit the middle content of the textfield.
If you take a look at the text setter of TextEditingController, you'll see:
set text(String newText) {
value = value.copyWith(
text: newText,
selection: const TextSelection.collapsed(offset: -1),
composing: TextRange.empty,
);
}
Notice that selection is reset, so you cannot use this setter. Instead, inside your builder, do:
if (snapshot.hasData) {
_nameController.value = _nameController.value.copyWith(
text: snapshot.data,
);
}
Just the text is going to change and you don't have to worry about the other properties.