Unhandled Exception: Bad state: Cannot add new events after calling close? - flutter

I'm making a login feature, I'm making fire responses to find out whether responses were successful or failed. can see my BLOC coding. the login process was successful but when I want to return to the login page after logging out the error appears Unhandled Exception: Bad state: Cannot add new events after calling close. how can I handle it?
API RESPONSES :
class ApiResponse<T> {
Status status;
T data;
String message;
ApiResponse.loading(this.message) : status = Status.LOADING;
ApiResponse.completed(this.data) : status = Status.COMPLETED;
ApiResponse.error(this.message) : status = Status.ERROR;
// #override
// String toString() {
// return "Status : $status \n Message : $message \n Data : $data";
// }
}
enum Status { LOADING, COMPLETED, ERROR }
BLOC :
class LoginBloc extends Object with Validators{
final _repository = EresidenceRepository();
final _userid = BehaviorSubject<String>();
final _password = BehaviorSubject<String>();
final _imei = BehaviorSubject<String>();
final _coordinate = BehaviorSubject<String>();
final BehaviorSubject<ApiResponse<login_responses>> _subject = BehaviorSubject<ApiResponse<login_responses>>();
Function(String) get userid => _userid.sink.add;
Function(String) get password => _password.sink.add;
Function(String) get imei => _imei.sink.add;
Function(String) get coordinate => _coordinate.sink.add;
Stream<String> get useridValidation => _userid.stream.transform(useridValidator);
Stream<String> get passwordValidation => _password.stream.transform(passwordValidator);
Stream<bool> get submitCheck => Rx.combineLatest2(useridValidation, passwordValidation, (e,p) => true);
login() async {
_subject.sink.add(ApiResponse.loading("Logging In..."));
try {
login_responses response = await _repository.login(
_userid.value, _password.value, _imei.value, _coordinate.value);
prefsBloc.changePrefsLogin(
PrefsState(false, response.data.userid, response.data.password, _imei.value, _coordinate.value, "")
);
_subject.sink.add(ApiResponse.completed(response));
print(response);
} catch (e) {
_subject.sink.add(ApiResponse.error(e.toString()));
print(e);
}
}
dispose(){
_userid.close();
_password.close();
_imei.close();
_coordinate.close();
_subject.close();
}
BehaviorSubject<ApiResponse<login_responses>> get subject => _subject;
}
final login = LoginBloc();
UI :
class LoginPage extends StatefulWidget {
#override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> with WidgetsBindingObserver{
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
FocusNode passwordFocusNode, useridFocusNode;
#override
void initState() {
super.initState();
prefsBloc.checkLoginPref(context);
WidgetsBinding.instance.addObserver(this);
}
#override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
#override
Widget build(BuildContext context) {
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle.dark,
child: Scaffold(
backgroundColor: Colors.white,
body: StreamBuilder(
stream: login.subject,
builder: (context, AsyncSnapshot<ApiResponse<login_responses>> snapshot){
if(snapshot.hasData) {
print(snapshot.data.status);
switch (snapshot.data.status) {
case Status.LOADING:
_onWidgetDidBuild((){
Scaffold.of(context).showSnackBar(
SnackBar(
content: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(snapshot.data.message),
CircularProgressIndicator(),
],
),
backgroundColor: Colors.black,
),
);
});
break;
case Status.COMPLETED:
login_responses result = snapshot.data.data;
if(result.data.bit70 == "000") {
_onWidgetDidBuild((){
login.dispose();
AppRoutes.replace(context, LoginVerifyPage());
});
}else{
_onWidgetDidBuild((){
login.dispose();
AppRoutes.replace(context, MainApp());
});
}
break;
case Status.ERROR:
_onWidgetDidBuild(() {
Scaffold.of(context).showSnackBar(SnackBar(
content: Text('${snapshot.data.message}'),
backgroundColor: Colors.red,
));
});
break;
}
}
return _formLogin();
}
),
)
);
}
_formLogin() {
return SafeArea(
child: Container(
child: Column(
children: <Widget>[
Expanded(
flex: 1,
child: Container(
padding: EdgeInsets.symmetric(horizontal: SizeConfig.widthMultiplier * 1, vertical: SizeConfig.heightMultiplier * 1),
child: CachedNetworkImage(
imageUrl: "https://images.glints.com/unsafe/1024x0/glints-dashboard.s3.amazonaws.com/company-logo/68545821966f833d182f98775c73c7ae.png",
errorWidget: (context, url, error) => Icon(Icons.broken_image),
fit: BoxFit.fill,
),
)
),
Expanded(
flex: 2,
child: Container(
padding: EdgeInsets.all(SizeConfig.heightMultiplier * 2),
child: Column(
children: <Widget>[
Container(
child: StreamBuilder<String>(
stream: login.useridValidation,
builder: (context, snapshot) => DataTextField(
errorText: snapshot.error,
hintText: "No Handphone",
textInputAction: TextInputAction.next,
icon: Icons.phone,
onSubmitted: () => FocusScope.of(context).requestFocus(passwordFocusNode),
onChanged: login.userid,
keyboardType: TextInputType.numberWithOptions(signed: true, decimal: true),
),
)
),
Container(
margin: EdgeInsets.only(top: SizeConfig.heightMultiplier * 2),
child: StreamBuilder<String>(
stream: login.passwordValidation,
builder: (context, snapshot) => PasswordTextField(
errorText: snapshot.error,
hintText: "Password",
textInputAction: TextInputAction.done,
onSubmitted: () {
FocusScope.of(context).requestFocus(FocusNode());
},
onChanged: login.password,
focusNode: passwordFocusNode,
),
)
),
Container(
width: SizeConfig.screenWidth,
margin: EdgeInsets.only(top: SizeConfig.heightMultiplier * 2.5),
child: GestureDetector(
onTap: () => AppRoutes.push(context, ForgotPasswordPage()),
child: Text(
Strings.titleForgotPass+" ?",
style: AppTheme.styleSubTitlePurpel,
textAlign: TextAlign.right,
),
)
),
Container(
width: SizeConfig.screenWidth,
margin: EdgeInsets.only(top: SizeConfig.heightMultiplier * 5),
child: StreamBuilder<bool>(
stream: login.submitCheck,
builder: (context, snapshot) => AppButton(
onPressed: snapshot.hasData ? () => login.login() : null,
text: Strings.signin
),
)
)
],
)
)
),
Expanded(
flex: 1,
child: Container(
alignment: Alignment.bottomCenter,
margin: EdgeInsets.only(bottom: SizeConfig.heightMultiplier * 2.5),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
Strings.dontAccount,
style: AppTheme.styleSubTitleBlackSmall,
textAlign: TextAlign.right,
),
Container(
margin: EdgeInsets.only(left: SizeConfig.widthMultiplier * 1),
child: InkWell(
onTap: () => AppRoutes.push(context, RegistrationPage()),
child: Text(
Strings.registration,
style: AppTheme.styleSubTitlePurpel,
textAlign: TextAlign.right,
),
)
)
],
)
)
)
],
),
),
);
}
void _onWidgetDidBuild(Function callback) {
WidgetsBinding.instance.addPostFrameCallback((_) {
callback();
});
}
}

