I am tring to pas data to user profile screen from firebase
i have got error Unhandled Exception: 'package:cloud_firestore/src/collection_reference.dart': Failed assertion: line 116 pos 14: 'path.isNotEmpty': a document path must be a non-empty string
what do i need fix?
user prfile controller
lass ProfileController extends GetxController {
final Rx<Map<String, dynamic>> _user = Rx<Map<String, dynamic>>({});
Map<String, dynamic> get user => _user.value;
Rx<String> _uid = ''.obs;
updateUserID(String uid) {
_uid.value = uid;
getUserData();
}
void getUserData() async {
print("HELLO");
List<String> thumbnails = [];
// var myVideos = await firestore
// .collection('videos')
// .where('uid', isEqualTo: "9GFyxV41rkd0Iu0oy1tctobVNk92")
// .get();
//
// for (int i = 0; i < myVideos.docs.length; i++) {
// thumbnails.add((myVideos.docs[i].data() as dynamic)['thumbnail']);
// }
DocumentSnapshot userDoc =
await firestore.collection('users').doc(_uid.value).get();
final userData = userDoc.data()! as dynamic;
print(userData);
String name = userData['name'];
String profilePhoto = userData['profilePhoto'];
int likes = 0;
int followers = 0;
int following = 0;
bool isFollowing = false;
// for (var item in myVideos.docs) {
// likes += (item.data()['likes'] as List).length;
// }
// var followerDoc = await firestore
// .collection('users')
// .doc(_uid.value)
// .collection('followers')
// .get();
// var followingDoc = await firestore
// .collection('users')
// .doc(_uid.value)
// .collection('following')
// .get();
// followers = followerDoc.docs.length;
// following = followingDoc.docs.length;
// firestore
// .collection('users')
// .doc(_uid.value)
// .collection('followers')
// .doc(authController.user.uid)
// .get()
// .then(
// (value) {
// if (value.exists) {
// isFollowing = true;
// } else {
// isFollowing = false;
// }
// },
// );
_user.value = {
'followers': followers.toString(),
'following': following.toString(),
'isFollowing': isFollowing,
'likes': likes.toString(),
'profilePhoto': profilePhoto,
'name': name,
'thumbnails': thumbnails,
};
update();
}
followUser() async {
var doc = await firestore
.collection('users')
.doc(_uid.value)
.collection('followers')
.doc(authController.user.uid)
.get();
if (!doc.exists) {
await firestore
.collection('users')
.doc(_uid.value)
.collection('followers')
.doc(authController.user.uid)
.set({});
await firestore
.collection('users')
.doc(authController.user.uid)
.collection('following')
.doc(_uid.value)
.set({});
_user.value
.update('followers', (value) => (int.parse(value) + 1).toString());
} else {
await firestore
.collection('users')
.doc(_uid.value)
.collection('followers')
.doc(authController.user.uid)
.delete();
await firestore
.collection('users')
.doc(authController.user.uid)
.collection('following')
.doc(_uid.value)
.delete();
_user.value
.update('followers', (value) => (int.parse(value) - 1).toString());
}
_user.value.update('isFollowing', (value) => !value);
update();
}
}
auth Controller
class AuthController extends GetxController {
static AuthController instance = Get.find(); //TODO 2
var displayName = '';
var displayPhoto = '';
String myId = '';
late Rx<User?> _user; //TODO 1
User get user => _user.value!; //TODO 3
Rx<File?>? pickedImageVal;
File? get profilePhoto => pickedImageVal!.value;
FirebaseAuth auth = FirebaseAuth.instance;
User? get userProfile => auth.currentUser;
var isSignedIn = false.obs;
var _googleSignIn = GoogleSignIn();
var googleAccount = Rx<GoogleSignInAccount?>(null);
var firebaseStorage = FirebaseStorage.instance;
var firestore = FirebaseFirestore.instance;
// var authController = AuthController.instance;
#override
void onReady() {
super.onReady();
_user = Rx<User?>(firebaseAuth.currentUser);
_user.bindStream(firebaseAuth.authStateChanges());
ever(_user, _setInitialScreen);
}
_setInitialScreen(User? user) {
if (user == null) {
Get.offAll(OnboardingPage());
} else {
Get.offAll(const Home());
}
}
#override
void onInit() {
displayName = userProfile != null ? userProfile!.displayName! : '';
super.onInit();
}
void pickImage() async {
final pickedImage =
await ImagePicker().pickImage(source: ImageSource.gallery);
if (pickedImage != null) {
Get.snackbar(
'プロファイル写真', 'You have successfully selected your profile picture!');
}
pickedImageVal = Rx<File?>(File(pickedImage!.path));
}
Future<String> _uploadToStorage(File image) async {
Reference ref =
firebaseStorage.ref().child('profilePics').child(auth.currentUser!.uid);
UploadTask uploadTask = ref.putFile(image);
TaskSnapshot snap = await uploadTask;
String downloadUrl = await snap.ref.getDownloadURL();
return downloadUrl;
}
void signUp(String name, String email, String password, File? image) async {
try {
await auth
.createUserWithEmailAndPassword(email: email, password: password)
.then((value) {
displayName = name;
auth.currentUser!.updateDisplayName(name);
});
isSignedIn = true.obs;
String downloadUrl = await _uploadToStorage(profilePhoto!);
model.UserM user = model.UserM(
name: name,
email: email,
uid: auth.currentUser!.uid,
profilePhoto: downloadUrl,
);
await firestore
.collection('users')
.doc(auth.currentUser!.uid)
.set(user.toJson());
update();
Get.offAll(() => Root());
} on FirebaseAuthException catch (e) {
String title = e.code.replaceAll(RegExp('-'), ' ').capitalize!;
String message = '';
if (e.code == 'weak-password') {
message = 'The password provided is too weak.';
} else if (e.code == 'email-already-in-use') {
message = ('The account already exists for that email.');
} else {
message = e.message.toString();
}
Get.snackbar(title, message,
snackPosition: SnackPosition.BOTTOM,
backgroundColor: kPrimaryColor,
colorText: Colors.white);
} catch (e) {
Get.snackbar('Error occured!', e.toString(),
snackPosition: SnackPosition.BOTTOM,
backgroundColor: kPrimaryColor,
colorText: Colors.white);
}
}
void signIn(String email, String password) async {
try {
await auth
.signInWithEmailAndPassword(email: email, password: password)
.then((value) => displayName = userProfile!.displayName!);
isSignedIn = true.obs;
myId = userProfile!.uid; //TODO 4
update();
Get.offAll(() => Root());
} on FirebaseAuthException catch (e) {
String title = e.code.replaceAll(RegExp('-'), ' ').capitalize!;
String message = '';
if (e.code == 'wrong-password') {
message = 'Invalid Password. Please try again!';
} else if (e.code == 'user-not-found') {
message =
('The account does not exists for $email. Create your account by signing up.');
} else {
message = e.message.toString();
}
Get.snackbar('ユーザー名が存在しません。', 'アカウントを作成してください。',
snackPosition: SnackPosition.BOTTOM,
backgroundColor: kPrimaryColor,
colorText: Colors.white);
} catch (e) {
//TODO; what is Get.snackbar.e.tostring(), means?
Get.snackbar(
'Error occured!',
e.toString(),
snackPosition: SnackPosition.BOTTOM,
backgroundColor: kPrimaryColor,
colorText: Colors.white,
);
}
}
void resetPassword(String email) async {
try {
await auth.sendPasswordResetEmail(email: email);
Get.back();
} on FirebaseAuthException catch (e) {
String title = e.code.replaceAll(RegExp('-'), ' ').capitalize!;
String message = '';
if (e.code == 'user-not-found') {
message =
('The account does not exists for $email. Create your account by signing up.');
} else {
message = e.message.toString();
}
Get.snackbar(title, message,
snackPosition: SnackPosition.BOTTOM,
backgroundColor: kPrimaryColor,
colorText: Colors.white);
} catch (e) {
Get.snackbar('Error occured!', e.toString(),
snackPosition: SnackPosition.BOTTOM,
backgroundColor: kPrimaryColor,
colorText: Colors.white);
}
}
void signInWithGoogle() async {
try {
googleAccount.value = await _googleSignIn.signIn();
print(googleAccount.value);
displayName = googleAccount.value!.displayName!;
displayPhoto = googleAccount.value!.photoUrl!;
isSignedIn.value = true;
update(); // <-- without this the isSignedin value is not updated.
} catch (e) {
Get.snackbar('Error occured!', e.toString(),
snackPosition: SnackPosition.BOTTOM,
backgroundColor: kPrimaryColor,
colorText: Colors.white);
}
}
void signout() async {
try {
await auth.signOut();
await _googleSignIn.signOut();
displayName = '';
isSignedIn.value = false;
update();
Get.offAll(() => Root());
} catch (e) {
Get.snackbar('Error occured!', e.toString(),
snackPosition: SnackPosition.BOTTOM,
backgroundColor: kPrimaryColor,
colorText: Colors.white);
}
}
}
//
// extension StringExtention on String{
// String capitalizedString(){
// return'${this[0].toUpperCase()}${this.substring(1)}';
// }
// }
user profile screen
class ProfileScreen extends StatefulWidget {
final String uid;
ProfileScreen({Key? key, required this.uid}) : super(key: key);
#override
State<ProfileScreen> createState() => _ProfileScreenState();
}
class _ProfileScreenState extends State<ProfileScreen> {
final ProfileController profileController = Get.put(ProfileController());
#override
void initState() {
super.initState();
profileController.updateUserID(widget.uid);
}
#override
Widget build(BuildContext context) {
return GetBuilder<ProfileController>(
init: ProfileController(),
builder: (controller) {
// if (controller.user.isEmpty) {
// return Center(
// child: CircularProgressIndicator(),
// );
// }
print(controller.user);
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.black12,
leading: Icon(Icons.person_add_alt_1_outlined),
actions: [
Icon(Icons.more_horiz),
],
title: Text(controller.user['name'].toString()),
),
body: SafeArea(
child: SingleChildScrollView(
child: Column(
children: [
Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ClipOval(
child: CachedNetworkImage(
fit: BoxFit.cover,
imageUrl: controller.user['profilePhoto']
.toString(),
height: 100,
width: 100,
placeholder: (context, url) =>
CircularProgressIndicator(),
errorWidget: (context, url, error) =>
Icon(Icons.error)),
)
],
),
SizedBox(
height: 15,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Column(
children: [
Text(controller.user['following'].toString()),
SizedBox(
height: 5,
),
Text('following'),
],
),
Container(
color: Colors.black54,
width: 1,
height: 15,
margin: EdgeInsets.symmetric(horizontal: 15),
),
Column(
children: [
Text(controller.user['followers'].toString()),
SizedBox(
height: 5,
),
Text('follower'),
],
),
Container(
color: Colors.black54,
width: 1,
height: 15,
margin: EdgeInsets.symmetric(horizontal: 15),
),
Column(
children: [
Text(controller.user['likes'].toString()),
SizedBox(
height: 5,
),
Text('likes'),
],
)
],
),
SizedBox(
height: 15,
),
Container(
width: 140,
height: 47,
decoration: BoxDecoration(
border: Border.all(
color: Colors.black12,
),
),
child: Center(
child: InkWell(
onTap: () {
// if (widget.uid == authController.user.uid) {
// authController.signOut();
// } else {
// controller.followUser();
},
// },
child: const Text(
"",
// widget.uid == authController.user.uid
// ? 'sign out'
// controller.user['isFollowing']
// ? 'unfollow'
// : 'follow',
),
),
),
),
Related
I am developing a voice/video-calling app using Flutter and Agora rtc engine (v 5.3.1). I have followed all the necessary steps for generating token and joining a channel. But, I can't see the local camera view in the UI and the logs gives me warning saying: "onWarning warn 8 msg invalid view for local video". Any leads would be big help, thanks in advance.
Logs:
W/spdlog (30579): [2023-01-04 21:02:33.375] [0] [warning] /tmp/jenkins/IRIS-SDK/rtc/cxx/src/internal/rtc_engine_event_handler.cc:43 onWarning warn 8 msg invalid view for local video
W/spdlog (30579): [2023-01-04 21:02:33.375] [0] [warning] /tmp/jenkins/IRIS-SDK/rtc/cxx/src/internal/rtc_engine_event_handler.cc:43 onWarning warn 16
msg nullptr
I/spdlog (30579): [2023-01-04 21:02:33.375] [0] [info] /tmp/jenkins/IRIS-SDK/rtc/cxx/src/internal/rtc_engine_event_handler.cc:72 onJoinChannelSuccess
channel b8667da0-8c6a-11ed-a9fb-578e8ad35bd6 uid 1
W/spdlog (30579): [2023-01-04 21:02:33.377] [0] [warning] /tmp/jenkins/IRIS-SDK/rtc/cxx/src/internal/rtc_engine_event_handler.cc:43 onWarning warn 16
msg nullptr
Call Screen Widget:
imports...
String baseUrl = 'https://...';
class CallScreen extends ConsumerStatefulWidget {
final Call call;
const CallScreen({Key? key, required this.call, }) : super(key: key);
#override
_CallScreenState createState() => _CallScreenState();
}
class _CallScreenState extends ConsumerState<CallScreen> {
int uid = 1;
List<int> _remoteUids = [];
bool isJoined = false,
switchCamera = true,
openMicrophone = true,
enableSpeakerphone = false;
late bool openCamera;
late RtcEngine _engine;
#override
void initState() {
initAgora();
openCamera = widget.call.isVideoCall;
super.initState();
}
Future<String?> _getToken() async {
String url = baseUrl + "/rtc/" + widget.call.callId + "/publisher/uid/" + uid.toString() + "/";;
String? token;
try{
var resp = await http.get(Uri.parse(url));
if(resp.statusCode == 200){
token = jsonDecode(resp.body)['rtcToken'];
return token;
}
else{
showMySnackBar(context: context, content: "Token Status ERR: "+jsonDecode(resp.body)['message']);
return null;
}
}
catch(e){
showMySnackBar(context: context, content: "Token Err: "+e.toString());
return null;
}
}
void _joinChannel() async {
String? token = await _getToken();
if(token != null){
await _engine.joinChannel(token, widget.call.callId, null, uid);
}
else{
showMySnackBar(context: context, content: 'Token is null!');
}
}
void _leaveChannel() async {
ref.read(callControllerProvider).endCall(
widget.call.callerId,
widget.call.receiverId,
context,
widget.call.isGroupChat
);
if(widget.call.isVideoCall) await _engine.stopPreview();
await _engine.leaveChannel();
}
void _switchCamera() {
_engine.switchCamera()
.then((value) {
setState(() {
switchCamera = !switchCamera;
});
})
.catchError((err) {
//print('switchCamera $err');
});
}
void _switchMicrophone() async {
// await _engine.muteLocalAudioStream(!openMicrophone);
await _engine.enableLocalAudio(!openMicrophone)
.then((value) {
setState(() {
openMicrophone = !openMicrophone;
});
})
.catchError((err) {
// print('enableLocalAudio $err');
});
}
void _switchSpeakerphone() {
_engine.setEnableSpeakerphone(!enableSpeakerphone)
.then((value) {
setState(() {
enableSpeakerphone = !enableSpeakerphone;
});
})
.catchError((err) {
//print('setEnableSpeakerphone $err');
});
}
void initAgora() async {
try{
await [Permission.microphone, Permission.camera].request();
_engine = await RtcEngine.createWithContext(RtcEngineContext(AgoraConfig.appId));
_engine.setEventHandler(
RtcEngineEventHandler(
warning: (warn) {
showMySnackBar(context: context, content: "Warn: "+warn.name);
},
error: (err) {
showMySnackBar(context: context, content: 'OnErr event: '+err.name);
},
joinChannelSuccess: (String channel, int userId, int elapsed) {
// print("local user ${connection.localUid} joined");
if(mounted){
setState(() {
isJoined = true;
uid = userId;
});
}
showMySnackBar(context: context, content: 'You ($userId) have joined the call!');
},
userJoined: (int remoteUid, int elapsed) {
//debugPrint("remote user $remoteUid joined");
if(mounted){
setState(() {
_remoteUids.add(remoteUid);
});
}
},
userOffline: (int remoteUid, UserOfflineReason reason) {
//debugPrint("remote user $remoteUid left channel");
if(mounted){
setState(() {
_remoteUids.removeWhere((element) => element == remoteUid);
});
}
},
leaveChannel: (stats) {
if(mounted){
setState(() {
isJoined = false;
if(!widget.call.isGroupChat || _remoteUids.length == 1){
_remoteUids.clear();
}
});
}
},
// onTokenPrivilegeWillExpire: (RtcConnection connection, String token) {
// debugPrint('[onTokenPrivilegeWillExpire] connection: ${connection.toJson()}, token: $token');
// },
),
);
await _engine.setChannelProfile(ChannelProfile.LiveBroadcasting);
//await _engine.setClientRole(ClientRole.Broadcaster);
await _engine.enableVideo();
if(widget.call.isVideoCall){
await _engine.startPreview();
}
else{
await _engine.muteLocalVideoStream(true);
await _engine.muteAllRemoteVideoStreams(true);
}
_joinChannel();
}
catch(e){
showMySnackBar(context: context, content: "Init Err: "+e.toString());
}
}
#override
void dispose() {
_leaveChannel();
_engine.destroy();
super.dispose();
}
// Display remote user's video
Widget _remoteVideo() {
if (_remoteUids.isNotEmpty) {
//TODO check for video on or off or if video call:
return rtc_remote_view.SurfaceView(
uid: _remoteUids[0],
channelId: widget.call.callId,
);
}
else {
return const Text(
'Please wait for others to join',
textAlign: TextAlign.center,
);
}
}
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async { _leaveChannel(); return true; },
child: Scaffold(
backgroundColor: Colors.white,
body: Stack(
children: [
Center(
child: _remoteVideo(),
),
Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: const EdgeInsets.only(right: 18.0, bottom: 12),
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(12)),
child: ColoredBox(
color: Colors.grey.shade200,
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 125, maxHeight: 175),
child: AspectRatio(
aspectRatio: 9/16,
child: Center(
child: isJoined
?
( //TODO: check for video on or off or if video call:
openCamera
? rtc_local_view.SurfaceView(
channelId: widget.call.callId,
)
: const Icon(
Icons.videocam_off_rounded,
color: Colors.black,
size: appActionsIconsSize,
)
)
: const CircularProgressIndicator(),
),
),
),
),
),
),
),
],
),
),
);
}
}
I found the issue: I was not setting the ClientRoleType correctly and that caused error in finding local view. One needs to define ClientRoleType (based on your logic) and ChannelProfileType.broadcast and everything seems to work.
The previous developer has implemented an authentication mechanism that's very hit or miss when it comes to successfully authenticating users. I've been trying to debug the issues, but this last one persists.
The logic for the authentication is the following:
Users opens the app and is navigated to the splash screen
The splash screen's cubit makes an async request to get an AuthModel that contains the Firebase user's UUID and if successful, emits an "already logged in" state
The splash screen then reacts to the state and navigates the user to the main screen
The main screen seems to listen for event emissions by the AppBloc & MainBloc in order to determine if the user can be shown the actual content.
The issue is that although steps (1), (2) & (3) seem to initially work, after running the app in debug mode a lot, the AppBloc's on<AppEventAuthModelChanged$>(_onUserChanged); runs after receiving a new AuthModel emission and inside the _onUserChanged function, we land on the return emit(const AppState.unauthenticated()) statement. After observing the debugger, this is caused because the _authRepository.token != null expression is false at that point (but becomes true later).
This token is retrieved via the await _connectToServer(firebaseUser: firebaseUser); call inside the Auth Repository Impl's_ensureUser() function that runs based on the authStateChanges() stream via the getter.
Now, I have observed that the _ensureUser() is run multiple times and always seems to emit a valid Firebase user. However, the call to the _connectToServer() seems to take too long to finish (in order to initialize the token field), and thus the AppBloc's logic in the _onUserChanged call fails. What would be a valid approach so that the call has finished in order for us to be sure that the token was either retrieved or that something actually went wrong?
Is the code unsalvageable?
Splash_component.dart for the splash_screen.dart:
class SplashComponent extends StatefulWidget {
const SplashComponent({Key? key}) : super(key: key);
#override
State<SplashComponent> createState() => _SplashComponentState();
}
class _SplashComponentState extends State<SplashComponent> {
SplashCubit get _cubit => context.read<SplashCubit>();
bool _isTextVisible = false;
#override
void initState() {
super.initState();
_cubit.init();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.red,
body: BlocConsumer<SplashCubit, SplashState>(
listener: (BuildContext context, SplashState state) {
state.maybeWhen(
newUser: () async {
setState(() => _isTextVisible = true);
//await Future<void>.delayed(const Duration(seconds: 1));
NavigatorUtils.replaceToAuthScreen(context);
},
alreadyLoggedIn: () async {
setState(() => _isTextVisible = true);
//await Future<void>.delayed(const Duration(seconds: 1));
NavigatorUtils.replaceToMainScreen(context);
},
orElse: () {},
);
},
builder: (BuildContext context, SplashState state) {
return state.maybeWhen(
orElse: () {
return Padding(
padding: const EdgeInsets.all(40),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image.asset('assets/images/blob_logo_white_splash.png'),
const SizedBox(height: 24),
AnimatedOpacity(
duration: const Duration(milliseconds: 500),
opacity: _isTextVisible ? 1.0 : 0.0,
child: Text(
'Carry your art with you',
style: montserratRegular22.copyWith(
color: AppColors.white),
),
),
],
),
);
},
);
},
),
);
}
}
Splash_cubit.dart:
class SplashCubit extends Cubit<SplashState> {
SplashCubit({required AuthRepository authRepository})
: _authRepository = authRepository,
super(const SplashState.loading());
final AuthRepository _authRepository;
Future<void> init() async {
try {
final AuthModel? authModel = await _authRepository.authModel.first;
if (authModel != null) {
return emit(const SplashState.alreadyLoggedIn());
}
return emit(const SplashState.newUser());
} catch (_) {
return emit(const SplashState.newUser());
}
}
}
Main_component.dart for the main_screen.dart:
const double _iconSize = 24;
class MainComponent extends StatelessWidget {
const MainComponent({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return BlocListener<AppBloc, AppState>(
listenWhen: (AppState previous, AppState current) {
return previous is AppStateAuthenticated$ &&
current is AppStateUnauthenticated$;
},
listener: (BuildContext context, AppState state) {
state.maybeWhen(
unauthenticated: () => NavigatorUtils.replaceToAuthScreen(context),
orElse: () {},
);
},
child: BlocBuilder<MainCubit, MainState>(
builder: (BuildContext context, MainState state) {
final MainCubit cubit = context.read<MainCubit>();
final AppState appState = context.watch<AppBloc>().state;
final TabType tab = state.tab;
return Scaffold(
body: appState.maybeWhen(
authenticated: (_) {
return SafeArea(
child: IndexedStack(
index: tab.index,
children: const <Widget>[
AlbumScreen(),
CameraScreen(),
ExploreScreen(),
],
),
);
},
unauthenticated: () {
NavigatorUtils.replaceToAuthScreen(context);
return const SizedBox.shrink();
},
orElse: () => const SizedBox.shrink(),
),
bottomNavigationBar: BottomNavigationBar(
onTap: (int index) => cubit.changeTab(TabType.values[index]),
currentIndex: cubit.state.tab.index,
elevation: 0,
selectedItemColor: AppColors.red,
selectedFontSize: 12,
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Image.asset(TabType.album.asset,
color: AppColors.grey,
height: _iconSize,
width: _iconSize),
activeIcon: Image.asset(TabType.album.asset,
color: AppColors.red,
height: _iconSize,
width: _iconSize),
label: 'ALBUM',
),
BottomNavigationBarItem(
icon: Image.asset(
TabType.camera.asset,
color: AppColors.grey,
height: _iconSize,
width: _iconSize,
),
activeIcon: Image.asset(
TabType.camera.asset,
color: AppColors.red,
height: _iconSize,
width: _iconSize,
),
label: 'CAMERA',
),
BottomNavigationBarItem(
icon: Image.asset(
TabType.explore.asset,
color: AppColors.grey,
height: _iconSize,
width: _iconSize,
),
activeIcon: Image.asset(
TabType.explore.asset,
color: AppColors.red,
height: _iconSize,
width: _iconSize,
),
label: 'EXPLORE',
),
],
),
);
},
),
);
}
}
app_bloc.dart:
class AppBloc extends Bloc<AppEvent, AppState> {
AppBloc({
required AuthRepository authRepository,
required UserRepository userRepository,
required ArtworkRepository artworkRepository,
required ArtistRepository artistRepository,
required GalleryRepository galleryRepository,
required VenueRepository venueRepository,
}) : _authRepository = authRepository,
_userRepository = userRepository,
_artworkRepository = artworkRepository,
_artistRepository = artistRepository,
_galleryRepository = galleryRepository,
_venueRepository = venueRepository,
super(const AppState.initial()) {
on<AppEventAuthModelChanged$>(_onUserChanged);
on<AppEventLogout$>(_onLogout);
_authModelSubscription = _authRepository.authModel.listen(
(AuthModel? authModel) => add(AppEvent.authModelChanged(authModel)),
);
}
final AuthRepository _authRepository;
final UserRepository _userRepository;
final ArtworkRepository _artworkRepository;
final ArtistRepository _artistRepository;
final GalleryRepository _galleryRepository;
final VenueRepository _venueRepository;
late StreamSubscription<AuthModel?> _authModelSubscription;
Future<void> _onUserChanged(
AppEventAuthModelChanged$ event, Emitter<AppState> emit) async {
final AuthModel? authModel = event.authModel;
if (authModel != null &&
state is! AppStateUnauthenticated$ &&
_authRepository.token != null) {
final String token = _authRepository.token!;
_userRepository.token = token;
_artworkRepository.token = token;
_artistRepository.token = token;
_galleryRepository.token = token;
_venueRepository.token = token;
await _artworkRepository.getSavedArtworks();
final User user = await _userRepository.getUserDetails();
return emit(AppState.authenticated(user: user));
} else {
_authRepository.token = null;
_artworkRepository.token = null;
_artistRepository.token = null;
return emit(const AppState.unauthenticated());
}
}
Future<void> _onLogout(AppEventLogout$ event, Emitter<AppState> emit) async {
unawaited(_authRepository.logOut());
emit(const AppState.unauthenticated());
}
#override
Future<void> close() {
_authModelSubscription.cancel();
return super.close();
}
}
auth_repository_impl.dart:
class AuthRepositoryImpl implements AuthRepository {
AuthRepositoryImpl({required DioClient dioClient}) : _client = dioClient;
final DioClient _client;
final auth.FirebaseAuth _auth = auth.FirebaseAuth.instance;
#override
String? token;
bool isFetchingToken = false;
#override
Stream<AuthModel?> get authModel {
return MergeStream<auth.User?>(
<Stream<auth.User?>>[
_auth.authStateChanges(),
],
)
.startWith(_auth.currentUser) //
.switchMap<AuthModel?>(_ensureUser)
.share()
.distinct();
}
Stream<AuthModel?> _ensureUser(auth.User? firebaseUser) async* {
if (firebaseUser == null) {
yield* Stream<AuthModel?>.value(null);
return;
}
if (token == null && !isFetchingToken) {
await _connectToServer(firebaseUser: firebaseUser);
}
yield* Stream<AuthModel?>.value(AuthModel(uid: firebaseUser.uid));
}
Future<void> _connectToServer(
{auth.UserCredential? userCredential, auth.User? firebaseUser}) async {
try {
String? firebaseToken;
isFetchingToken = true;
if (userCredential != null) {
firebaseToken = await userCredential.user?.getIdToken();
} else {
firebaseToken = await firebaseUser?.getIdToken();
}
final String email =
userCredential?.user?.email ?? firebaseUser?.email ?? '';
final String baseUrl = getIt<AppRepository>().env == Env.staging
? stagingUrl
: productionUrl;
final ApiResponse response = await _client.httpCall(
baseUrl: baseUrl,
path: '/mobile/login',
httpMethod: HttpMethod.POST,
queryParameters: <String, dynamic>{
'email': email,
'token': firebaseToken,
},
);
final Map<String, dynamic> data = response.data;
token = data['message'];
isFetchingToken = false;
} on DioError catch (ex) {
if (ex.type == DioErrorType.connectTimeout) {
throw Exception('Connection Timeout Exception');
}
throw Exception(ex.message);
}
}
#override
Future<void> logOut() async => await _auth.signOut();
#override
Future<void> registerWithEmailAndPassword(
{required String email, required String password}) async {
final auth.UserCredential userCredential =
await _auth.createUserWithEmailAndPassword(
email: email,
password: password,
);
await _connectToServer(userCredential: userCredential);
}
#override
Future<void> loginWithApple() async {
final String rawNonce = NonceGenerator.generateNonce();
final String nonce = NonceGenerator.sha256ofString(rawNonce);
final AuthorizationCredentialAppleID appleCredential =
await SignInWithApple.getAppleIDCredential(
scopes: <AppleIDAuthorizationScopes>[
AppleIDAuthorizationScopes.email,
AppleIDAuthorizationScopes.fullName
],
nonce: nonce,
);
final auth.OAuthCredential oauthCredential =
auth.OAuthProvider('apple.com').credential(
idToken: appleCredential.identityToken,
rawNonce: rawNonce,
);
final auth.UserCredential userCredential =
await auth.FirebaseAuth.instance.signInWithCredential(oauthCredential);
await _connectToServer(userCredential: userCredential);
}
#override
Future<void> loginWithEmailAndPassword(
{required String email, required String password}) async {
try {
final auth.UserCredential userCredential =
await _auth.signInWithEmailAndPassword(
email: email,
password: password,
);
await _connectToServer(userCredential: userCredential);
log('z1z:: Server token $token');
} catch (e) {
rethrow;
}
}
#override
Future<void> resetPassword({required String email}) async =>
await _auth.sendPasswordResetEmail(email: email);
}
here is the error message from the console
user.dart
import 'package:cloud_firestore/cloud_firestore.dart';
class User {
final String email;
final String uid;
final String photoUrl;
final String username;
final String bio;
final List<String> followers;
final List<String> following;
const User({
required this.email,
required this.uid,
required this.photoUrl,
required this.username,
required this.bio,
required this.followers,
required this.following,
});
static User fromSnap(DocumentSnapshot snap) {
var snapshot = snap.data() as Map<String, dynamic>;
return User(
username: snapshot["username"],
uid: snapshot["uid"],
email: snapshot["email"],
photoUrl: snapshot["photoUrl"],
bio: snapshot["bio"],
followers: snapshot["followers"],
following: snapshot["following"],
);
}
Map<String, dynamic> toJson() => {
"username": username,
"uid": uid,
"email": email,
"photoUrl": photoUrl,
"bio": bio,
"followers": followers,
"following": following,
};
}
user_provider
import 'package:flutter/material.dart';
import 'package:social_network/models/user.dart';
import 'package:social_network/resources/auth_method.dart';
class UserProvider with ChangeNotifier {
User? _user;
final AuthMethods _authMethods = AuthMethods();
User get getUser => _user!;
Future<void> refreshUser() async {
User? user = await _authMethods.getUserDetails();
_user = user;
notifyListeners();
}
}
add_post_screen
import 'dart:typed_data';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:provider/provider.dart';
import 'package:social_network/providers/user_provider.dart';
import 'package:social_network/resources/firestore_methods.dart';
import 'package:social_network/utils/colors.dart';
import 'package:social_network/utils/utils.dart';
class AddPostScreen extends StatefulWidget {
const AddPostScreen({Key? key}) : super(key: key);
#override
_AddPostScreenState createState() => _AddPostScreenState();
}
class _AddPostScreenState extends State<AddPostScreen> {
Uint8List? _file;
bool _isLoading = false;
final TextEditingController _descriptionController = TextEditingController();
_selectImage(BuildContext parentContext) async {
return showDialog(
context: parentContext,
builder: (BuildContext context) {
return SimpleDialog(
title: const Text('Create a post'),
children: <Widget>[
SimpleDialogOption(
padding: const EdgeInsets.all(20),
child: const Text('Take a photo'),
onPressed: () async {
Navigator.of(context).pop();
Uint8List file = await pickImage(ImageSource.camera);
setState(() {
_file = file;
});
},
),
SimpleDialogOption(
padding: const EdgeInsets.all(20),
child: const Text('Choose from gallery'),
onPressed: () async {
Navigator.of(context).pop();
Uint8List file = await pickImage(ImageSource.gallery);
setState(() {
_file = file;
});
},
),
SimpleDialogOption(
padding: const EdgeInsets.all(20),
child: const Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
});
}
void postImage(
String uid,
String username,
String profileImage,
) async {
setState(() {
_isLoading = true;
});
try {
String res = await FirestoreMethods().uploadPost(
_descriptionController.text, _file!, uid, username, profileImage);
if (res == "Succes") {
setState(() {
_isLoading = false;
});
showSnackBar('Posted!', context);
clearImage();
} else {
showSnackBar(res, context);
}
} catch (e) {
setState(() {
_isLoading = false;
});
showSnackBar(e.toString(), context);
}
}
void clearImage() {
setState(() {
_file = null;
});
}
#override
void dispose() {
super.dispose();
_descriptionController.dispose();
}
#override
Widget build(BuildContext context) {
final UserProvider userProvider = Provider.of<UserProvider>(context);
return _file == null
? Center(
child: IconButton(
icon: const Icon(Icons.upload),
onPressed: () => _selectImage(context),
),
)
: Scaffold(
appBar: AppBar(
backgroundColor: mobileBackgroundColor,
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: clearImage,
),
title: const Text('Post to'),
centerTitle: false,
actions: <Widget>[
TextButton(
onPressed: () => postImage(
userProvider.getUser.uid,
userProvider.getUser.username,
userProvider.getUser.photoUrl,
),
child: const Text(
'Post',
style: TextStyle(
color: Colors.redAccent,
fontWeight: FontWeight.bold,
fontSize: 18,
),
),
),
],
),
body: Column(
children: [
_isLoading
? const LinearProgressIndicator()
: const Padding(
padding: EdgeInsets.only(top: 0),
),
const Divider(),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CircleAvatar(
backgroundImage: NetworkImage(
'https://images.unsplash.com/photo-1522441815192-d9f04eb0615c?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=654&q=80',
// userProvider.getUser.photoUrl,
),
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.4,
child: TextField(
controller: _descriptionController,
decoration: const InputDecoration(
hintText: 'Write a caption...',
border: InputBorder.none,
),
maxLines: 8,
),
),
SizedBox(
height: 45,
width: 45,
child: AspectRatio(
aspectRatio: 487 / 451,
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: MemoryImage(_file!),
fit: BoxFit.fill,
alignment: FractionalOffset.topCenter,
),
),
),
),
),
const Divider(),
],
)
],
));
}
}
auth_methods.dart
import 'dart:typed_data';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:social_network/models/user.dart' as model;
import 'package:social_network/resources/storage_methods.dart';
class AuthMethods {
final FirebaseAuth _auth = FirebaseAuth.instance;
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
Future<model.User?> getUserDetails() async {
User currentUser = _auth.currentUser!;
DocumentSnapshot documentSnapshot =
await _firestore.collection('user').doc(currentUser.uid).get();
return model.User.fromSnap(documentSnapshot);
}
//sign up the user
Future<String> signUpUser({
required String email,
required String password,
required String username,
required String bio,
required Uint8List? file,
}) async {
String res = "Some error occurred";
try {
if (email.isNotEmpty ||
password.isNotEmpty ||
username.isNotEmpty ||
bio.isNotEmpty ||
file != null) {
//register the user
UserCredential cred = await _auth.createUserWithEmailAndPassword(
email: email, password: password);
String photoUrl = await StorageMethods()
.uploadImageToStorage('profilePictures', file!, false);
//adding user to our database
model.User _user = model.User(
bio: bio,
username: username,
uid: cred.user!.uid,
email: email,
photoUrl: photoUrl,
following: [],
followers: [],
);
await _firestore.collection("users").doc(cred.user!.uid).set(
_user.toJson(),
);
res = "Succes";
} else {
res = "Please enter all the fields";
}
} catch (err) {
res = err.toString();
}
return res;
}
// logging the user
Future<String> loginUser(
{required String email, required String password}) async {
String res = "Some error occured";
try {
if (email.isNotEmpty || password.isNotEmpty) {
await _auth.signInWithEmailAndPassword(
email: email, password: password);
res = "Succes";
} else {
res = "Please enter all the fields required";
}
} catch (err) {
res = err.toString();
}
return res;
}
}
Hello. I'll try to make a post button and when I try to upload a photo and press the post button I receive this error.
Do you have any solution for this error? Thanks a lot!
Error message:
Restarted application in 474ms.
[VERBOSE-2:ui_dart_state.cc(198)] Unhandled Exception: type 'Null' is not a subtype of type 'Map<String, dynamic>' in type cast
════════ Exception caught by gesture ═══════════════════════════════════════════
Null check operator used on a null value
════════════════════════════════════════════════════════════════════════════════
[VERBOSE-2:profiler_metrics_ios.mm(203)] Error retrieving thread information: (os/kern) object terminated
I believe that your issue in
onPressed: () => postImage(
userProvider.getUser.uid,
userProvider.getUser.username,
userProvider.getUser.photoUrl,
),
Make sure that the user is not null before calling the method,
It would be more helpful to post more code _authMethods.getUserDetails(); what’s inside this method?
UPDATE:
Future<model.User?> getUserDetails() async {
User currentUser = _auth.currentUser!;
DocumentSnapshot documentSnapshot =
await _firestore.collection('user').doc(currentUser.uid).get();
//add the following lines
print('documentSnapshot: $documentSnapshot');
if(documentSnapshot == null) return null;
return model.User.fromSnap(documentSnapshot);
}
The logic in the program I wrote is that it is like Twitter. There will be photo sharing and text sharing. However, there is an error in my codes and when I do not share photos, I get the error 'Null check-operator used on null value'. How can I change this?
When I do not share photos, I get the error below.
These are my codes. I would appreciate it if you could tell me the changes I need to make.
add_post_screenn
class AddPostScreen extends StatefulWidget {
const AddPostScreen({Key? key}) : super(key: key);
#override
State<AddPostScreen> createState() => _AddPostScreenState();
}
class _AddPostScreenState extends State<AddPostScreen> {
Uint8List? _file;
final TextEditingController _descriptionController = TextEditingController();
bool _isLoading = false;
void postImage(
String uid,
String username,
String profImage,
) async {
setState(() {
_isLoading = true;
});
try {
String res = await FirestoreMethods().uploadPost(
_descriptionController.text, _file!, uid, username, profImage);
if (res == 'succes') {
setState(() {
_isLoading = false;
});
showSnackBar('Posted!', context);
clearImage();
} else {
showSnackBar(res, context);
}
} catch (e) {
showSnackBar(e.toString(), context);
}
}
_selectImage(BuildContext context) async {
return showDialog(
context: context,
builder: (context) {
return SimpleDialog(
title: const Text('Create a Post'),
children: [
SimpleDialogOption(
padding: const EdgeInsets.all(20),
child: const Text('Take a photo'),
onPressed: () async {
Navigator.of(context).pop();
Uint8List file = await pickImage(
ImageSource.camera,
);
setState(() {
_file = file;
});
},
),
SimpleDialogOption(
padding: const EdgeInsets.all(20),
child: const Text('Chose from gallery'),
onPressed: () async {
Navigator.of(context).pop();
Uint8List file = await pickImage(
ImageSource.gallery,
);
setState(() {
_file = file;
});
},
),
SimpleDialogOption(
padding: const EdgeInsets.all(20),
child: const Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
}),
],
);
});
}
void clearImage() {
_file = null;
}
#override
void dispose() {
super.dispose();
_descriptionController.dispose();
}
#override
Widget build(BuildContext context) {
final User user = Provider.of<UserProvider>(context).getUser;
//yorum satırı olacak
return _file == null
? Center(
child: IconButton(
icon: const Icon(Icons.upload),
onPressed: () => _selectImage(context),
),
)
// //buraya kadar
: Scaffold(
appBar: AppBar(
backgroundColor: mobileBackgroundColor,
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: clearImage,
),
title: const Text('Paylaşım'),
centerTitle: false,
actions: [
TextButton(
onPressed: () => postImage(
user.uid,
user.username,
user.photoUrl,
),
child: const Text(
'Paylaş',
style: TextStyle(
color: Colors.blueAccent,
fontWeight: FontWeight.bold,
fontSize: 16,
),
))
],
),
body: Column(
children: [
_isLoading
? const LinearProgressIndicator()
: const Padding(
padding: EdgeInsets.only(
top: 0,
),
),
const Divider(),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CircleAvatar(
backgroundImage: NetworkImage(user.photoUrl),
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.45,
child: TextField(
controller: _descriptionController,
decoration: const InputDecoration(
hintText: 'Hayallerini Yaz!',
border: InputBorder.none,
),
maxLines: 8,
),
),
SizedBox(
height: 45,
width: 45,
child: AspectRatio(
aspectRatio: 487 / 451,
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: MemoryImage(_file!),
fit: BoxFit.fill,
alignment: FractionalOffset.topCenter,
)),
),
),
),
const Divider(),
],
),
],
),
);
}
}
firestore_methods
class FirestoreMethods {
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
//upload post
Future<String> uploadPost(
String description,
Uint8List file,
String uid,
String username,
String profImage,
) async {
String res = 'Some error occurred';
try {
String photoUrl =
await StorageMethods().uploadImageToStorage('posts', file, true);
String postId = const Uuid().v1();
Post post = Post(
description: description,
uid: uid,
username: username,
postId: postId,
datePublished: DateTime.now(),
postUrl: photoUrl,
porfImage: profImage,
likes: [],
);
_firestore.collection('posts').doc(postId).set(
post.toJson(),
);
res = 'succes';
} catch (err) {
res = err.toString();
}
return res;
}
Future<void> LikePost(String postId, String uid, List likes) async {
try {
if (likes.contains(uid)) {
await _firestore.collection('posts').doc(postId).update({
'likes': FieldValue.arrayRemove([uid]),
});
} else {
await _firestore.collection('posts').doc(postId).update({
'likes': FieldValue.arrayUnion([uid]),
});
}
} catch (e) {
print(
e.toString(),
);
}
}
Future<void> postComment(String postId, String text, String uid, String name,
String profilePic) async {
try {
if (text.isNotEmpty) {
String commentId = const Uuid().v1();
await _firestore
.collection('posts')
.doc(postId)
.collection('comments')
.doc(commentId)
.set({
'profilePic': profilePic,
'name': name,
'uid': uid,
'text': text,
'commentId': commentId,
'datePublished': DateTime.now(),
});
} else {
print('Text is empty');
}
} catch (e) {
print(
e.toString(),
);
}
}
storage.methods
class StorageMethods {
final FirebaseAuth _auth = FirebaseAuth.instance;
final FirebaseStorage _storage = FirebaseStorage.instance;
// firebase deposuna resim ekleme firebase deposuna resim ekleme
Future<String> uploadImageToStorage(
String childName, Uint8List file, bool isPost) async {
// firebase depolama alanımıza konum oluşturma
Reference ref =
_storage.ref().child(childName).child(_auth.currentUser!.uid);
if (isPost) {
String id = const Uuid().v1();
ref = ref.child(id);
}
// putting in uint8list format -> Upload task like a future but not future
UploadTask uploadTask = ref.putData(file);
TaskSnapshot snap = await uploadTask;
String downloadUrl = await snap.ref.getDownloadURL();
return downloadUrl;
}
}
The error cause comes from the _file!. When posting only text there is going to be no _file as it's going to be null.
To fix this change the references from _file! to just _file and the FirestoreMethods.uploadPost should be the following:
FirestoreMethods.uploadPost - Change file to be nullable and check for null.
Future<String> uploadPost(
String description,
Uint8List? file, // <- Here
String uid,
String username,
String profImage,
) async {
String res = 'Some error occurred';
try {
String photoUrl = file != null // <- Here
? await StorageMethods().uploadImageToStorage('posts', file, true)
: '';
String postId = const Uuid().v1();
Post post = Post(
description: description,
uid: uid,
username: username,
postId: postId,
datePublished: DateTime.now(),
postUrl: photoUrl,
porfImage: profImage,
likes: [],
);
_firestore.collection('posts').doc(postId).set(
post.toJson(),
);
res = 'succes';
} catch (err) {
res = err.toString();
}
return res;
}
_AddPostScreenState.postImage - Change _file! to just _file
void postImage(
String uid,
String username,
String profImage,
) async {
setState(() {
_isLoading = true;
});
try {
String res = await FirestoreMethods().uploadPost(
_descriptionController.text, _file, uid, username, profImage);
if (res == 'succes') {
setState(() {
_isLoading = false;
});
showSnackBar('Posted!', context);
clearImage();
} else {
showSnackBar(res, context);
}
} catch (e) {
showSnackBar(e.toString(), context);
}
}
I think you are getting error in this line
Reference ref =
_storage.ref().child(childName).child(_auth.currentUser!.uid);
Here the current user maybe null. To fix this error for now you can use
Reference ref =
_storage.ref().child(childName).child(_auth.currentUser?.uid);
//Note instead of using ! I have used ? which means currentUser can be null. ! means you are marking that its not null and value is unknown
but please note that you are receiving _auth.currentUser as null. Please check those codes
I've created a custom Login Page that looks and works the way I intended. I'm having trouble figuring out how and where in my code to display a progress indicator while it authenticates the Firebase User and pulls the users name, photo, or placeholders. Also, if authentication fails, where and how should an alert be displayed. Here is my current code...
Future<Null> _loginButton() async {
_email = _emailController.text.toString().replaceAll(" ", "");
_password = _passController.text.toString().replaceAll(" ", "");
//_username = _nameController.text.toString().replaceAll(" ", "");
if (_email != null && _password != null) {
try {
await FirebaseAuth.instance
.signInWithEmailAndPassword(email: _email, password: _password);
final FirebaseUser currentUser = await _auth.currentUser();
final userid = currentUser.uid;
currentUserId = userid;
FirebaseDatabase.instance
.reference()
.child('users/$userid')
.onValue
.listen((Event event) {
if (event.snapshot.value == null) {
imageString = "placeholder";
name = _username;
} else if (event.snapshot.value != null) {
imageString = event.snapshot.value['image'];
name = event.snapshot.value['displayName'];
}
fb.child('users/$userid').set({
'displayName': name,
'image': imageString,
'uid' : userid.toString()
});
}).onDone(() {
Navigator.pushNamed(context, '/menu');
Main.uid = userid;
});
} catch (error) {
}
} else {}
}
Check this a functional example from my project to implement CircularProgressIndicator between flutter transitions.
class LoginPage extends StatefulWidget {
#override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
bool isLoading = false;
#override
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: isLoading
? Column(
children: <Widget>[
CircularProgressIndicator(),
Divider(
height: 20,
color: Colors.transparent,
),
Text("Please wait..."),
],
mainAxisSize: MainAxisSize.min,
)
: MaterialButton(
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Image.asset(
'assets/google.png',
width: 25.0,
),
Padding(
child: Text(
"Sign in with Google",
style: TextStyle(
fontFamily: 'Roboto',
color: Color.fromRGBO(68, 68, 76, .8),
),
),
padding: new EdgeInsets.only(left: 15.0),
),
],
),
onPressed: () {
googleUserSignIn().then((user) {
this.setState(() {
//isLoading = false;
// Your task....
});
});
},
color: Colors.white,
elevation: 5,
highlightElevation: 2,
)));
}
Future<FirebaseUser> googleUserSignIn() async {
this.setState(() {
isLoading = true;
});
GoogleSignInAccount googleUser = await Firebase().firebaseGSI.signIn();
GoogleSignInAuthentication googleAuth = await googleUser.authentication;
final AuthCredential credential = GoogleAuthProvider.getCredential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
FirebaseUser user =
await Firebase().firebaseAuth.signInWithCredential(credential);
return user;
}
}