How to set a Sub-Collection as DateTime.Now()? - flutter

I have an app where users, every day can take photos of their day and then display them in a Carsoule for that day. For instance, if he takes 10 photos today, all of them go to one subcollection and then display them on one page of the carousel. I tried this solution but didn't work out, Really appreciate the help
class Cam extends StatefulWidget {
const Cam({super.key});
#override
State<Cam> createState() => _CamState();
}
class _CamState extends State<Cam> {
File? image;
final db = FirebaseFirestore.instance;
final storageRef = FirebaseStorage.instance;
final ImagePicker _picker = ImagePicker();
Future cam() async {
final image = await _picker.pickImage(source: ImageSource.camera);
if (image == null) return;
final temImage = File(image.path);
final fileName = basename(temImage.path);
final destination = 'files/$fileName';
try {
UploadTask uploadTask = storageRef.ref(destination).putFile(temImage);
String urlRef = await (await uploadTask).ref.getDownloadURL();
final photo = PhotoModel(ImgUrl: urlRef, dateCreation: DateTime.now());
final photoToDb = db
.collection('photos')
.doc()
.collection('${DateTime.now()}')
.withConverter(
fromFirestore: PhotoModel.fromFirestore,
toFirestore: ((PhotoModel photoModel, options) =>
photoModel.toFirestore()),
);
photoToDb.add(photo);
} catch (e) {
print('errro');
}
}
#override
Widget build(BuildContext context) {
return Center(
child: IconButton(
onPressed: () {
cam();
},
icon: Icon(
Icons.camera,
size: 40,
color: Colors.red,
),
),
);
}
}

Since you're addressing the doc with .collection('${DateTime.now()}'), the collection name is based on the timestamp (including the exact time). So that will always be unique.
If you want a single collection per day, you should generate a string with just the date portion of that timestamp. Something like:
.collection(DateFormat('yyyy-MM-dd').format(DateTime.now()))
Also see: How do I format a date with Dart?

Related

How to use sharedpreferences to save users roles and navigate to a specific page depending on role in Flutter