The problem is that you have created an instance of your bloc globally (which is not a good practice), and after the login process is done you have called login.dispose() which closes all of the streams in your LoginBloc, and you can't add new events to closed streams.
You'd better create an instance of your LoginBloc in your LoginPage initState method, and close it in the dispose method.
This way, whenever you navigate to login page, a new bloc is created and it would work as expected.
UPDATE:
A simple example:
class LoginPage extends StatefulWidget {
#override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
LoginBloc _loginBloc;
#override
void initState() {
super.initState();
_loginBloc = LoginBloc();
}
#override
void dispose() {
_loginBloc.close();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Container();
}
}

Related

Exception caught by widgets library - Bad state

import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class ChatScreen extends StatefulWidget {
static const String screenRoute = 'chat_screen';
const ChatScreen({Key? key}) : super(key: key);
#override
State<ChatScreen> createState() => _ChatScreenState();
}
class _ChatScreenState extends State<ChatScreen> {
final _firestore = FirebaseFirestore.instance;
final _auth = FirebaseAuth.instance;
late User signedInUser;
String? messageText;
#override
void initState() {
super.initState();
getCurrentUser();
}
void getCurrentUser() {
try {
final user = _auth.currentUser;
if (user != null) {
signedInUser = user;
print(signedInUser.email);
}
} catch (e) {
print(e);
}
}
//void getMessages() async {
// final messages = await _firestore.collection('messages').get();
//for (var message in messages.docs) {
// print(message.data());
// }
//}
void messagesStream() async {
await for (var snapshot in _firestore.collection('messages').snapshots()) {
for (var message in snapshot.docs) {
print(message.data());
}
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.yellow[900],
title: Row(
children: [
Image.asset('assets/images/logo.png', height: 25),
const SizedBox(width: 10),
const Text('ChatMe'),
],
),
actions: [
IconButton(
onPressed: () {
messagesStream();
//_auth.signOut();
//Navigator.pop(context);
},
icon: const Icon(Icons.download),
),
],
),
body: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
StreamBuilder<QuerySnapshot>(
stream: _firestore.collection('messages').snapshots(),
builder: (context, snapshot) {
List<Text> messageWidgets = [];
if (!snapshot.hasData) {
return const Center(
child: CircularProgressIndicator(
backgroundColor: Colors.blue,
),
);
}
final messages = snapshot.data!.docs;
for (var message in messages) {
final messageText = message.get('text');
final messageSender = message.get('sender');
final messageWidget = Text('$messageText - $messageSender');
messageWidgets.add(messageWidget);
}
return ListView(
children: messageWidgets,
);
},
),
Container(
decoration: const BoxDecoration(
border: Border(
top: BorderSide(
color: Colors.orange,
width: 2,
),
),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: TextField(
onChanged: (value) {
messageText = value;
},
decoration: const InputDecoration(
contentPadding: EdgeInsets.symmetric(
vertical: 10,
horizontal: 20,
),
hintText: 'Write Your Message Here..',
border: InputBorder.none,
),
),
),
TextButton(
onPressed: () {
_firestore.collection('messages').add({
'text': messageText,
'sender': signedInUser.email,
});
},
child: Text(
'send',
style: TextStyle(
color: Colors.blue[800],
fontWeight: FontWeight.bold,
fontSize: 18,
),
),
),
],
),
),
],
),
),
);
}
}
════════ Exception caught by widgets library ═══════════════════════════════════
Bad state: field does not exist within the DocumentSnapshotPlatform
The relevant error-causing widget was
StreamBuilder<QuerySnapshot<Object?>>
lib\…\screens\chat_screen.dart:82
════════════════════════════════════════════════════════════════════════════════

