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

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
}

Related

_TypeError was thrown building NotesListView(dirty) : type 'Null' is not a subtype of type 'String'

I don't know where this error is coming from
enter image description here
the debug console says the error is in the returning line and the return line return just returning a widget but the error is about string is null i don't from where this error is coming
this is notes_view.dart file
class NotesView extends StatefulWidget {
const NotesView({super.key});
#override
State<NotesView> createState() => _NotesViewState();
}
class _NotesViewState extends State<NotesView> {
late final FirebaseCloudStorage _notesService;
String get userId => AuthService.firebase().currentUser!.id;
#override
void initState() {
_notesService = FirebaseCloudStorage();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.blue[100],
appBar: AppBar(
title: const Text("Your Notes"),
actions: [
IconButton(
onPressed: () {
Navigator.of(context).pushNamed(createOrUpdateNoteRoute);
},
icon: const Icon(Icons.add),
),
PopupMenuButton<MenuActions>(
onSelected: (value) async {
switch (value) {
case MenuActions.logout:
final shouldLogout = await showLogoutDialog(context);
if (shouldLogout) {
await AuthService.firebase().logOut();
Navigator.of(context)
.pushNamedAndRemoveUntil(loginRoute, (_) => false);
}
break;
}
},
itemBuilder: (context) {
return const [
PopupMenuItem<MenuActions>(
value: MenuActions.logout, child: Text("Log out")),
];
},
)
],
),
body: StreamBuilder(
stream: _notesService.allNotes(ownerUserId: userId),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
case ConnectionState.active:
if (snapshot.hasData) {
final allNotes = snapshot.data as Iterable<CloudNote>;
return NotesListView(
notes: allNotes,
onDeleteNote: (note) async {
await _notesService.deleteNote(
documentId: note.documentId);
print(note.documentId);
},
onTap: (note) {
Navigator.of(context).pushNamed(
createOrUpdateNoteRoute,
arguments: note,
);
},
);
} else {
return const CircularProgressIndicator();
}
default:
return const CircularProgressIndicator();
}
},
));
}
}
this is notes_list_view.dart file
typedef NoteCallback = void Function(CloudNote note);
class NotesListView extends StatelessWidget {
final Iterable<CloudNote> notes;
final NoteCallback onDeleteNote;
final NoteCallback onTap;
const NotesListView({
Key? key,
required this.notes,
required this.onDeleteNote,
required this.onTap,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: notes.length,
itemBuilder: (context, index) {
final note = notes.elementAt(index);
return ListTile(
onTap: () {
onTap(note);
},
textColor: Colors.black87,
title: Text(
note.text,
maxLines: 1,
softWrap: true,
overflow: TextOverflow.ellipsis,
),
trailing: IconButton(
icon: Icon(
Icons.delete,
color: Colors.red[200],
),
onPressed: () async {
final shouldDelete = await deleteDialog(context);
if (shouldDelete) {
onDeleteNote(note);
}
},
),
);
},
);
}
}
this is the firebase_cloud_storage.dart file
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:mynotes/services/cloud/cloud_note.dart';
import 'package:mynotes/services/cloud/cloud_storage_constants.dart';
import 'package:mynotes/services/cloud/cloud_storage_exceptions.dart';
class FirebaseCloudStorage {
final notes = FirebaseFirestore.instance.collection('notes');
Future<void> deleteNote({required String documentId}) async {
try {
await notes.doc(documentId).delete();
} catch (e) {
throw CouldNotDeleteNoteException();
}
}
Future<void> updateNote({
required String documentId,
required String text,
}) async {
try {
await notes.doc(documentId).update({textFieldName: text});
} catch (_) {
throw CouldNotUpdateNotesException();
}
}
Stream<Iterable<CloudNote>> allNotes({required String ownerUserId}) {
return notes.snapshots().map((event) => event.docs
.map((doc) => CloudNote.fromSnapshot(doc))
.where((note) => note.ownerUserId == ownerUserId));
}
Future<Iterable<CloudNote>> getNotes({required String ownerUserId}) async {
try {
return await notes
.where(ownerUserIdFieldName, isEqualTo: ownerUserId)
.get()
.then(
(value) => value.docs.map((doc) => CloudNote.fromSnapshot(doc)),
);
} catch (e) {
throw CouldNotGetAllNotesException();
}
}
Future<CloudNote> createNewNote({required String ownerUserId}) async {
final document = await notes.add({
ownerUserIdFieldName: ownerUserId,
textFieldName: '',
});
final fetchedNote = await document.get();
return CloudNote(
documentId: fetchedNote.id,
ownerUserId: ownerUserId,
text: '',
);
}
static final FirebaseCloudStorage _shared =
FirebaseCloudStorage._sharedInstance();
FirebaseCloudStorage._sharedInstance();
factory FirebaseCloudStorage() => _shared;
}
#immutable
class CloudNote {
final String documentId;
final String ownerUserId;
final String text;
const CloudNote({
required this.documentId,
required this.ownerUserId,
required this.text,
});
CloudNote.fromSnapshot(QueryDocumentSnapshot<Map<String, dynamic>> snapshot)
: documentId = snapshot.id,
ownerUserId = snapshot.data()[ownerUserIdFieldName],
text = snapshot.data()[textFieldName] as String;
}
I will like to accept null data while reading map, try
text = snapshot.data()[textFieldName] ?? "got null value";
CloudNote.fromSnapshot(QueryDocumentSnapshot<Map<String, dynamic>> snapshot)
: documentId = snapshot.id,
ownerUserId = snapshot.data()[ownerUserIdFieldName] ?? "default value",
text = snapshot.data()[textFieldName] ?? "Default value";

