I am facing an error as the below one while retrieving data from FireStore:
The following NoSuchMethodError was thrown building BlocBuilder<AppCubit, AppStates>(dirty, state: _BlocBuilderBaseState<AppCubit, AppStates>#783ea):
The getter 'cover' was called on null.
Receiver: null
Tried calling: cover
So realised that this error because of the dependency injection and I found that userModel is null and this is the userModel class I have:
class UserModel {
String name;
String email;
String phone;
String uId;
String image;
String cover;
String bio;
bool isEmailVerified;
UserModel({
this.name,
this.email,
this.phone,
this.uId,
this.image,
this.cover,
this.bio,
this.isEmailVerified,
});
UserModel.fromJson(Map<String, dynamic> json) {
email = json['email'];
name = json['name'];
phone = json['phone'];
uId = json['uId'];
image = json['image'];
cover = json['cover'];
bio = json['bio'];
isEmailVerified = json['isEmailVerified'];
}
Map<String, dynamic> toMap() {
return {
'name': name,
'email': email,
'phone': phone,
'uId': uId,
'image': image,
'cover': cover,
'bio': bio,
'isEmailVerified': isEmailVerified,
};
}
}
and I created a getUserData method in my Cubit class as the below method:
UserModel userModel;
void getUserData() {
emit(AppGetUserLoadingState());
FirebaseFirestore.instance.collection('users').doc(uId).get().then((value) {
// print(value.data());
userModel = UserModel.fromJson(value.data());
emit(
AppGetUserSuccessState(),
);
}).catchError((error) {
print(error.toString());
emit(
AppGetUserErrorState(
error.toString(),
),
);
});
}
as I am calling this method when the app starts in the main like the below code:
Widget build(BuildContext context) {
return BlocProvider(
create: (BuildContext context) => AppCubit()..getUserData()..handleScroll(),
child: BlocConsumer<AppCubit, AppStates>(
listener: (context, state) {},
builder: (context, state) {...},
),
);
}
and this is below one of multiple parts which I have the error:
DecorationImage(
image: NetworkImage(
'${userModel.cover}',
),
fit: BoxFit.cover,
),
and this is the full class I am using :
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:mysocially/shared/cubit/cubit.dart';
import 'package:mysocially/shared/cubit/states.dart';
import 'package:mysocially/styles/icon_broken.dart';
class SettingsScreen extends StatelessWidget {
const SettingsScreen({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return BlocConsumer<AppCubit, AppStates>(
listener: (context, state) {},
builder: (context, state) {
var userModel = AppCubit.get(context).userModel;
// print(userModel.cover);
return Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Container(
height: 190.0,
child: Stack(
alignment: AlignmentDirectional.bottomCenter,
children: [
Align(
alignment: AlignmentDirectional.topCenter,
child: Container(
width: double.infinity,
height: 140.0,
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(4.0),
topRight: Radius.circular(4.0),
),
image: DecorationImage(
image: NetworkImage(
'${userModel.cover}',
),
fit: BoxFit.cover,
),
),
),
),
CircleAvatar(
radius: 51.0,
backgroundColor:
Theme.of(context).scaffoldBackgroundColor,
child: CircleAvatar(
// radius: responsive.height * 0.025,
radius: 50.0,
backgroundImage: NetworkImage(
'${userModel.image}',
),
),
),
],
),
),
SizedBox(
height: 5.0,
),
Text(
'${userModel.name}',
style: Theme.of(context).textTheme.bodyText1,
),
Text(
'${userModel.bio}',
style: Theme.of(context).textTheme.caption,
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 20.0),
child: Row(
children: [
Expanded(
child: InkWell(
child: Column(
children: [
Text(
'100',
style: Theme.of(context).textTheme.subtitle2,
),
Text(
'Posts',
style: Theme.of(context).textTheme.caption,
),
],
),
onTap: () {},
),
),
Expanded(
child: InkWell(
child: Column(
children: [
Text(
'100',
style: Theme.of(context).textTheme.subtitle2,
),
Text(
'Posts',
style: Theme.of(context).textTheme.caption,
),
],
),
onTap: () {},
),
),
Expanded(
child: InkWell(
child: Column(
children: [
Text(
'62',
style: Theme.of(context).textTheme.subtitle2,
),
Text(
'Photos',
style: Theme.of(context).textTheme.caption,
),
],
),
onTap: () {},
),
),
Expanded(
child: InkWell(
child: Column(
children: [
Text(
'200',
style: Theme.of(context).textTheme.subtitle2,
),
Text(
'Followers',
style: Theme.of(context).textTheme.caption,
),
],
),
onTap: () {},
),
),
Expanded(
child: InkWell(
child: Column(
children: [
Text(
'600',
style: Theme.of(context).textTheme.subtitle2,
),
Text(
'Followings',
style: Theme.of(context).textTheme.caption,
),
],
),
onTap: () {},
),
),
],
),
),
Row(
children: [
Expanded(
child: OutlinedButton(
onPressed: () {},
child: Text('Add Photos'),
),
),
SizedBox(
width: 10.0,
),
OutlinedButton(
onPressed: () {},
child: Icon(
IconBroken.Edit,
size: 20.0,
),
),
],
),
],
),
);
},
);
}
}
I tried to handle this by using GetX state management by uing init: Get.find(),, but I failed to handle this error using flutter_bloc..
for more infos: The data doesn't retrieved in the first login, but it retrieved when I restart the app but it gives me this error for a second and gives me back the data..
Any Suggestions here?
I hope I mentioned all the infos needed..
#Edited So I figured something so here's I put a condition at the begining of builder as the below code:
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:mysocially/modules/new_post/new_post_screen.dart';
import 'package:mysocially/shared/components/components.dart';
import 'package:mysocially/shared/cubit/cubit.dart';
import 'package:mysocially/shared/cubit/states.dart';
import 'package:mysocially/styles/icon_broken.dart';
class SocialLayout extends StatelessWidget {
const SocialLayout({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return BlocConsumer<AppCubit, AppStates>(
listener: (context, state) {},
builder: (context, state) {
var cubit = AppCubit.get(context);
if (state is AppGetUserSuccessState) {
return Scaffold(
extendBody: true,
appBar: AppBar(
title: Text(
cubit.titles[cubit.currentIndex],
),
actions: [
IconButton(
onPressed: () {},
icon: Icon(IconBroken.Notification),
),
IconButton(
onPressed: () {},
icon: Icon(IconBroken.Search),
),
],
),
body: cubit.screens[cubit.currentIndex],
bottomNavigationBar: AnimatedOpacity(
opacity: cubit.show ? 1.0 : 0.0,
duration: Duration(milliseconds: 500),
child: BottomNavigationBar(
currentIndex: cubit.currentIndex,
onTap: (index) {
cubit.changeBottomNavIndex(index);
},
items: [
BottomNavigationBarItem(
icon: Icon(IconBroken.Home), label: 'Home'),
BottomNavigationBarItem(
icon: Icon(IconBroken.Chat), label: 'Chat'),
// BottomNavigationBarItem(
// icon: Icon(IconBroken.Paper_Upload,), label: 'Post'),
BottomNavigationBarItem(
icon: Icon(IconBroken.Location), label: 'Location'),
BottomNavigationBarItem(
icon: Icon(IconBroken.Setting), label: 'Settings'),
],
),
),
floatingActionButtonLocation:
FloatingActionButtonLocation.centerDocked,
floatingActionButton: AnimatedOpacity(
opacity: cubit.show ? 1.0 : 0.0,
duration: Duration(milliseconds: 500),
child: FloatingActionButton(
onPressed: () {
navigateTo(
context,
NewPostScreen(),
);
},
tooltip: 'Post',
child: Icon(
IconBroken.Paper_Upload,
color: Colors.white,
),
elevation: 2.0,
),
),
);
} else {
return Center(child: CircularProgressIndicator());
}
},
);
}
}
now the app works fine after I restart the app and didn't gives me the error, but I I tried to login in first time it shows the CircularProgressIndicator() to infinite till I restart the app and login again..
here's all my AppStates:
abstract class AppStates {}
class AppInitialState extends AppStates {}
class AppGetUserLoadingState extends AppStates {}
class AppGetUserSuccessState extends AppStates {}
class AppGetUserErrorState extends AppStates {
final String error;
AppGetUserErrorState(this.error);
}
class AppChangeBottomNavBarState extends AppStates {}
class AppChangeBottomSheetState extends AppStates {}
class AppChangeModeState extends AppStates {}
class AppHandleReverseHideBottomWidgetState extends AppStates {}
class AppHandleForwardHideBottomWidgetState extends AppStates {}
class AppHideBottomWidgetState extends AppStates {}
class AppShowBottomWidgetState extends AppStates {}
class AppNewPostState extends AppStates {}
This is the below LoginScreen:
import 'package:conditional_builder/conditional_builder.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:mysocially/layout/social_layout.dart';
import 'package:mysocially/modules/register/register_screen.dart';
import 'package:mysocially/network/local/cache_helper.dart';
import 'package:mysocially/shared/components/components.dart';
import 'cubit/cubit.dart';
import 'cubit/states.dart';
class LoginScreen extends StatelessWidget {
var formKey = GlobalKey<FormState>();
var emailController = TextEditingController();
var passwordController = TextEditingController();
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (BuildContext context) => AppLoginCubit(),
child: BlocConsumer<AppLoginCubit, AppLoginStates>(
listener: (context, state) {
if (state is AppLoginErrorState) {
showToast(
text: state.error,
state: ToastStates.ERROR,
);
}
if(state is AppLoginSuccessState) {
CacheHelper.saveData(
key: 'uId',
value: state.uId,
).then((value)
{
navigateAndFinish(
context,
SocialLayout(),
);
});
}
},
builder: (context, state) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Form(
key: formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'LOGIN',
style: Theme.of(context).textTheme.headline4.copyWith(
color: Colors.black,
),
),
Text(
'Login now to to contact with friends',
style: Theme.of(context).textTheme.bodyText1.copyWith(
color: Colors.grey,
),
),
SizedBox(
height: 30.0,
),
defaultFormField(
controller: emailController,
type: TextInputType.emailAddress,
validate: (String value) {
if (value.isEmpty) {
return 'please enter your email address';
}
},
label: 'Email Address',
prefix: Icons.email_outlined,
),
SizedBox(
height: 15.0,
),
defaultFormField(
controller: passwordController,
type: TextInputType.visiblePassword,
suffix: AppLoginCubit.get(context).suffix,
onSubmit: (value) {
if (formKey.currentState.validate()) {
AppLoginCubit.get(context).userLogin(
email: emailController.text,
password: passwordController.text,
);
}
},
isPassword: AppLoginCubit.get(context).isPassword,
suffixPressed: () {
AppLoginCubit.get(context)
.changePasswordVisibility();
},
validate: (String value) {
if (value.isEmpty) {
return 'password is too short';
}
},
label: 'Password',
prefix: Icons.lock_outline,
),
SizedBox(
height: 30.0,
),
ConditionalBuilder(
condition: state is! AppLoginLoadingState,
builder: (context) => defaultButton(
function: () {
if (formKey.currentState.validate()) {
AppLoginCubit.get(context).userLogin(
email: emailController.text,
password: passwordController.text,
);
}
},
text: 'login',
isUpperCase: true,
),
fallback: (context) =>
Center(child: CircularProgressIndicator()),
),
SizedBox(
height: 15.0,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Don\'t have an account?',
),
defaultTextButton(
function: () {
navigateTo(
context,
AppRegisterScreen(),
);
},
text: 'register',
),
],
),
],
),
),
),
),
),
);
},
),
);
}
}