flutter riverpod leaving screen then come back it doesn't maintain the state

So I have two screens:
-Book_screen to display all the books(click on any book to go to article_screen)
-article_screen to display articles
In article_screen, I can click on article to save it as favorites.
but when I go back to book_screen then come back to article_screen, those favorited articles doesn't show the favorited status(icon red heart).
this is my article screen code:
class ArticleENPage extends ConsumerStatefulWidget{
final String bookName;
const ArticleENPage({Key? key,#PathParam() required this.bookName,}) : super(key: key);
#override
ArticleENScreen createState()=> ArticleENScreen();
}
class ArticleENScreen extends ConsumerState<ArticleENPage> {
late Future<List<Code>> codes;
#override
void initState() {
super.initState();
codes = fetchCodes();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(widget.bookName,style: const TextStyle(fontSize: 24,fontWeight: FontWeight.bold),),backgroundColor: Colors.white,foregroundColor: Colors.black,elevation: 0,),
body: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
//SizedBox(height: 10),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 15.0),
child: Container(
margin: const EdgeInsets.only(top:10),
height: 43,
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 2),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(50),
border: Border.all(
color: Colors.black.withOpacity(0.32),
),
),
child: Consumer(
builder: (context,ref,_) {
return TextField(
onChanged: (value) {
searchStringController controller = ref.read(searchStringProvider.notifier);
controller.setText(value.toLowerCase());
},
decoration: const InputDecoration(
border: InputBorder.none,
icon: Icon(Icons.search,size:18),
hintText: "Search Here",
hintStyle: TextStyle(color: Color.fromRGBO(128,128, 128, 1)),
),
);
}
),
),
),
const SizedBox(height: 10),
Expanded(
child: FutureBuilder(
builder: (context, AsyncSnapshot<List<Code>> snapshot) {
if (snapshot.hasData) {
return Center(
child: Consumer(
builder: (context,ref,child) {
final searchString = ref.watch(searchStringProvider);
return ListView.separated(
padding: const EdgeInsets.all(8),
itemCount: snapshot.data!.length,
itemBuilder: (BuildContext context, int index) {
return snapshot.data![index].name
.toLowerCase()
.contains(searchString) ||
snapshot.data![index].description
.toLowerCase()
.contains(searchString)
? Consumer(
builder: (context,ref,child) {
final favlist = ref.watch(FavoriteListController.favoriteListProvider);
print(favlist);
final alreadySaved = favlist.contains(snapshot.data![index]);
return Card(
child:Padding(
padding: const EdgeInsets.all(10),
child:ExpandableNotifier(
child: ScrollOnExpand(
child: ExpandablePanel(
theme: const ExpandableThemeData(hasIcon: true),
header: RichText(text: TextSpan(children: highlight(snapshot.data![index].name, searchString,'title')),),
collapsed: RichText(text: TextSpan(children: highlight(snapshot.data![index].description, searchString,'content')), softWrap: true, maxLines: 3, overflow: TextOverflow.ellipsis,),
expanded: Column(
children: [
RichText(text: TextSpan(children: highlight(snapshot.data![index].description, searchString,'content')), softWrap: true ),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
IconButton(
icon: Icon(
alreadySaved ? Icons.favorite : Icons.favorite_border,
color: alreadySaved ? Colors.red : null,
semanticLabel: alreadySaved ? 'Remove from saved' : 'Save',
),
onPressed: () {
FavoriteListController controller = ref.read(FavoriteListController.favoriteListProvider.notifier);
if (alreadySaved) {
controller.toggle(snapshot.data![index]);
} else {
controller.toggle(snapshot.data![index]);
}
},
),
IconButton(
icon: const Icon(Icons.content_copy),
onPressed: () {
setState(() {
Clipboard.setData(ClipboardData(text: snapshot.data![index].name+"\n"+snapshot.data![index].description))
.then((value) {
ScaffoldMessenger.of(context).showSnackBar(new SnackBar(content: Text('Copied')));
},);
});
},
),],),],)),),)));})
: Container();
},
separatorBuilder: (BuildContext context, int index) {
return snapshot.data![index].name
.toLowerCase()
.contains(searchString) ||
snapshot.data![index].description
.toLowerCase()
.contains(searchString)
? Divider()
: Container();
},
);
}
),
);
} else if (snapshot.hasError) {
return const Center(child: Text('Something went wrong :('));
}
return const Align(alignment:Alignment.topCenter,child:CircularProgressIndicator());
},
future: codes,
),
),
],
),
);
}
//read from files
Future<List<Code>> fetchCodes() async {
final response =
await rootBundle.loadString('assets/articles.json');
var CodeJson = json.decode(response)[widget.bookName] as List<dynamic>;
return CodeJson.map((code) => Code.fromJson(code)).toList();
}
}
I tried using riverpod for provider and save to sharedpreference the list of code that I favorited.
final sharedPrefs =
FutureProvider<SharedPreferences>((_) async => await SharedPreferences.getInstance());
class FavoriteListController extends StateNotifier<List<Code>>{
FavoriteListController(this.pref) : super(Code.decode(pref?.getString("favcode")??""));
static final favoriteListProvider = StateNotifierProvider<FavoriteListController, List<Code>>((ref) {
final pref = ref.watch(sharedPrefs).maybeWhen(
data: (value) => value,
orElse: () => null,
);
print(pref?.getString("favcode"));
return FavoriteListController(pref);
});
final SharedPreferences? pref;
void toggle(Code code) {
if (state.contains(code)) {
state = state.where((id) => id != code).toList();
} else {
state = [...state, code];
}
final String encodedData = Code.encode(state);
pref!.setString("favcode", encodedData);
}
}
I am not sure what is the cause of this but I think it might be because of futurebuilder? I am confused to how to solve this issue...
I am stuck in a dead end so any help or advice would be really appreciated
edit 1-
this is my source code in case I have not include all the necessary codes
https://github.com/sopheareachte/LawCode
edit-2
do I need to change "late Future<List> codes;" that fetch all the codes for futurebuilder to riverpod futureprovider too for it to work?
Maybe the problem is, that you define a static provider inside of your controller class. Try this code:
final sharedPrefs = FutureProvider<SharedPreferences>((_) async => await SharedPreferences.getInstance());
final favoriteListProvider = StateNotifierProvider<FavoriteListController, List<Code>>((ref) {
final pref = ref.watch(sharedPrefs).maybeWhen(
data: (value) => value,
orElse: () => null,
);
print(pref?.getString("favcode"));
return FavoriteListController(pref);
});
class FavoriteListController extends StateNotifier<List<Code>>{
FavoriteListController(this.pref) : super(Code.decode(pref?.getString("favcode")??""));
final SharedPreferences? pref;
void toggle(Code code) {
if (state.contains(code)) {
state = state.where((id) => id != code).toList();
} else {
state = [...state, code];
}
final String encodedData = Code.encode(state);
pref!.setString("favcode", encodedData);
}
}