Why can't I read data from the shared preferences import?

I have a ListView.builder that builds a certain amount of widgets depending on user input. Each widget has their own specific name and has a DropDownMenu. I save this value with the corresponding name of the widget. It saves it correctly. However, when I try and read the data and create a new list from it, this error appears: [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: NoSuchMethodError: The method '[]' was called on null.
'course' is a list. I am using the shared preferences import. When you tap the flat button, it should build the new list, but it is not. Could you explain to me why this is not working please?
This is code in the main app.
void main() {
runApp(Hemis());
}
class Hemis extends StatefulWidget {
#override
_HemisState createState() => _HemisState();
}
class _HemisState extends State<Hemis> {
_read() async {
final prefs = await SharedPreferences.getInstance();
for(int i = 0; i < course.length; i++) {
listMarks[i].name = course[i].name;
listMarks[i].mark = prefs.getInt(course[i].name) ?? 0;
}
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: SingleChildScrollView(
child: Column(
children: <Widget>[
ListView.builder(
itemCount: course.length,
itemBuilder: (context, index) {
return ModuleListItem(
name: '${course[index].name}',
credits: course[index].credits,
);
},
),
FlatButton(
onPressed: () {
_read();
for(int i = 0; i < course.length; i++) {
print('${listMarks[i].name}: ${listMarks[i].mark}');
}
},
),
],
),
)
)
);
}
}
The widget that is being built.
final percentage = List<String>.generate(100, (i) => "$i");
class ModuleListItem extends StatefulWidget {
const ModuleListItem ({ Key key, this.name, this.credits }): super(key: key);
final String name;
final int credits;
#override
_ModuleListItemState createState() => _ModuleListItemState();
}
class _ModuleListItemState extends State<ModuleListItem> {
String dropdownValue;
bool isSwitched = false;
_save() async {
final prefs = await SharedPreferences.getInstance();
final key = '${widget.name}';
final value = int.parse(dropdownValue);
prefs.setInt(key, value);
print('saved $value');
}
#override
Widget build(BuildContext context) {
return Row(
children: <Widget>[
DropdownButton<String>(
value: dropdownValue,
icon: Icon(Icons.keyboard_arrow_down),
onChanged: (String newValue) {
setState(() {
dropdownValue = newValue;
});
},
items: percentage.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
),
),
Switch(
value: isSwitched,
onChanged: (value) {
setState(() {
isSwitched = value;
if(isSwitched == true) {
_save();
}
print(isSwitched);
});
},
),
],
),
);
}
}

the argument type 'JsObject cant be assigned to the parameter type 'BuildContext '