I'm working on app that have user logins (Admin login and user login). First i make a user part and it works, the account keep logged even when the app restart. and then when i have to separate the users (admin and user) i got some problem. I don't know how to code the shared preferences, this is the code when i make a user part
preference_helper.dart
import 'package:shared_preferences/shared_preferences.dart';
class PreferencesHelper {
final Future<SharedPreferences> sharedPreferences;
const PreferencesHelper({required this.sharedPreferences});
static const String login = 'LOGIN';
void setIsLogin(bool value) async {
final prefs = await sharedPreferences;
prefs.setBool(login, value);
}
Future<bool> get isLogin async {
final prefs = await sharedPreferences;
return prefs.getBool(login) ?? false;
}
}
i use the provider like this
preference_notifier.dart
class PreferencesNotifier extends ChangeNotifier {
PreferencesHelper preferencesHelper;
PreferencesNotifier({required this.preferencesHelper}) {
_getIsLogin();
}
bool _isLogin = false;
bool get isLogin => _isLogin;
void _getIsLogin() async {
_isLogin = await preferencesHelper.isLogin;
notifyListeners();
debugPrint(_isLogin ? 'isLogin true' : 'isLogin false');
}
void setIsLogin(bool value) async {
preferencesHelper.setIsLogin(value);
_getIsLogin();
}
}
i want to use shared preferences to save the user roles and navigate to specific page. So if the user's log in it will go to the UserHomePage and if the admin log in it will go to the AdminHomePage. My backend is firebase firestore.
this is part of sign page (when click register button)
MaterialButton(
color: primaryColor,
textTheme: ButtonTextTheme.primary,
height: 40,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
onPressed: () async {
setState(() {
_isLoading = true;
});
try {
final navigator = Navigator.of(context);
final email = _emailController.text;
final password = _passwordController.text;
const role = "user";
await _auth
.createUserWithEmailAndPassword(
email: email,
password: password,
)
.then((value) => {postDetailsToFirestore(email, role)});
navigator.pop();
} catch (err) {
final snackBar = SnackBar(content: Text(err.toString()));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
} finally {
setState(() {
_isLoading = false;
});
}
},
child: const Text('Signup'),
),
postDetailsToFirestore(String email, String role) async {
FirebaseFirestore firebaseFirestore = FirebaseFirestore.instance;
var user = _auth.currentUser;
CollectionReference ref = firebaseFirestore.collection('users');
ref.doc(user!.uid).set({'email': _emailController.text, 'role': role});
}
this is the login page (when click the login button)
MaterialButton(
color: primaryColor,
textTheme: ButtonTextTheme.primary,
height: 40,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
onPressed: () async {
setState(() {
_isLoading = true;
});
try {
final navigator = Navigator.of(context);
final email = _emailController.text;
final password = _passwordController.text;
await _auth.signInWithEmailAndPassword(
email: email,
password: password,
);
route();
value.setIsLogin(true);
navigator.pushReplacementNamed(HomePage.routeName);
} catch (err) {
final snackBar = SnackBar(content: Text(err.toString()));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
} finally {
setState(() {
_isLoading = false;
});
}
},
child: const Text('Login'),
),
i want to navigate the navigator to specific user role
this is the route() function
void route() {
User? user = FirebaseAuth.instance.currentUser;
FirebaseFirestore.instance.collection('users').doc(user!.uid).get().then(
(DocumentSnapshot documentSnapshot) {
if (documentSnapshot.exists) {
if (documentSnapshot.get('role') == "user") {
Navigator.pushNamed(context, UserHomePage.routeName);
} else {
Navigator.pushNamed(context, AdminHomePage.routeName);
}
} else {
debugPrint('Document does not exist on the database');
}
},
);
}
and this is the main.dart at runApp()
runApp(
await preferencesHelper.isLogin
? const MyApp(
pageRouteName: HomePage.routeName,
)
: const MyApp(
pageRouteName: LoginPage.routeName,
),
);
I really need to know how am i supposed to do because this is for my exam. I'm sorry if my english is bad, i'm barely use English to talk. Thank you
that code that i share is what i tried to make sharedpreferences but it just for 1 user, i dont know how to separate user (admin and user)
First of all, you need to use architecture to separate the UI from logic and in your architect, you have to create a layer to handle basic requests of the local database and then create a class for implementing basic commands of the database, then you can create a separated storage layer for each of entities that you have.
the abstract basic commands class is like this :
abstract class LocalStorage {
Future<void> write(final String key, final dynamic json);
dynamic read<S>(final String key);
void remove(final String key);
void removeAll();
}
and for implementation :
class StorageService implements LocalStorage {
StorageService() {
_init();
}
late GetStorage storage;
void _init() {
storage = GetStorage();
}
#override
Future<void> write(final String key, final dynamic value) async {
await storage.write(key, convert.jsonEncode(value));
}
#override
dynamic read<S>(final String key) {
final value = storage.read(key);
if (value == null) return;
return convert.jsonDecode(value.toString());
}
#override
void remove(final String key) {
GetStorage().remove(key);
}
#override
void removeAll() {
GetStorage.Remove(key1);
GetStorage.Remove(key2);
...
}
}
and for Usage for each entity:
class UserStorage {
final LocalStorage _storage;
Future<void> SaveUser(User usr) async {
await _storage.write(userKey, usr);
}
}
I have used GetX to handle local storage for read and write but you can replace your preferred shared preference library.

ImagePicker.platform shows warning - Flutter

I am using the following code to pick an image from user's gallery.
Future getImageFromGallery(BuildContext context) async {
await ImagePicker.platform()
.pickImage(source: ImageSource.gallery)
.then((image) {
if (image != null) {
_cropImage(image, context);
}
});
}
I am getting the following warning.
The member 'platform' can only be used within 'package:image_picker/image_picker.dart' or a test.
I'm not sure what the warning means. I tried looking it up but couldn't figure out the solution to resolve this warning.
Try below code hope its help to you
Declare File type form dart.io package
File? imagePicked;
Create Function for pick up the image
void gallaryImage() async {
final picker = ImagePicker();
final pickedImage = await picker.pickImage(
source: ImageSource.gallery,
);
final pickedImageFile = File(pickedImage!.path);
setState(() {
imagePicked = pickedImageFile;
});
}
Create your Widget
TextButton(
onPressed: gallaryImage,
child: Text(
'Gallery',
style: TextStyle(
color: Colors.black,
),
),
),
You can just change the code
ImagePicker.platform().pickImage(...)
to
ImagePicker().pickImage(...)
so
Future getImageFromGallery(BuildContext context) async {
await ImagePicker()
.pickImage(source: ImageSource.gallery)
.then((image) {
if (image != null) {
_cropImage(image, context);
}
});
}

Fetch data from firestore after Sign In before showing HomeScreen