The method 'getProducts' was called on null

i am trying to retrieve data using rest api from woocommerce website using flutter
this is the api for retrieve json data
Future<List<Product>> getProducts(String tagId) async {
List<Product> data = new List<Product>();
try {
String url = Config.url +
Config.productsURL +
"?consumer_key=${Config.key}&consumer_secret=${Config.secret}&tag=$tagId";
var response = await Dio().get(url,
options: new Options(
headers: {HttpHeaders.contentTypeHeader: "application/json"}));
if (response.statusCode == 200) {
data = (response.data as List).map((i) => Product.fromJson(i),).toList();
}
} on DioError catch (e) {
print(e.response);
}
return data;
}
this is the widget to handle the data to the mobile app
class WidgetHomeProducts extends StatefulWidget {
WidgetHomeProducts({Key key, this.labelName, this.tagId}) : super(key : key);
String labelName;
String tagId;
#override
_WidgetHomeProductsState createState() => _WidgetHomeProductsState();
}
class _WidgetHomeProductsState extends State<WidgetHomeProducts> {
APIServices apiServices;
#override
void initState() {
apiServices = new APIServices();
super.initState();
}
#override
Widget build(BuildContext context) {
return Container(
color: const Color(0xffF4F7FA),
child: Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Padding(
padding: EdgeInsets.only(left: 16, top: 4),
child: Text(
this.widget.labelName,
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
),
Padding(
padding: EdgeInsets.only(left: 16, top: 4),
child: FlatButton(
onPressed: () {},
child: Text(
'View All',
style: TextStyle(color: Colors.blueAccent),
),
),
),
],
),
_productList(),
],
),
);
}
Widget _productList(){
return new FutureBuilder(
future: apiServices.getProducts(this.widget.tagId),
builder: (BuildContext context, AsyncSnapshot<List<Product>> model){
if(model.hasData){
return _buildList(model.data);
}if(model.hasError){
print("error");
}
return Center(child: CircularProgressIndicator(),);
});
}
i got The method error message that says
'getProducts' was called on null.
Receiver: null
Tried calling: getProducts("971")
can anyone help me to fix this?