hi guys so i was trying to add some error check code in my page 'product_edit' to handle any potential http response error
so this is my page
product_edit.dart
==============================
import 'package:flutter/material.dart';
import 'package:scoped_model/scoped_model.dart';
import '../models/product.dart';
import '../scoped-models/main.dart';
class ProductEdit extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _ProductEditState();
}
}
class _ProductEditState extends State<ProductEdit> {
final Map<String, dynamic> _formData = {
'title': null,
'description': null,
'price': null,
'image':
'https://cdn.pixabay.com/photo/2020/03/04/07/28/black-4900736_960_720.jpg'
};
//String _title;
// String _description;
// double _price;
final GlobalKey<FormState> _formkey = GlobalKey<FormState>();
Widget _buildtypetextfield(Product product) {
return TextFormField(
decoration: InputDecoration(labelText: 'Product Type'),
initialValue: product == null ? '' : product.title,
validator: (String value) {
if (value.isEmpty) {
return 'product type and name is required';
}
return null;
},
onSaved: (String value) {
_formData['title'] = value;
},
);
}
Widget _builddescriptiontextfield(Product product) {
return TextFormField(
maxLines: 6,
decoration: InputDecoration(labelText: 'Description'),
initialValue: product == null ? '' : product.description,
validator: (String value) {
if (value.isEmpty || value.length < 10) {
return 'product description is required,and should be 10+ characters long';
}
return null;
},
onSaved: (String value) {
_formData['description'] = value;
},
);
}
Widget _buildpricetextfield(Product product) {
return TextFormField(
keyboardType: TextInputType.number,
decoration: InputDecoration(labelText: 'Price'),
initialValue: product == null ? '' : product.price.toString(),
validator: (String value) {
if (value.isEmpty ||
!RegExp(r'^(?:[1-9]\d*|0)?(?:\.\d+)?$').hasMatch(value)) {
return 'product price is required,and should be a number';
}
return null;
},
onSaved: (String value) {
_formData['price'] = double.parse(value);
},
);
}
Widget _buildSubmitButton() {
return ScopedModelDescendant<MainModel>(
builder: (BuildContext context, Widget child, MainModel model) {
return model.isloading
? Center(child: CircularProgressIndicator())
: RaisedButton(
child: Text('Save'),
textColor: Colors.white,
onPressed: () => _submitform(
model.addproduct,
model.updateproduct,
model.selectproduct,
model.selectedProductIndex),
);
},
);
}
Widget _buildPageContent(BuildContext context, Product product) {
final double deviceWidth = MediaQuery.of(context).size.width;
final double targetWidth = deviceWidth > 550.0 ? 500.0 : deviceWidth * 0.95;
final double targetPadding = deviceWidth - targetWidth;
return GestureDetector(
onTap: () {
FocusScope.of(context).requestFocus(FocusNode());
},
child: Container(
margin: EdgeInsets.all(10.0),
child: Form(
key: _formkey,
child: ListView(
padding: EdgeInsets.symmetric(horizontal: targetPadding / 2),
children: <Widget>[
_buildtypetextfield(product),
_builddescriptiontextfield(product),
_buildpricetextfield(product),
SizedBox(
height: 10.0,
),
_buildSubmitButton(),
],
),
),
),
);
}
void _submitform(
Function addproduct, Function updateproduct, Function setSelectedProduct,
[int selectedproductIndex]) {
if (!_formkey.currentState.validate()) {
return;
}
_formkey.currentState.save();
//final Map<String, dynamic> product = {
// 'title': _title,
// 'description': _description,
// 'price': _price,
// 'image':
// 'https://cdn.pixabay.com/photo/2020/03/04/07/28/black-4900736_960_720.jpg'
// };
if (selectedproductIndex == -1) {
addproduct(_formData['title'], _formData['description'],
_formData['image'], _formData['price'])
.then((_) => Navigator.pushReplacementNamed(context, '/products')
.then((_) => setSelectedProduct(null)));
} else {
updateproduct(_formData['title'], _formData['description'],
_formData['image'], _formData['price'])
.then((_) => Navigator.pushReplacementNamed(context, '/products')
.then((_) => setSelectedProduct(null)));
}
}
#override
Widget build(BuildContext context) {
return ScopedModelDescendant<MainModel>(
builder: (BuildContext context, Widget child, MainModel model) {
final Widget pageContent =
_buildPageContent(context, model.selectedproduct);
return model.selectedProductIndex == -1
? pageContent
: Scaffold(
appBar: AppBar(
title: Text('Edit Product'),
),
body: pageContent,
);
},
);
}
}
================
and the error occurred in the '_submitform' function block as i was typing the argument context in the show dialog widget
it's a capture of the error
and here is the function , thank u all very much, thanks in advance
_submitform
void _submitform(
Function addproduct, Function updateproduct, Function setSelectedProduct,
[int selectedproductIndex]) {
if (!_formkey.currentState.validate()) {
return;
}
_formkey.currentState.save();
//final Map<String, dynamic> product = {
// 'title': _title,
// 'description': _description,
// 'price': _price,
// 'image':
// 'https://cdn.pixabay.com/photo/2020/03/04/07/28/black-4900736_960_720.jpg'
// };
if (selectedproductIndex == -1) {
addproduct(_formData['title'], _formData['description'],
_formData['image'], _formData['price'])
.then((bool success) {
if (success) {
Navigator.pushReplacementNamed(context, '/products')
.then((_) => setSelectedProduct(null));
} else {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('something went wrong'),
content: Text('please try again!'),
actions: <Widget>[
FlatButton(
onPressed: () => Navigator.of(context).pop(),
child: Text('Okay'),
)
],
);
});
}
});
} else {
updateproduct(_formData['title'], _formData['description'],
_formData['image'], _formData['price'])
.then((_) => Navigator.pushReplacementNamed(context, '/products')
.then((_) => setSelectedProduct(null)));
}
}
showDialog and Navigator requires BuildContext. In your _submitform function add a parameter : BuildContext.
void _submitform(BuildContext context,
Function addproduct, Function updateproduct, Function setSelectedProduct,
[int selectedproductIndex]) {
// code
}
Pass BuildContext here in _submitform function and use that context in showDialog and Navigator. Again you are using _submitform function inside _buildSubmitButton function. So again add a BuildContext parameter in _buildSubmitButton function and pass it to _submitform function and so on. Ultimately you have to pass BuildContext of build function to the _submitform function. This is the flow as per your current code.