When a User sign up for the first time, i want that he gets a own firestore document with some data. This data I want to show on the homescreen but I get an error that the data is not there yet. After hot reload the data is there so the problem is that the homescreen is shown before the data is fetched from firestore although I use a FutureBuilder. I only got this problem when a user signs in for the first time.
class GoogleSignInProvider extends ChangeNotifier {
final googleSignIn = GoogleSignIn();
GoogleSignInAccount _user;
GoogleSignInAccount get user => _user;
Future googleLogin() async {
try {
final googleUser = await googleSignIn.signIn();
if (googleUser == null) return;
_user = googleUser;
final googleAuth = await googleUser.authentication;
final credential = GoogleAuthProvider.credential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
await FirebaseAuth.instance.signInWithCredential(credential);
await getUserData();
} catch (e) {
print(e.toString());
}
notifyListeners();
}
Future getUserData() async {
final myUser = FirebaseAuth.instance.currentUser;
bool userExist = await checkIfUserExists(myUser.uid);
if (userExist == false) {
print('User dont exist');
await FirebaseFirestore.instance.collection('users').doc(myUser.uid).set({
"email": myUser.email,
"plans": [],
"userScore": "100",
});
} else {
print('User exist');
}
/// Save userData from firestore in a Helper class which is shown on the homescreen
var userData = FirebaseFirestore.instance.collection('users').doc(myUser.uid);
return FutureBuilder(
future: userData.get(),
builder: (context, userDataSnapshot) {
if (userDataSnapshot.data == ConnectionState.done) {
var value = userDataSnapshot.data;
UserManager.userdata = value.data(); //static class where userData is stored
return null;
} else {
return Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
}
});
}
Future<bool> checkIfUserExists(String uid) async {
try {
var collectionRef = FirebaseFirestore.instance.collection('users');
var doc = await collectionRef.doc(uid).get();
return doc.exists;
} catch (e) {
throw e;
}
}
EDIT
This is the Button where the user can sign In
ElevatedButton.icon(
onPressed: () {
final provider = Provider.of<GoogleSignInProvider>(context, listen: false);
provider.googleLogin();
},
icon: Icon(MdiIcons.google),
label: Text(
'Sign In with Google',
style: TextStyle(fontSize: 16),
),
),
The problem here is that FutureBuilder is a widget, it should not be used in a function to wait for a future to complete, but in another widget to have callbacks on the completion and change display based on that.
If not rendered, FutureBuilder will do nothing but be instantiated and occupy memory.
You should probably modify your code as such:
...
/// Save userData from firestore in a Helper class which is shown on the homescreen
var userData = await FirebaseFirestore.instance.collection('users').doc(myUser.uid).get();
UserManager.userdata = userData.data();
...
Should you want to add a CircularProgress on your main screen, this would be done by lisntening to your Provider in some way.

Flutter how to handle time based events?

I am having a widget in the flutter which can be dismissed by watching a rewarded video. But I don't want the widget to be completely dismissed. Say for 3 days.
So if the user clicks on the specific widget then the ads will be disabled for 3 days. Is it possible to do? Could someone help me with references or ideas to get this done?
Please help
First, Get shared preferences Package to make local storage to track the Date shared_preferences: ^2.0.5
Make A Local Storage like this -
import 'package:shared_preferences/shared_preferences.dart';
class SetUserLocalStorage {
Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
void date(String valueOfDate) async {
final SharedPreferences prefs = await _prefs;
prefs.setString(UserStorageKey().valueOfDate, valueOfDate);
}
void clear() async { // For Your Further Operation, If needed
final SharedPreferences prefs = await _prefs;
prefs.clear();
}
}
class GetUserLocalStorage {
Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
Future<String> date() async {
final SharedPreferences prefs = await _prefs;
return prefs.getString(UserStorageKey().valueOfDate);
}
}
class UserStorageKey {
String get valueOfDate => "valueOfDate";
}
Now, in your page / Screen, Define Variable -
bool _showTheAd = false;
DateTime _now = DateTime.now();
DateTime _date = DateTime.now();
and In InitState, Start Checking the Condition on the base of Time,I am making it in three part for better understanding
#override
void initState() {
super.initState();
_initialPoint();
}
in _initialPoint() -
void _initialPoint() async {
await GetUserLocalStorage().date().then((value) {
setState(() {
_date = DateTime.parse(value);
});
}).then((value) {
_conditionCheck();
});
}
In _conditionCheck -
void _conditionCheck() {
if (_date == null) {
setState(() {
_showTheAd = true;
});
} else {
setState(() {
_now = DateTime.now();
});
if (_now.isAfter(_date)) {
setState(() {
_showTheAd = true;
});
}
}
}
I know that,these are like "dirty code", but I think that will help you understand the scenario.
in body, show the add based on the _showTheAd condition, and use some interceptor / listener of kind to sense when the video is end,I am using an inkwell, and execute the code in onTap(), full scenario -
Container(
child: Column(
children: [
if (_showTheAd)
InkWell(
onTap: () {
setState(
() {
_date = _now.add(
Duration(seconds: 5),
); // to add Date _now.add(Duration(days:3));
},
);
SetUserLocalStorage().date(_date.toIso8601String());
},
child: Center(
child: Container(
height: 120,
width: 120,
color: Colors.red,
child: Text("the ad"),
),
),
)
],
),
),

How do you call one future and then pass that return value into another future to run?

I am trying to take a picture with my Android camera, upload that picture to Google Firebase Storage, get the downloadable URL of that image on Storage, and update the user's photo feed on Firestore. If I only call takeImage() it takes the image and uploads successfully to storage. If I call _uploadImage with a dummy image url, it correctly updates the feed. But I cannot get the result of takeImage to pass as a parameter to _uploadImage().
void takeAndSave() async {
url = await takeImage();
_uploadImage(url);
}
Future<String> takeImage() async {
// open camera
var image = await ImagePicker.pickImage(source: ImageSource.camera);
// save image to temp storage
final String fileName = "${Random().nextInt(10000)}.jpg";
Directory directory = await getApplicationDocumentsDirectory(); // AppData folder path
String appDocPath = directory.path;
// copy image to path
File savedImage = await image.copy('$appDocPath/' + fileName);
// upload file to Firebase Storage
final StorageReference ref = FirebaseStorage.instance.ref().child(fileName);
final StorageUploadTask task = ref.putFile(savedImage);
String downloadURL = await ref.getDownloadURL();
url = downloadURL;
// _image = image;
return downloadURL;
}
Future<void> _uploadImage(String url) async {
final FirebaseUser user = await widget.auth.currentUser();
String uid = user.uid;
print('uid = ' + uid);
print(url);
// upload URL to Firebase Firestore Cloud Storage
Firestore.instance.runTransaction((Transaction transaction) async {
DocumentReference _newPhoto = Firestore.instance.collection('users').document(user.uid);
await _newPhoto.collection('cards').add({"url" : url});
});
}
To chain future tasks: means if we have two future tasks and second is dependent upon result of first response then we can use "Future.wait()". In the below example i have created two methods with async keyword that will fetch data from server and i want to execute "fetchPostAgain()" method after the response of first "fetchPost()" then i can use "Future.wait()".
import 'dart:async';
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:flutter_app/models/Post.dart';
import 'package:http/http.dart' as http;
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
void initState() {
super.initState();
}
FetchFirstPost getFirstPost;
String myString = "Loading...";
void _takeImage() {
Future.wait([fetchPost()]).then((FutureOr) => {
fetchPostAgain()
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
title: const Text('Example'),
),
body: Center(
child: Column(
children: <Widget>[
SingleChildScrollView(
child: Text(myString),
),
RaisedButton(
child: Text("Run Future"),
onPressed: _takeImage,
),
],
),
/*child: CallApiDemo(),*/
),
),
);
}
Future<Post> fetchPost() async {
final Completer completer = Completer();
final response = await http.get('https://jsonplaceholder.typicode.com/posts/1');
log('data: ' + response .statusCode.toString());
if (response.statusCode == 200) {
// If the call to the server was successful, parse the JSON
setState(() {
myString = response.body;
});
return postFromJson(response.body);
} else {
// If that call was not successful, throw an error.
throw Exception('Failed to load post');
}
}
Future<Post> fetchPostAgain() async{
final response = await http.get('https://jsonplaceholder.typicode.com/posts/1');
log('GOT SECOND RESPONSE');
log('data: ' + response .statusCode.toString());
if (response.statusCode == 200) {
setState(() {
myString = myString + "\n\n\nAGAIN\n\n\n" + response.body;
});
return postFromJson(response.body);
} else {
// If that call was not successful, throw an error.
throw Exception('Failed to load post');
}
}
}
As per your code, it should work properly, but there could be a chance that your takeImage() method is returning an exception. Try catching that exception and see if it helps.
Below is referenced from https://www.dartlang.org/tutorials/language/futures#async-await
If a Future-returning function completes with an error, you probably want to capture that error. Async functions can handle errors using try-catch:
Future<String> takeImage() async {
try {
// Your code
} catch (e) {
// Handle error...
}
}