implement onTap in flutter

I try to create an onTap function on ChatRoomListTile which navigates me to the chatscreen where the id is = chatRoomId. Unfortunately I don't know how to deliver the ID.
Unfortunately I am very unexperienced in flutter and I made this app via tutorial... It works nice but I am still learning, so a big big big thanks!
my Database.dart
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:signal/helperfunctions/sharedpref_helper.dart';
class DatabaseMethods{
Future addUserInfoToDB(String userId, Map<String, dynamic>userInfoMap)
async {
return FirebaseFirestore.instance
.collection("users")
.doc(userId)
.set(userInfoMap);
}
Future<Stream<QuerySnapshot>> getUserByUserName(String username) async{
return FirebaseFirestore.instance
.collection("users")
.where("username", isEqualTo: username)
.snapshots();
}
Future addMessage(String chatRoomId, String messageId, Map messageInfoMap) async {
return FirebaseFirestore.instance
.collection ("chatrooms")
.doc(chatRoomId)
.collection("chats")
.doc(messageId)
.set(messageInfoMap);
}
updateLastMessageSend(String chatRoomId, Map lastMessageInfoMap){
return FirebaseFirestore.instance
.collection("chatrooms")
.doc(chatRoomId)
.update(lastMessageInfoMap);
}
createChatRoom(String chatRoomId, Map chatRoomInfoMap) async{
final snapShot = await FirebaseFirestore.instance
.collection("chatrooms")
.doc(chatRoomId)
.get();
if(snapShot.exists){
//chatroom already exists
return true;
}else{
//chatroom does not exists
return FirebaseFirestore.instance
.collection("chatrooms")
.doc(chatRoomId)
.set(chatRoomInfoMap);
}
}
Future<Stream<QuerySnapshot>> getChatRoomMessages(chatRoomId) async {
return FirebaseFirestore.instance
.collection("chatrooms")
.doc(chatRoomId)
.collection("chats")
.orderBy("ts", descending: true)
.snapshots();
}
// Future<Stream<QuerySnapshot>> openChatRoom(String chatRoomId) async{
// return FirebaseFirestore.instance
// .collection("chatrooms")
// .doc(chatRoomId)
// .collection("chats")
// .snapshots();
// }
Future<Stream<QuerySnapshot>> getChatRooms() async {
String myUsername = await SharedPreferenceHelper().getUserName();
return FirebaseFirestore.instance
.collection("chatrooms")
.orderBy("lastMessageSendTs", descending: true)
.where("users",arrayContains: myUsername)
.snapshots();
}
Future<QuerySnapshot> getUserInfo(String username) async {
return await FirebaseFirestore.instance
.collection("users")
.where("username", isEqualTo: username)
.get();
}
}
and my home.dart
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:signal/helperfunctions/sharedpref_helper.dart';
import 'package:signal/services/auth.dart';
import 'package:signal/services/database.dart';
import 'package:signal/views/signin.dart';
import 'services/colorpicker.dart';
import 'chatscreen.dart';
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
bool isSearching = false;
String myName, myProfilePic, myUserName, myEmail, messageSentTs;
Stream usersStream, chatRoomsStream;
TextEditingController searchUsernameEditingController =
TextEditingController();
getMyInfoFromSharedPreference() async{
myName = await SharedPreferenceHelper().getDisplayName();
myProfilePic = await SharedPreferenceHelper().getUserProfileUrl();
myUserName = await SharedPreferenceHelper().getUserName();
myEmail = await SharedPreferenceHelper().getUserEmail();
}
//dieser string sollte eigentlich aus sicherheitsgründen random generiert werden
getChatRoomIdByUsernames(String a, String b){
if(a.substring(0,1).codeUnitAt(0) > b.substring(0,1).codeUnitAt(0)){
return "$b\_$a";
}else{
return "$a\_$b";
}
}
onSearchBtnClick() async {
isSearching = true;
setState(() {});
usersStream = await DatabaseMethods()
.getUserByUserName(searchUsernameEditingController.text);
setState(() {});
}
Widget chatRoomsList(){
return StreamBuilder(
stream: chatRoomsStream,
builder: (context, snapshot){
return snapshot.hasData
? ListView.builder(
itemCount: snapshot.data.docs.length,
shrinkWrap: true,
itemBuilder: (context, index){
DocumentSnapshot ds = snapshot.data.docs[index];
return ChatRoomListTile(ds["lastMessage"], ds.id,myUserName);
})
: Center(child: CircularProgressIndicator());
},
);
}
// print("this is the values we have $myUserName $username"));
// Chatroom ID Kontrolle ( achtung beim randomisen der CRId)
Widget searchUserListTile({String profileUrl, name, username, email}){
return GestureDetector(
onTap: (){
var chatRoomId = getChatRoomIdByUsernames(myUserName, username);
Map<String, dynamic> chatRoomInfoMap = {
"users" : [myUserName, username]
};
DatabaseMethods().createChatRoom(chatRoomId, chatRoomInfoMap);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ChatScreen(username, name)
)
);
},
child: Row(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(30),
child: Image.network(
profileUrl,
height: 20,
width: 20,
),
),
SizedBox(width: 12),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children:[
Text(name),
Text(email),
],
)
],
),
);
}
Widget searchUsersList(){
return StreamBuilder(
stream: usersStream,
builder: (context, snapshot){
return snapshot.hasData
? ListView.builder(
itemCount: snapshot.data.docs.length,
shrinkWrap: true,
itemBuilder: (context, index){
DocumentSnapshot ds = snapshot.data.docs[index];
return searchUserListTile(
profileUrl: ds["imgUrl"],
name: ds["name"],
username: ds["username"],
email: ds["email"]
);
},
) : Center(child: CircularProgressIndicator(),);
}
);
}
getChatRooms() async {
chatRoomsStream = await DatabaseMethods().getChatRooms();
setState(() {});
}
onScreenLoaded() async {
await getMyInfoFromSharedPreference();
getChatRooms();
}
#override
void initState() {
onScreenLoaded();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color.fromRGBO(233, 240, 244, 1),
appBar: AppBar(
title: Image.asset('assets/images/logo.png', width: 40,),
elevation: 0.0,
backgroundColor: Colors.white,
actions: [
InkWell(
onTap: (){
AuthMethods().signOut().then((s) {
Navigator.pushReplacement(context, MaterialPageRoute
(builder: (context) => SignIn()));
});
},
child: Container(
padding: EdgeInsets.symmetric(horizontal: 16),
child: Icon(Icons.exit_to_app)),
)
],
),
body: Container(
margin: EdgeInsets.symmetric(horizontal: 20),
child: Column(
children: [
Row(
children: [
isSearching
? GestureDetector(
onTap: (){
isSearching = false;
searchUsernameEditingController.text = "";
setState(() {});
},
child: Padding(
padding: EdgeInsets.only(right: 12),
child: Icon(Icons.arrow_back)),
) : Container(),
Expanded(
child: Container(
// Disable Searchbar
width: 0,
height: 0,
margin: EdgeInsets.symmetric(vertical: 16),
padding: EdgeInsets.symmetric(horizontal:16),
decoration: BoxDecoration
(border: Border.all(
color: Colors.black87,
width: 1,
style: BorderStyle.solid), borderRadius: BorderRadius.circular(24)),
child: Row(
children: [
Expanded(
child: TextField(
controller: searchUsernameEditingController,
decoration: InputDecoration(
border: InputBorder.none, hintText: "Finde einen Freund"),
)),
GestureDetector(
onTap:(){
if(searchUsernameEditingController.text != ""){
onSearchBtnClick();
}
},
// Disable Search Icon
// child: Icon(Icons.search),
)
],
),
),
)
]
),
isSearching ? searchUsersList() : chatRoomsList()
],
),
),
);
}
}
class ChatRoomListTile extends StatefulWidget {
final String lastMessage, chatRoomId, myUsername;
ChatRoomListTile(this.lastMessage, this.chatRoomId, this.myUsername);
#override
_ChatRoomListTileState createState() => _ChatRoomListTileState();
}
class _ChatRoomListTileState extends State<ChatRoomListTile> {
String profilePicUrl ="", name="", username="";
getThisUserInfo() async {
username = widget.chatRoomId.replaceAll(widget.myUsername, "").replaceAll("_", "");
QuerySnapshot querySnapshot = await DatabaseMethods().getUserInfo(username);
name = "${querySnapshot.docs[0]["name"]}";
profilePicUrl = "${querySnapshot.docs[0]["imgUrl"]}";
setState(() {});
}
#override
void initState() {
getThisUserInfo();
super.initState();
}
#override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.symmetric(vertical: 2, horizontal: 0,),
padding: EdgeInsets.symmetric(horizontal:12,vertical: 20,),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
),
child: Row(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image.network(
profilePicUrl,
height: 35,
width: 35,
),
),
SizedBox(width: 15),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
name,
style: TextStyle(
fontSize: 14,
fontFamily: 'Roboto',
color: Color.fromRGBO(57, 59, 85, 0.8),
fontWeight: FontWeight.w500,
),
),
SizedBox(height: 5),
Container(
width: (MediaQuery.of(context).size.width) - 150,
child: Text(
widget.lastMessage,
style: new TextStyle(
fontSize: 13.0,
fontFamily: 'Roboto',
color: Color.fromRGBO(171, 183, 201, 1),
fontWeight: FontWeight.w400,
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
),
],
)
],
),
);
}
}
you can pass the id as a constructor from the listTile,
Navigator.of(context).push.... ChatScreen(id: chatRoomId)
as so, and in ChatScreen you can receive it like so
class ChatScreenextends StatefulWidget {
final String id;
ChatScreen({requried this.id});
#override
_ChatScreenState createState() => _ChatScreenState ();
}
class _ChatScreenState extends State<ChatScreen> {
if it is a stateless Widget class you can directly access the id as id, if it is a stateful Widget then widget.id ,
Another way of doing this is passing as Navigator argument you can check the docs for more info Flutter Docs
Hello and thank you very much for your time ... i tried to implement it but unfortunate i get an error. here the GestureDetector i added to my home.dart ::: I also tried to change id: chatRoomId to this.Id or just chatRoomId .. but it keeps underlined and erroring :
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => ChatScreen(id: chatRoomId)));
},
child: Container(
margin: EdgeInsets.symmetric(vertical: 2, horizontal: 0,),
padding: EdgeInsets.symmetric(horizontal:12,vertical: 20,),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
),
and here the update of the chatscreen.dart
class ChatScreen extends StatefulWidget {
final String chatWidthUsername, name, id;
ChatScreen(this.chatWidthUsername, this.name, {this.id});
#override
_ChatScreenState createState() => _ChatScreenState();
}
class _ChatScreenState extends State<ChatScreen> {
String chatRoomId, messageId = "";
Stream messageStream;
String myName, myProfilePic, myUserName, myEmail;
TextEditingController messageTextEditingController = TextEditingController();
thanks again so much !
greetings
alex

Snackbar appearing repeatedly after appearing once

I have shown snackbar after incorrect login in flutter. But after appearing once, the snackbar is appearing repeatedly without even pressing the submit button. Where am I doing wrong? Please help. Here is my code below. In the snapshot.data.status == '2' portion I have showing the snackbar. I want the snackbar to be displayed only when the login falis i.e. when snapshot.data.status == '2' satisfies.
class MyApp extends StatefulWidget {
final UserDataStorage storage;
MyApp({Key key, #required this.storage}) : super(key: key);
#override
State<StatefulWidget> createState() {
return _TextControl();
}
}
class _TextControl extends State<MyApp> {
final _formKey = GlobalKey<FormState>();
bool snackBarActive = false;
#override
void initState() {
super.initState();
widget.storage.readCounter().then((int value) {
if (value > 0) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => TextInput(storage: GetDataStorage())),
);
}
});
}
final TextEditingController _controller = TextEditingController();
final TextEditingController _controller2 = TextEditingController();
#override
void dispose() {
_controller.dispose();
_controller2.dispose();
super.dispose();
}
Future<Login> _futureLogin;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
backgroundColor: Colors.purple[400],
title: Text('Login'),
),
body: Container(
padding: EdgeInsets.only(left: 15, top: 20, right: 15, bottom: 20),
child: Column(
children: <Widget>[
Center(
child: Image(image: AssetImage('assets/images/logo.png')),
),
Container(
padding: EdgeInsets.only(left: 0, top: 20, right: 0, bottom: 0),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Column(
children: <Widget>[
TextFormField(
controller: _controller,
decoration: InputDecoration(hintText: 'Email'),
keyboardType: TextInputType.emailAddress,
validator: (value) {
if (value.isEmpty || !value.isValidEmail()) {
return 'Please enter valid email.';
}
return null;
},
),
TextFormField(
controller: _controller2,
decoration: InputDecoration(hintText: 'Password'),
obscureText: true,
validator: (value) {
if (value.isEmpty) {
return 'Please enter password.';
}
return null;
},
),
Container(
margin: EdgeInsets.only(top: 10.0),
child: RaisedButton(
color: Colors.purple[400],
padding: EdgeInsets.only(
left: 130, top: 10, right: 130, bottom: 10),
textColor: Colors.white,
onPressed: () {
if (_formKey.currentState.validate()) {
setState(() {
_futureLogin = userLogin(
_controller.text, _controller2.text);
});
Scaffold.of(context)
.removeCurrentSnackBar();
}
},
child: Text('Login',
style: TextStyle(fontSize: 18.0)),
),
),
],
),
(_futureLogin != null)
? FutureBuilder<Login>(
future: _futureLogin,
builder: (context, snapshot) {
if (snapshot.hasData) {
if (snapshot.data.status == '1') {
widget.storage
.writeCounter(snapshot.data.usrid);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => TextInput(
storage: GetDataStorage())),
);
} else if (snapshot.data.status == '2') {
snackBarActive = true;
if (snackBarActive) {
Scaffold.of(context)
.showSnackBar(SnackBar(
content: Text(
"Invalid login credentials.",
style: TextStyle(
color: Colors.red[100])),
))
.closed
.whenComplete(() {
setState(() {
snackBarActive = false;
});
});
}
return Text('');
}
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
return Center(
child: CircularProgressIndicator());
},
)
: Text(''),
],
)),
),
],
)),
);
}
}
Have you tried changing the status variable to 1 after checking snapshot.data.status == '2', try that and it might help you stop the continuous snackbar widget showing up.
Write this code before calling snackbar,
ScaffoldMessenger.of(context).hideCurrentSnackBar();
example;
getSnackBar(BuildContext context, String snackText) {
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text(snackText)));
}