Related
So i'm pretty lost trying to get my app to fetch not only the desired text from firestore but also the image i placed there. I know the error "Bad state: field does not exist within the DocumentSnapshotPlatform" implies that either I'm missing field or there's something wrong with at least one of those fields. There's only four things I'm trying to fetch from firestore "imgUrl, title, desc, organizer" i have checked for spelling and order and i can't figure it out. I have also checked firestore to see if the order and spelling was correct and i dont see what's the issue. Please help, Thank you so much in advanced.
////////////////////////////// News Class Starts\\\\\\\\\\\\\\
import 'package:myfuji/screens/CrudMethods.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'CrudMethods.dart';
import 'add_blog.dart';
class News extends StatefulWidget {
#override
_NewsState createState() => _NewsState();
}
class _NewsState extends State<News> {
CrudMethods crudMethods = CrudMethods();
late QuerySnapshot blogSnapshot;
#override
void initState() {
crudMethods.getData().then((result) {
blogSnapshot = result;
setState(() {});
});
super.initState();
}
Widget blogsList() {
return ListView.builder(
padding: const EdgeInsets.only(top: 24),
itemCount: blogSnapshot.docs.length,
itemBuilder: (context, index) {
return BlogTile(
organizer: blogSnapshot.docs[index].get('Organizer'),
desc: blogSnapshot.docs[index].get('desc'),
imgUrl: blogSnapshot.docs[index].get('imgUrl'),
title: blogSnapshot.docs[index].get('title'),
);
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Events"),
),
body: Container(
child: blogSnapshot != null
? blogsList()
: const Center(
child: CircularProgressIndicator(),
)),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add),
onPressed: () {
Navigator.push(
context, MaterialPageRoute(builder: (context) => AddBlog()));
},
),
);
}
}
class BlogTile extends StatelessWidget {
final String imgUrl, title, desc, organizer;
const BlogTile(
{required this.organizer,
required this.desc,
required this.imgUrl,
required this.title});
#override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.only(bottom: 24, right: 16, left: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(8)),
child: Image.network(
imgUrl,
width: MediaQuery.of(context).size.width,
fit: BoxFit.cover,
height: 200,
),
),
),
const SizedBox(height: 16),
Text(
title,
style: const TextStyle(fontSize: 17),
),
const SizedBox(height: 2),
Text(
'$desc - By $organizer',
style: const TextStyle(fontSize: 14),
)
],
),
);
}
}
///////////////////////////////////////////// class AddBlog begins here \\\\\\\\\\\
import 'dart:io';
import 'package:myfuji/screens/CrudMethods.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:random_string/random_string.dart';
class AddBlog extends StatefulWidget {
#override
_AddBlogState createState() => _AddBlogState();
}
class _AddBlogState extends State<AddBlog> {
//
late File selectedImage;
final picker = ImagePicker();
bool isLoading = false;
CrudMethods crudMethods = new CrudMethods();
Future getImage() async {
final pickedFile = await picker.getImage(source: ImageSource.gallery);
setState(() {
if (pickedFile != null) {
selectedImage = File(pickedFile.path);
} else {
print('No image selected.');
}
});
}
Future<void> uploadBlog() async {
if (selectedImage != null) {
// upload the image
setState(() {
isLoading = true;
});
Reference firebaseStorageRef = FirebaseStorage.instance
.ref()
.child("events/")
.child("${randomAlphaNumeric(9)}.jpg");
final UploadTask task = firebaseStorageRef.putFile(selectedImage);
var imageUrl;
await task.whenComplete(() async {
try {
imageUrl = await firebaseStorageRef.getDownloadURL();
} catch (onError) {
print("Error");
}
print(imageUrl);
});
// print(downloadUrl);
Map<String, dynamic> blogData = {
"imgUrl": imageUrl,
"Organizer": authorTextEditingController.text,
"title": titleTextEditingController.text,
"desc": descTextEditingController.text
};
crudMethods.addData(blogData).then((value) {
setState(() {
isLoading = false;
});
Navigator.pop(context);
});
// upload the blog info
}
}
//
TextEditingController titleTextEditingController =
new TextEditingController();
TextEditingController descTextEditingController = new TextEditingController();
TextEditingController authorTextEditingController =
new TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Create Blog"),
actions: [
GestureDetector(
onTap: () {
uploadBlog();
},
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 16),
child: Icon(Icons.file_upload)),
)
],
),
body: isLoading
? Container(
child: Center(
child: CircularProgressIndicator(),
))
: SingleChildScrollView(
child: Container(
margin: EdgeInsets.symmetric(horizontal: 16),
child: Column(
children: [
GestureDetector(
onTap: () {
getImage();
},
child: selectedImage != null
? Container(
height: 150,
margin: EdgeInsets.symmetric(vertical: 24),
width: MediaQuery.of(context).size.width,
child: ClipRRect(
borderRadius:
BorderRadius.all(Radius.circular(8)),
child: Image.file(
selectedImage,
fit: BoxFit.cover,
),
),
)
: Container(
height: 150,
decoration: BoxDecoration(
color: Colors.grey,
borderRadius:
BorderRadius.all(Radius.circular(8))),
margin: EdgeInsets.symmetric(vertical: 24),
width: MediaQuery.of(context).size.width,
child: Icon(
Icons.camera_alt,
color: Colors.white,
),
),
),
TextField(
controller: titleTextEditingController,
decoration: InputDecoration(hintText: "enter title"),
),
TextField(
controller: descTextEditingController,
decoration: InputDecoration(hintText: "enter desc"),
),
TextField(
controller: authorTextEditingController,
decoration:
InputDecoration(hintText: "enter author name"),
),
],
)),
),
);
}
}
///////////////////////////////////////////// class CrudMethods begins here \\\\\\\\\\\
import 'package:cloud_firestore/cloud_firestore.dart';
class CrudMethods {
Future<void> addData(blogData) async {
print(blogData);
FirebaseFirestore.instance
.collection("events/")
.add(blogData)
.then((value) => print(value))
.catchError((e) {
print(e);
});
}
getData() async {
return await FirebaseFirestore.instance.collection("events/").get();
}
}
/////////////////////////////////////////////firestore\\\\\\\\\\\\\
This maybe related to having “/“ after collection name here:
.collection("events/")
Instead try this:
.collection("events")
Also it may be best to change child to collection here:
Reference firebaseStorageRef = FirebaseStorage.instance
.ref()
.child("events/")
Try to see if you get data back by running this:
itemCount: blogSnapshot.docs.length,
itemBuilder: (context, index) {
QuerySnapshot snap = blogSnapshot.data; // Snapshot
List<DocumentSnapshot> items = snap.documents; // List of Documents
DocumentSnapshot item = items[index]; Specific Document
return BlogTile(
organizer: item.data['Organizer'],
desc: item.data['desc'],
imgUrl: item.data['imgUrl'],
title: item.data['title'],
);
},
I think you need to utilize a QueryDocumentSnapshot to access the data in the document.
I am trying to use bloc to fetch contacts from contact service and display the contact names in an overlay widget in a list view. Can you please check my code and let me know what is wrong as it is not able to fetch any contacts and display the name.
Thank you for your time in advance.
P.S. I have added print statements but none of them are displaying the contact details(name).
File1: main.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:invite_friends/contactsBloc.dart';
import 'accessContacts.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(scaffoldBackgroundColor: Colors.white),
home: BlocProvider
(create: (BuildContext context) => ContactCubit(),
child: AccessContacts()),
);
}
}
File 2: accessContacts.dart
import 'package:contacts_service/contacts_service.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:permission_handler/permission_handler.dart';
import 'contactDetails.dart';
import 'contactsBloc.dart';
class AccessContacts extends StatelessWidget {
late OverlayEntry _overlayEntry;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: Padding(
padding: const EdgeInsets.only(left: 16, top: 80),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ElevatedButton(onPressed: () {
_overlayEntry = showOverlayContacts(context);
Overlay.of(context)?.insert(_overlayEntry);
}, child: const Text('Grant permission to access contacts'),
style: ElevatedButton.styleFrom(
minimumSize: const Size(199, 41),
elevation: 5.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5.0)),
shadowColor: const Color(0xFFE5E5E5),
primary: const Color(0xFFE5E5E5),
onPrimary: const Color(0xFFB13937),
textStyle: const TextStyle(fontFamily: 'Poppins',
fontSize: 14)),),
],
),
),
);
}
// check contacts permission
Future<PermissionStatus> _getPermission() async {
final PermissionStatus permission = await Permission.contacts.request();
print(permission);
return permission;
}
showOverlayContacts(BuildContext context) {
final ContactCubit _contactBloc = BlocProvider.of<ContactCubit>(context);
BlocProvider.of<ContactCubit>(context).getContacts();
TextEditingController editingController = TextEditingController();
OverlayState? overlayState = Overlay.of(context);
return OverlayEntry(builder: (context) =>
Positioned(
bottom: 350,
width: MediaQuery
.of(context)
.size
.width,
height: MediaQuery
.of(context)
.size
.height / 2,
child: Card(
elevation: 20,
child: searchContacts(editingController,_contactBloc),
),
), opaque: false);
}
Column searchContacts(TextEditingController editingController,_contactBloc) {
List<Contact> contacts;
return Column(
// mainAxisAlignment: MainAxisAlignment.start,
// crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(top: 22, left: 16, right: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: const [
Text('Find contacts (upto 5) ',
style: TextStyle(fontFamily: 'Poppins',
fontSize: 14)),
Icon(Icons.cancel),
],
),
),
const Divider(
color: Colors.grey
),
Padding(
padding: const EdgeInsets.only(left: 16),
child: TextField(
onChanged: (value) {
print(value);
},
controller: editingController,
showCursor: true,
autofocus: true,
decoration: const InputDecoration(
border: InputBorder.none,
isDense: true,
suffixIcon: Padding(
padding: EdgeInsets.only(right: 16),
child: Icon(Icons.search, size: 30,),
),
suffixIconConstraints: BoxConstraints(
minWidth: 20,
minHeight: 20,
),
),
),
),
const Divider(
color: Colors.grey
),
BlocBuilder<ContactCubit, List<Contact>>(bloc: _contactBloc, builder: (BuildContext context, List<Contact> contacts){
return Expanded(
child: ListView.builder(itemCount: 3,itemBuilder: (context, index) {
Contact contact = contacts.elementAt(index);
return Text(contact.displayName ?? '');
},
),
);},)
],);
}}
File3: contactsBloc.dart
import 'package:contacts_service/contacts_service.dart';
import 'package:invite_friends/contactDetails.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:invite_friends/getContactList.dart';
import '';
class ContactCubit extends Cubit<List<Contact>> {
ContactCubit() : super([]);
final _dataService = DataService();
Future getContacts() async {
await _dataService.getPhoneContacts();
print(state.length);
// _dataService.getPhoneContacts();
emit(state);
}
}
File 4: getContactList.dart
import 'package:contacts_service/contacts_service.dart';
import 'package:permission_handler/permission_handler.dart';
import 'contactDetails.dart';
class DataService {
Future getPhoneContacts() async {
try{
Contact contact;
final PermissionStatus permissionStatus = await Permission.contacts.request();
if (permissionStatus == PermissionStatus.granted) {
// Get all contacts without thumbnail (faster)
List<Contact> contacts = await ContactsService.getContacts(withThumbnails: false);
for(contact in contacts){
print(contact.displayName);
}
} }
catch(e){
rethrow;
}
return [];
}
}
In your DataService class, when you call getPhoneContacts() you fetch the List of contacts List<Contact> contacts but only print it to console.
I think what you want to do is return it to your Bloc, so you have to add return contacts; and change the return type of getPhoneContacts() to Future<List<Contact>>.
In your ContactCubit you have to assign the result of your await _dataService.getPhoneContacts(); to a variable and emit this as State, e.g.:
Future getContacts() async {
final contacts = await _dataService.getPhoneContacts();
print(contacts.length);
emit(contacts);
}
And in your widget you can then access the contact by using a BlocBuilder like this:
BlocBuilder.of<ContactCubit, List<Contact>>(
bloc: _contactBloc,
(context, contacts) {
return Expanded(
child: ListView.builder(
itemCount: contacts.length,
itemBuilder: (context, index) {
Contact contact = contacts.elementAt(index);
return Text(contact.displayName ?? '');
},
),
),
}
)
My app something like Udemy app, it's developed by Flutter
I was published iOS release to App store and working well
but in Android studio doing strange situation...
When I run app on emulator or real device connected to Android studio every thing working well, but when run it with command
Flutter run --release
or build the apk file or abb file
the raisedbutton with Whatsapp icon shown in image below doesn't work
The big problem in signing page [code shown below] when user try to signin I get this HTTP try get error with message
MissingPluginException(No implementation found for method getAll on channel plugins.flutter.io/shared_preferences
Note: I use word (errrrrrrrrrrrrrooooor) in code shown below to show where error message show
after search about this error I get that we get this error when no previous values in shared preferences, but in app first use before signing I don't use shared preferences
So, I don't understand why this happen, specially in apk release and working well in test mode on emulator or real device
Note: I was add Internet permissions in manifest file
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
So, I need help about that
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:connect/models/app_logo.dart';
import 'package:connect/widgets/app_bar_two.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/services.dart';
import 'package:http/http.dart' as http;
import '../models/common_functions.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../providers/auth.dart';
import '../models/http_exception.dart';
import '../constants.dart';
enum AuthMode { Signup, Login }
class AuthScreen extends StatefulWidget {
static const routeName = '/auth';
#override
_AuthScreenState createState() => _AuthScreenState();
}
class _AuthScreenState extends State<AuthScreen> {
final _controller = StreamController<AppLogo>();
final searchController = TextEditingController();
fetchMyLogo() async {
var url = BASE_URL +'/api/app_logo';
try {
final response = await http.get(url);
print(response.body);
if (response.statusCode == 200) {
var logo = AppLogo.fromJson(jsonDecode(response.body));
_controller.add(logo);
}
// print(extractedData);
} catch (error) {
throw (error);
}
}
void initState() {
super.initState();
this.fetchMyLogo();
}
#override
Widget build(BuildContext context) {
final deviceSize = MediaQuery.of(context).size;
// final transformConfig = Matrix4.rotationZ(-8 * pi / 180);
// transformConfig.translate(-10.0);
return Scaffold(
appBar: CustomAppBarTwo(),
// resizeToAvoidBottomInset: false,
body: Stack(
children: <Widget>[
SingleChildScrollView(
child: Container(
height: deviceSize.height,
width: deviceSize.width,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
child: StreamBuilder<AppLogo>(
stream: _controller.stream,
builder: (context, snapshot) {
if (snapshot.connectionState ==
ConnectionState.waiting) {
return Container();
} else {
if (snapshot.error != null) {
return Text("Error Occured");
} else {
// saveImageUrlToSharedPref(snapshot.data.darkLogo);
return CachedNetworkImage(
imageUrl: snapshot.data.favicon,
fit: BoxFit.contain,
height: 33,
);
}
}
},
),
),
Container(
margin: EdgeInsets.only(bottom: 20.0),
padding:
EdgeInsets.symmetric(vertical: 4.0, horizontal: 94.0),
child: Text(
'Login',
style: TextStyle(
color: kTextColor,
fontSize: 30,
fontWeight: FontWeight.normal,
),
),
),
Flexible(
flex: deviceSize.width > 600 ? 2 : 1,
child: AuthCard(),
),
],
),
),
),
],
),
);
}
}
class AuthCard extends StatefulWidget {
const AuthCard({
Key key,
}) : super(key: key);
#override
_AuthCardState createState() => _AuthCardState();
}
class _AuthCardState extends State<AuthCard> {
final GlobalKey<FormState> _formKey = GlobalKey();
Map<String, String> _authData = {
'email': '',
'password': '',
};
var _isLoading = false;
final _passwordController = TextEditingController();
Future<void> _submit() async {
if (!_formKey.currentState.validate()) {
// Invalid!
return;
}
_formKey.currentState.save();
setState(() {
_isLoading = true;
});
try {
// Log user in
await Provider.of<Auth>(context, listen: false).login(
_authData['email'],
_authData['password'],
);
String deviceName;
String deviceVersion;
String identifier;
final DeviceInfoPlugin deviceInfoPlugin = new DeviceInfoPlugin();
try {
if (Platform.isAndroid) {
var build = await deviceInfoPlugin.androidInfo;
deviceName = build.model;
deviceVersion = build.version.toString();
identifier = build.androidId; //UUID for Android
} else if (Platform.isIOS) {
var data = await deviceInfoPlugin.iosInfo;
deviceName = data.name;
deviceVersion = data.systemVersion;
identifier = data.identifierForVendor; //UUID for iOS
}
} on PlatformException {
print('Failed to get platform version');
}
try{
var response = await http.post(
Uri.parse("https://connect-elearning.com/newVersion/APIs/checkIDntfr.php"),
body: {
'IdentifierPst': identifier,
'userNoPst': _authData['email'],
});
Map<String,dynamic> data = jsonDecode(response.body);
String svdIdentifier = data["svdIdentifier"];
String svdUserID = data["svdUserID"];
String realUserID = data["RealUserID"];
if(svdIdentifier != "notFound"){
if(svdIdentifier == identifier && svdUserID == realUserID){
Navigator.pushNamedAndRemoveUntil(context, '/home', (r) => false);
//CommonFunctions.showSuccessToast('Login Successful');
CommonFunctions.showSuccessToast('Login Successful');
} else if(svdIdentifier != identifier || svdUserID != realUserID){
// SharedPreferences preferences = await SharedPreferences.getInstance();
// await preferences.clear();
showDialog(
context: context,
builder: (_) {
return AlertDialog(
title: Text('Your account is registered for another device, please contact technical support.'),
//title: Text('Your ${svdIdentifier} ${svdUserID} ${realUserID}'),
actions: [
FlatButton(
onPressed: () => Navigator.pop(context, true), // passing true
child: Text('Exit'),
),
],
);
}).then((exit) {
if (exit == null) {SystemNavigator.pop();}
if (exit) {
SystemNavigator.pop();
} else {
SystemNavigator.pop();
}
});
}
} else {
try{
var response = await http.post(
Uri.parse("https://connect-elearning.com/newVersion/APIs/userIDntfrSv.php"),
body: {
'userNoPst': _authData['email'],
'dvNamePst': deviceName,
//'dvOSPst': deviceVersion,
'dvIdntPst': identifier,
});
} catch (e){
print(e);
}
Navigator.pushNamedAndRemoveUntil(context, '/home', (r) => false);
CommonFunctions.showSuccessToast('Login Successful');
}
} catch (e) {
//print("note that ${e}");
}
} on HttpException catch (error) {
var errorMsg = 'Wrong data, you can contact customers services';
CommonFunctions.showErrorDialog(errorMsg, context);
} catch (error) {
//const errorMsg = 'Could not authenticate!, may you need to contact to customers services via whats-app: +201033040777';
//String errorMsg = 'Done!${error}, Go to My courses page';
Error message shown in the next line
print("errrrrrrrrrrrrrooooor ${error}");
}
setState(() {
_isLoading = false;
});
}
#override
Widget build(BuildContext context) {
final deviceSize = MediaQuery.of(context).size;
return Container(
height: 360,
constraints: BoxConstraints(minHeight: 260),
width: deviceSize.width * 0.8,
padding: EdgeInsets.all(16.0),
child: Form(
key: _formKey,
child: SingleChildScrollView(
child: Column(
children: <Widget>[
TextFormField(
decoration: InputDecoration(
//labelText: 'Email Address',
labelText: 'Mobile',
prefixIcon: Icon(
//Icons.mail_outline,
Icons.phone_android,
color: Colors.grey,
), // myIcon is a 48px-wide widget.
),
//keyboardType: TextInputType.emailAddress,
keyboardType: TextInputType.phone,
validator: (input) => input.length < 11
? "Enter valid mobile number!"
: null,
/*validator: (value) {
if (value.isEmpty || !value.contains('#')) {
return 'Invalid email!';
}
},*/
onSaved: (value) {
_authData['email'] = value;
},
),
TextFormField(
decoration: InputDecoration(
labelText: 'Password',
prefixIcon: Icon(
Icons.vpn_key,
color: Colors.grey,
),
),
obscureText: true,
controller: _passwordController,
validator: (value) {
if (value.isEmpty || value.length < 4) {
return 'Password is too short!';
}
},
onSaved: (value) {
_authData['password'] = value;
},
),
SizedBox(
height: 20,
),
if (_isLoading)
CircularProgressIndicator()
else
ButtonTheme(
minWidth: deviceSize.width * 0.8,
child: RaisedButton(
child: Text(
'Sign In',
style: TextStyle(fontWeight: FontWeight.bold),
),
onPressed: _submit,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(7.0),
side: BorderSide(color: kBlueColor),
),
splashColor: Colors.blueAccent,
padding:
EdgeInsets.symmetric(horizontal: 50.0, vertical: 20),
color: kBlueColor,
textColor: Colors.white,
),
),
SizedBox(
height: 20,
),
/* ButtonTheme(
minWidth: deviceSize.width * 0.8,
child: RaisedButton(
child: Text(
'Create Account',
style: TextStyle(fontWeight: FontWeight.bold),
),
onPressed: () {
Navigator.of(context).pushNamed(SignUpScreen.routeName);
},
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(7.0),
side: BorderSide(color: kGreyColor),
),
splashColor: Colors.blueAccent,
padding: EdgeInsets.symmetric(horizontal: 50.0, vertical: 20),
color: kGreyColor,
textColor: kTextColor,
),
),*/
],
),
),
),
);
}
}
Edit:
When I try to run app with command
flutter run --release
I get this 2 errors in Terminal
E/flutter (18074): [ERROR:flutter/lib/ui/ui_dart_state.cc(199)] Unhandled Exception: MissingPluginException(No implementation found for method getAll on channel plugins.flutter.io/shared_preferences)
E/flutter (18074):
E/flutter (18074): [ERROR:flutter/lib/ui/ui_dart_state.cc(199)] Unhandled Exception: MissingPluginException(No implementation found for method checkPermissionStatus on channel flutter.baseflow.com/permi
ssions/methods)
and I try to run
flutter clean
flutter pub get
... etc, and nothing new
Update
when I updated gradle to the last version, app working well except few users still get same error
I don't understand why!
I have a google_sign_in button which when clicked should present the google sign in then the user should be redirected to a CreateAccountPage. The page has an input and a submit button. When the user submits their preferred username, the google account data together with the created username should be stored as a document in users collection cloud firestore database and the user redirected to another page, say TimelinePage.
Below is my home.dart file:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:musagram/pages/activity_feed.dart';
import 'package:musagram/pages/create_account.dart';
import 'package:musagram/pages/profile.dart';
import 'package:musagram/pages/search.dart';
import 'package:musagram/pages/timeline.dart';
import 'package:musagram/pages/upload.dart';
final GoogleSignIn googleSignIn = GoogleSignIn();
final usersRef = FirebaseFirestore.instance.collection('users');
final DateTime timestamp = DateTime.now();
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
bool isAuth = false;
PageController pageController;
int pageIndex = 0;
#override
void initState() {
super.initState();
pageController = PageController();
// Detects when user signed in
googleSignIn.onCurrentUserChanged.listen((account) {
handleSignIn(account);
}, onError: (err) {
print('Error signing in: $err');
});
// Reauthenticate user when app is opened
googleSignIn.signInSilently(suppressErrors: false).then((account) {
handleSignIn(account);
}).catchError((err) {
print('Error signing in: $err');
});
}
handleSignIn(GoogleSignInAccount account) {
if (account != null) {
createUserInFirestore();
setState(() {
isAuth = true;
});
} else {
setState(() {
isAuth = false;
});
}
}
createUserInFirestore() async {
// 1) check if user exists in users collection in database (according to their id)
final GoogleSignInAccount user = googleSignIn.currentUser;
final DocumentSnapshot doc = await usersRef.doc(user.id).get();
if (!doc.exists) {
// 2) if the user doesn't exist, then we want to take them to the create account page
final username = await Navigator.push(
context, MaterialPageRoute(builder: (context) => CreateAccount()));
// 3) get username from create account, use it to make new user document in users collection
usersRef.doc(user.id).set({
"id": user.id,
"username": username,
"photoUrl": user.photoUrl,
"email": user.email,
"displayName": user.displayName,
"bio": "",
"timestamp": timestamp
});
}
}
#override
void dispose() {
pageController.dispose();
super.dispose();
}
login() {
googleSignIn.signIn();
}
logout() {
googleSignIn.signOut();
}
onPageChanged(int pageIndex) {
setState(() {
this.pageIndex = pageIndex;
});
}
onTap(int pageIndex) {
pageController.animateToPage(
pageIndex,
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
}
Scaffold buildAuthScreen() {
return Scaffold(
body: PageView(
children: <Widget>[
// Timeline(),
ElevatedButton(
child: Text('Logout'),
onPressed: logout,
),
ActivityFeed(),
Upload(),
Search(),
Profile(),
],
controller: pageController,
onPageChanged: onPageChanged,
physics: NeverScrollableScrollPhysics(),
),
bottomNavigationBar: CupertinoTabBar(
currentIndex: pageIndex,
onTap: onTap,
activeColor: Theme.of(context).primaryColor,
items: [
BottomNavigationBarItem(icon: Icon(Icons.whatshot)),
BottomNavigationBarItem(icon: Icon(Icons.notifications_active)),
BottomNavigationBarItem(
icon: Icon(
Icons.photo_camera,
size: 35.0,
),
),
BottomNavigationBarItem(icon: Icon(Icons.search)),
BottomNavigationBarItem(icon: Icon(Icons.account_circle)),
]),
);
// return RaisedButton(
// child: Text('Logout'),
// onPressed: logout,
// );
}
Scaffold buildUnAuthScreen() {
return Scaffold(
body: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topRight,
end: Alignment.bottomLeft,
colors: [
Theme.of(context).accentColor,
Theme.of(context).primaryColor,
],
),
),
alignment: Alignment.center,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text(
'MusaGram',
style: TextStyle(
fontFamily: "Signatra",
fontSize: 90.0,
color: Colors.white,
),
),
GestureDetector(
onTap: login,
child: Container(
width: 260.0,
height: 60.0,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(
'assets/images/google_signin_button.png',
),
fit: BoxFit.cover,
),
),
),
)
],
),
),
);
}
#override
Widget build(BuildContext context) {
return isAuth ? buildAuthScreen() : buildUnAuthScreen();
}
}
And here's the create_account.dart
import 'package:flutter/material.dart';
import 'package:musagram/widgets/header.dart';
class CreateAccount extends StatefulWidget {
#override
_CreateAccountState createState() => _CreateAccountState();
}
class _CreateAccountState extends State<CreateAccount> {
final _formKey = GlobalKey<FormState>();
String username;
submit() {
_formKey.currentState.save();
Navigator.pop(context, username);
}
#override
Widget build(BuildContext parentContext) {
return Scaffold(
appBar: header(context, titleText: "Set up your profile"),
body: ListView(
children: <Widget>[
Container(
child: Column(
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 25.0),
child: Center(
child: Text(
"Create a username",
style: TextStyle(fontSize: 25.0),
),
),
),
Padding(
padding: EdgeInsets.all(16.0),
child: Container(
child: Form(
key: _formKey,
child: TextFormField(
onSaved: (val) => username = val,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: "Username",
labelStyle: TextStyle(fontSize: 15.0),
hintText: "Must be at least 3 characters",
),
),
),
),
),
GestureDetector(
onTap: submit,
child: Container(
height: 50.0,
width: 350.0,
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(7.0),
),
child: Center(
child: Text(
"Submit",
style: TextStyle(
color: Colors.white,
fontSize: 15.0,
fontWeight: FontWeight.bold,
),
),),
),
),
],
),
),
],
),
);
}
}
method of google sign in looks like this. It will first authenticate the user then it will check is users data present on the firestore. if it's present data will not be overridden and else data will be added.
also at the end you can navigate to your createAccountPage. and you can pass the required values to the constructor. directly map of data or values.
Future<UserCredential> signInWithGoogle() async {
//first trigger authentication
final GoogleSignInAccount googleUser = await GoogleSignIn().signIn();
//obtain the auth details
final GoogleSignInAuthentication googleAuth =
await googleUser.authentication;
//create a new credentials
final GoogleAuthCredential credential = GoogleAuthProvider.credential(
accessToken: googleAuth.accessToken, idToken: googleAuth.idToken);
//saving the user data to shared preferences
SharedPrefsHelper _sharedpref = new SharedPrefsHelper();
// // sign in method
try {
UserCredential firebaseUser =
await FirebaseAuth.instance.signInWithCredential(credential);
if (firebaseUser != null) {
// Check is already sign up
final QuerySnapshot result = await FirebaseFirestore.instance
.collection('users')
.where('id', isEqualTo: firebaseUser.user.uid)
.get();
//userdata to update at firebase
//passing this via constructor to the next page
Map<String, dynamic> userdata = {
'useremail': firebaseUser.user.email,
'displayname': firebaseUser.user.displayName,
'photoUrl': firebaseUser.user.photoURL,
'userid': firebaseUser.user.uid,
};
// navigate . if you don't have context here you can pass this as a paramater to the
method. no issue if you call inside build or init
//userdata is map. just for handling easily
//you can also create simple variables and pass each value to them.
Navigator.pushReplacement(context, CreateUserAccount(userdata));
}
return firebaseUser;
} catch (error) {
print(error);
}
}
Inside the CreateUserAccount
class CreateUserAccount extends StatefulWidget {
final Map<String, dynamic> userdata;
CreateUserAccount(this.userdata);
#override
CreateUserAccountState createState() => CreateUserAccountState();
}
class CreateUserAccountState extends State<CreateUserAccount> {
TextEditingController _textController = new TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Column(
children: [
Container(
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 20),
child: TextField(
controller: _textController,
),
),
ElevatedButton(
onPressed: () async{
//get the username from textfield
//and update data to firebase
widget.userdata['username'] = _textController.text;
await FirebaseFirestore.instance
.collection('users')
.doc(widget.userdata['userId'])
.set(
widget.userdata,
SetOptions(merge: true),
);
Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) => TimeLinePage());
},
child: Text("submit"),
),
],
),
));
}
}
Inside the CreateUserAccount we simply added the username in map and added record in firebase. Then you can navigate to the timeline after this.
my code when it found the data object it turns fine and i do not have any error. but when the data that i pass is not in the database it will return the exception. I don't know where to fix the error
exception image
this is my code. the widget.barcode is i get from another file.
modelRuangFasiliti.dart
class RuangFasiliti {
final String ruangfasiliti_id;
final String ruang_id;
final String fasiliti_id;
final String kuantiti;
final String namaruang;
final String namafasiliti;
RuangFasiliti({
this.ruangfasiliti_id,
this.ruang_id,
this.fasiliti_id,
this.kuantiti,
this.namaruang,
this.namafasiliti,
});
factory RuangFasiliti.fromJson(Map<String, dynamic> json) {
return RuangFasiliti(
ruangfasiliti_id: json['ruangfasiliti_id'],
ruang_id: json['ruang_id'],
fasiliti_id: json['fasiliti_id'],
kuantiti: json['kuantiti'],
namaruang: json['namaruang'],
namafasiliti: json['namafasiliti'],
);
}
}
borangaduan.dart
import 'dart:convert';
import 'package:eaduanfsktm/api.dart';
import 'package:eaduanfsktm/sejarahaduan.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:http/http.dart' as http;
import 'package:eaduanfsktm/model/modelRuangFasiliti.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'dart:io';
import 'package:image_picker/image_picker.dart';
import 'dart:math' as Math;
import 'package:image/image.dart' as Img;
import 'package:path_provider/path_provider.dart';
import 'dart:async';
import 'package:async/async.dart';
import 'package:path/path.dart';
class BorangAduan extends StatefulWidget {
final String idpengguna, barcode;
BorangAduan(this.idpengguna, this.barcode);
#override
_BorangAduanState createState() => _BorangAduanState();
}
class _BorangAduanState extends State<BorangAduan> {
final _key = new GlobalKey<FormState>();
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
TextEditingController controllerruang_id;
TextEditingController controllerfasiliti_id;
TextEditingController controller_namaruang;
TextEditingController controller_namafasiliti;
TextEditingController controllermaklumat = new TextEditingController();
File _image;
RuangFasiliti ruangfasiliti = new RuangFasiliti();
Future<RuangFasiliti> getruangfasiliti() async {
final response = await http.get(BaseUrl.lihatruangfasiliti(widget.barcode));
if (response.statusCode == 200) {
setState(
() {
ruangfasiliti = RuangFasiliti.fromJson(json.decode(response.body));
controllerruang_id =
new TextEditingController(text: "${ruangfasiliti.ruang_id}");
controllerfasiliti_id =
new TextEditingController(text: "${ruangfasiliti.fasiliti_id}");
controller_namaruang =
new TextEditingController(text: " ${ruangfasiliti.namaruang}");
controller_namafasiliti =
new TextEditingController(text: " ${ruangfasiliti.namafasiliti}");
},
);
}
return ruangfasiliti;
}
#override
void initState() {
super.initState();
getruangfasiliti();
}
bool isloading = false;
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: Text("Borang Aduan"),
),
body: isloading
? new CircularProgressIndicator()
: new ListView(
children: <Widget>[
aduanbox(),
SizedBox(height: 10.0),
],
),
),
);
}
Widget aduanbox() {
return Container(
child: Form(
key: _key,
child: Padding(
padding: const EdgeInsets.all(2.0),
child: Card(
//color: Colors.black,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
new Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Expanded(
child: Container(
child: new TextFormField(
controller: controllerfasiliti_id,
readOnly: true,
decoration: InputDecoration(
labelText: "KOD FASILITI",
),
),
),
flex: 2,
),
SizedBox(width: 10.0),
Expanded(
child: Container(
child: new TextFormField(
controller: controller_namafasiliti,
readOnly: true,
decoration: InputDecoration(
labelText: "NAMA FASILITI",
),
),
),
flex: 2,
),
],
),
new Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Expanded(
child: Container(
child: new TextFormField(
controller: controllerruang_id,
readOnly: true,
decoration: InputDecoration(
labelText: "KOD LOKASI",
),
),
),
flex: 2,
),
SizedBox(width: 10.0),
Expanded(
child: Container(
child: new TextFormField(
controller: controller_namaruang,
readOnly: true,
decoration: InputDecoration(
labelText: "LOKASI",
),
),
),
flex: 2,
),
],
),
TextFormField(
controller: controllermaklumat,
validator: (value) {
if (value.isEmpty) {
return 'Masukkan Maklumat Kerosakan';
}
return null;
},
decoration: InputDecoration(
labelText: "MAKLUMAT",
hintText: "Masukkan maklumat kerosakan"),
),
SizedBox(height: 10.0),
Row(
children: <Widget>[
RaisedButton(
child: Icon(Icons.image),
onPressed: getImageGallery,
),
SizedBox(width: 5.0),
RaisedButton(
child: Icon(Icons.camera_alt),
onPressed: getImageCamera,
),
],
),
SizedBox(height: 10.0),
Container(
alignment: Alignment.centerLeft,
child: _image == null
? new Text("Tiada imej !")
: new Image.file(_image),
),
SizedBox(height: 10.0),
Container(
height: 45.0,
child: GestureDetector(
onTap: () {
if (_key.currentState.validate()) {
tambahaduan(_image);
}
},
child: Material(
borderRadius: BorderRadius.circular(10.0),
color: Colors.blueAccent,
elevation: 7.0,
child: Center(
child: Text(
'HANTAR',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontFamily: 'Montserrat'),
),
),
),
),
),
],
),
),
),
),
),
);
}
Future getImageGallery() async {
var imageFile = await ImagePicker.pickImage(source: ImageSource.gallery);
final tempDir = await getTemporaryDirectory();
final path = tempDir.path;
int rand = new Math.Random().nextInt(100000);
Img.Image image = Img.decodeImage(imageFile.readAsBytesSync());
Img.Image smallerImg = Img.copyResize(image, width: 500);
var compressImg = new File("$path/image_$rand.jpg")
..writeAsBytesSync(Img.encodeJpg(smallerImg, quality: 85));
setState(() {
_image = compressImg;
});
}
Future getImageCamera() async {
var imageFile = await ImagePicker.pickImage(source: ImageSource.camera);
final tempDir = await getTemporaryDirectory();
final path = tempDir.path;
int rand = new Math.Random().nextInt(100000);
Img.Image image = Img.decodeImage(imageFile.readAsBytesSync());
Img.Image smallerImg = Img.copyResize(image, width: 500);
var compressImg = new File("$path/image_$rand.jpg")
..writeAsBytesSync(Img.encodeJpg(smallerImg, quality: 85));
setState(() {
_image = compressImg;
});
}
Future tambahaduan(File _image) async {
var stream = new http.ByteStream(DelegatingStream.typed(_image.openRead()));
var length = await _image.length();
var uri = Uri.parse((BaseUrl.tambahaduan()));
var request = new http.MultipartRequest("POST", uri);
var multipartFile = new http.MultipartFile("aduanimages", stream, length,
filename: basename(_image.path));
request.fields['fasiliti_id'] = controllerfasiliti_id.text;
request.fields['ruang_id'] = controllerruang_id.text;
request.fields['maklumat'] = controllermaklumat.text;
request.fields['idpengguna'] = widget.idpengguna;
request.files.add(multipartFile);
var response = await request.send();
if (response.statusCode == 200) {
print("Image Uploaded");
setState(
() {
Fluttertoast.showToast(
msg: "Aduan Berjaya",
toastLength: Toast.LENGTH_LONG,
gravity: ToastGravity.CENTER,
backgroundColor: Colors.black,
textColor: Colors.white,
fontSize: 18.0,
);
Navigator.of(this.context).push(CupertinoPageRoute(
builder: (BuildContext context) => SejarahAduan()));
},
);
} else {
print("Upload Failed");
}
response.stream.transform(utf8.decoder).listen((value) {
print(value);
});
}
}
please help...
Add a check in getruangfasiliti() (in borangaduan.dart file) to verify if response.body is null or empty .you may have to add an else condition to show the user that you didn't get any data.