How to resolve flutter dropdownButtonFormField dynamic selection checking for dropdownButton's value

I am trying to ask the user to select the type of item in the first dropdown and then select from its corresponding available colours in the second dropdown. However, when after I have selected a colour (i.e. white) and now want to switch to another item that does not have this colour, an error is thrown:
"There should be exactly one item with [DropdownButton]'s value: white. \nEither zero or 2 or more
[DropdownMenuItem]s were detected with the same value"
Please help, I have already tried to setState at various places to update the values but this error still occurs.
The following is my code snippet:
StreamBuilder<QuerySnapshot>(
stream: mainItemsSnapshots,
builder: (context, snapshot) {
if (snapshot.hasError) return Text("Error");
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
default:
{
List<DropdownMenuItem> dropdownMenuItems =
snapshot.data.documents
.map((DocumentSnapshot mainItem) {
return new DropdownMenuItem<String>(
value: mainItem.documentID,
child: Text(mainItem.documentID),
);
}).toList();
return DropdownButtonFormField<String>(
items: dropdownMenuItems,
onChanged: (String value) {
if (value != tempMainItemType) {
setState(() {
tempMainItemType = value;
tempItemColorsList.clear();
tempItemColorsList = [];
tempMainItemColor = null;
});
}
if (tempItemColorsList.isEmpty && value != null) {
tempItemColorsList = snapshot.data.documents
.where((element) => element.documentID == value)
.first
.data["colors"]
.keys
.map((color) => color.toString())
.toList()
.cast<String>();
}
setState((){});
},
onSaved: (String value) {
_order.mainItemType = value;
},
value: tempMainItemType,
);
}
}
},
),
// Main color
if (tempItemColorsList?.isNotEmpty)
Padding(
padding: const EdgeInsets.only(top: spacingGeneral),
child: textFieldLabel(context, "Main color"),
),
if (tempItemColorsList?.isNotEmpty)
DropdownButtonFormField(
items: tempItemColorsList.map((String color) {
return new DropdownMenuItem<String>(
value: color,
child: Text(color),
);
}).toList(),
onSaved: (String value) {
_order.mainColor = value;
},
value: tempMainItemColor,
onChanged: (String value) {
setState(() {
tempMainItemColor = value;
});
},
),
This maybe too late, but you can create a Map<String, List<String>> where the keys are the items of the first dropdown list, and the value will be the items of second dropdown list.
Here, I created a state that stores the selected item of the first dropdown list. Then I used it to map the items of the second dropdown list.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: SampleDD(),
);
}
}
class SampleDD extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: DoubleDropdown(
items: <String, List<String>>{
'dark colors': ['black', 'gray', 'brown'],
'light colors': ['white', 'yellow', 'green'],
},
onChangedFirst: (val) => print('Selected first: $val'),
onChangedSecond: (val) => print('Selected second: $val'),
),
),
);
}
}
class DoubleDropdown extends StatefulWidget {
DoubleDropdown({
#required this.items,
#required this.onChangedFirst,
#required this.onChangedSecond,
});
final Map<String, List<String>> items;
final ValueChanged<String> onChangedFirst;
final ValueChanged<String> onChangedSecond;
#override
_DoubleDropdownState createState() => _DoubleDropdownState();
}
class _DoubleDropdownState extends State<DoubleDropdown> {
String selectedKey;
#override
void initState() {
super.initState();
selectedKey = widget.items.keys.first;
}
#override
Widget build(BuildContext context) {
return Column(
children: [
_buildFirstDropdown(),
_buildSecondDowndown(),
],
);
}
Widget _buildFirstDropdown() => DropdownButtonFormField<String>(
items: widget.items.keys.map((e) {
return DropdownMenuItem<String>(
child: Text(e),
value: e,
);
}).toList(),
onChanged: (val) {
setState(() => selectedKey = val);
widget.onChangedFirst(val);
},
value: selectedKey,
);
Widget _buildSecondDowndown() => DropdownButtonFormField<String>(
items: widget.items[selectedKey].map((e) {
return DropdownMenuItem<String>(
child: Text(e),
value: e,
);
}).toList(),
onChanged: widget.onChangedSecond,
value: widget.items[selectedKey].first,
);
}

Flutter - Stream error not persisting (using bloc pattern)

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);
},
);
}