Related
So I made a CRUD app with Firebase Firestore in Flutter. I have this error where it says
Exception caught by gesture
═══════════════════════
a document path must be a non-empty string
'package:cloud_firestore/src/collection_reference.dart':
package:cloud_firestore/src/collection_reference.dart:1
Failed assertion: line 116 pos 14: 'path.isNotEmpty'
I build this app with various tutorial videos in Youtube. I can make the Create, Read, and Delete features but I face this problem with Update features. Here's the code ...or check this Github https://github.com/rayhanyovi/Dapurku
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:intl/intl.dart';
import 'package:dapurkuu/warna.dart';
import 'package:dapurkuu/widget.dart';
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
//=====================================
const Color Red = Color(0xFFDA4040);
const Color Blue = Color(0xFF5F52EE);
const Color Black = Color(0xFF3A3A3A);
const Color Grey = Color(0xFF717171);
const Color BGColor = Color(0xFFEEEFF5);
final controllerName = TextEditingController();
final controllerAge = TextEditingController();
final controllerBirthday = TextEditingController();
final updateName = TextEditingController();
final updateAge = TextEditingController();
final updateBirthday = TextEditingController();
//=====================================
class User {
String id;
final String name;
final int age;
final DateTime birthday;
User({
this.id = '',
required this.name,
required this.age,
required this.birthday,
});
Map<String, dynamic> toJson() => {
'id': id,
'name': name,
'age': age,
'birthday': birthday,
};
static User fromJson(Map<String, dynamic> json) => User(
id: json['id'],
name: json['name'],
age: json['age'],
birthday: (json['birthday'] as Timestamp).toDate(),
);
}
//=====================================
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
SystemChrome.setSystemUIOverlayStyle(
SystemUiOverlayStyle(statusBarColor: Colors.transparent));
return MaterialApp(
debugShowCheckedModeBanner: false,
title: "Test",
home: Home(),
);
}
}
//=====================================
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
final CollectionReference _users =
FirebaseFirestore.instance.collection('users');
#override
Widget build(BuildContext context) {
Future createUser(User user) async {
final docUser = FirebaseFirestore.instance.collection('users').doc();
user.id = docUser.id;
final json = user.toJson();
await docUser.set(json);
Future updateUser(User user) async {
final docUser = FirebaseFirestore.instance.collection('users').doc();
user.id = docUser.id;
final json = user.toJson();
await docUser.update(json);
}
}
Widget buildUser(User user) {
return Container(
margin: EdgeInsets.only(
right: 20,
left: 20,
bottom: 15,
),
child: Material(
child: ListTile(
onTap: () {
print("card");
},
contentPadding:
EdgeInsets.only(top: 10, bottom: 10, left: 20, right: 15),
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
tileColor: Colors.white,
title: Padding(
padding: const EdgeInsets.only(bottom: 5),
child: Text(
user.name,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Jumlah: ${user.age}',
),
Text(
'Kadaluarsa: ${DateFormat('dd MMMM yyyy').format(user.birthday)}')
],
),
trailing: Wrap(spacing: 0, children: [
IconButton(
icon: Icon(Icons.edit),
iconSize: 24,
color: Colors.black,
onPressed: () async {
await Edit(context, createUser);
},
),
IconButton(
icon: Icon(Icons.delete),
iconSize: 24,
color: Colors.red,
onPressed: () {
final docUser = FirebaseFirestore.instance
.collection('users')
.doc(user.id);
docUser.delete();
},
),
]),
),
),
);
}
return Scaffold(
backgroundColor: BGColor,
appBar: _AppBar(),
body: Column(
children: [
/* JUDUL */
Column(
children: [
Container(
margin: EdgeInsets.only(left: 20),
alignment: Alignment.centerLeft,
child: SearchBox()),
Container(
margin: EdgeInsets.only(left: 20),
alignment: Alignment.centerLeft,
child: Text(
'Bahan Dapurmu',
style: TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
color: Black,
),
),
)
],
),
/* STREAM BUILDER */
StreamBuilder(
stream: readUsers(),
builder: (context, snapshot) {
if (snapshot.hasData) {
final users = snapshot.data!;
return Expanded(
child: ListView(
scrollDirection: Axis.vertical,
shrinkWrap: true,
children: users.map(buildUser).toList(),
),
);
} else if (snapshot.hasError) {
return Container(
child: Text("data kosong"),
);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
}),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
await Create(context, createUser);
},
child: Icon(Icons.add),
),
);
}
Edit(BuildContext context, Future<dynamic> updateUser(User user)) async {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text("Tambahkan bahan"),
content: Column(
children: [
TextField(
decoration: const InputDecoration(
labelText: 'Nama',
),
controller: updateName,
),
TextField(
decoration: const InputDecoration(
labelText: 'Umur',
),
controller: updateAge,
keyboardType: TextInputType.number,
),
TextField(
controller: updateBirthday,
//editing controller of this TextField
decoration: InputDecoration(
labelText: "Enter Date" //label text of field
),
readOnly: true,
//set it true, so that user will not able to edit text
onTap: () async {
DateTime? pickedDate = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(1950),
//DateTime.now() - not to allow to choose before today.
lastDate: DateTime(2100));
if (pickedDate != null) {
print(
pickedDate);
//pickedDate output format => 2021-03-10 00:00:00.000
//formatted date output using intl package => 2021-03-16
setState(() {
updateBirthday.text = pickedDate
.toString(); //set output date to TextField value.
});
} else {}
},
),
],
),
actions: <Widget>[
MaterialButton(
elevation: 5.0,
child: Text("Ubah Data"),
onPressed: () {
final user = User(
name: updateName.text,
age: int.parse(updateAge.text),
birthday: DateTime.parse(updateBirthday.text),
);
final docUser = FirebaseFirestore.instance
.collection('users')
.doc(user.id);
updateUser(user);
}),
],
);
});
}
Create(BuildContext context, Future<dynamic> createUser(User user)) async {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text("Tambahkan bahan"),
content: Column(
children: [
TextField(
decoration: const InputDecoration(
labelText: 'Nama',
),
controller: controllerName,
),
TextField(
decoration: const InputDecoration(
labelText: 'Umur',
),
controller: controllerAge,
keyboardType: TextInputType.number,
),
TextField(
controller: controllerBirthday,
//editing controller of this TextField
decoration: InputDecoration(
labelText: "Enter Date" //label text of field
),
readOnly: true,
//set it true, so that user will not able to edit text
onTap: () async {
DateTime? pickedDate = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(1950),
//DateTime.now() - not to allow to choose before today.
lastDate: DateTime(2100));
if (pickedDate != null) {
print(
pickedDate);
//pickedDate output format => 2021-03-10 00:00:00.000
//formatted date output using intl package => 2021-03-16
setState(() {
controllerBirthday.text = pickedDate
.toString(); //set output date to TextField value.
});
} else {}
},
),
],
),
actions: <Widget>[
MaterialButton(
elevation: 5.0,
child: Text("Tambahkan"),
onPressed: () {
final user = User(
name: controllerName.text,
age: int.parse(controllerAge.text),
birthday: DateTime.parse(controllerBirthday.text),
);
createUser(user);
}),
],
);
});
}
Stream<List<User>> readUsers() => FirebaseFirestore.instance
.collection('users')
.snapshots()
.map((snapshot) =>
snapshot.docs.map((doc) => User.fromJson(doc.data())).toList());
}
//=====================================
AppBar _AppBar() {
return AppBar(
elevation: 0,
backgroundColor: BGColor,
title: Center(
child: Text(
"Dapurku",
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Black,
),
),
),
);
}
//=====================================
class UpdateButton extends StatelessWidget {
const UpdateButton({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
child: IconButton(
icon: Icon(Icons.edit),
iconSize: 24,
color: Colors.black,
onPressed: () {},
),
);
}
}
//=====================================
class SearchBox extends StatelessWidget {
const SearchBox({
Key? key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.only(left: 10),
child: const TextField(
decoration: InputDecoration(
contentPadding: EdgeInsets.all(0),
prefixIcon: Icon(
Icons.search,
color: Black,
size: 20,
),
prefixIconConstraints: BoxConstraints(
maxHeight: 20,
minWidth: 25,
),
border: InputBorder.none,
hintText: "Cari Bahan...",
hintStyle: TextStyle(
color: Grey,
)),
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
),
);
}
}
Can you tell me what I did wrong or help me solve the problem? I've been looking for it anywhere and solving this problem for days. It is for my school assignment :/
this is my code
import 'package:flutter/src/widgets/container.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:intl/intl.dart';
import 'package:flutter/material.dart';
class NewTransaction extends StatefulWidget {
final Function addTx;
NewTransaction(this.addTx);
#override
State<NewTransaction> createState() => _NewTransactionState();
}
class _NewTransactionState extends State<NewTransaction> {
final _titleController = TextEditingController();
final _amountController = TextEditingController();
DateTime _selectedDate;
void _submitData() {
final enteredTitle = _titleController.text;
final enteredAmount = double.parse(_amountController.text);
if (enteredTitle.isEmpty || enteredAmount <= 0) {
return;
}
widget.addTx(
enteredTitle,
enteredAmount,
);
Navigator.of(context).pop();
}
void _presentDatePicker() {
showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2019),
lastDate: DateTime.now(),
).then((pickedDate) {
if (pickedDate == null) {
return;
}
setState(() {
_selectedDate = pickedDate;
});
});
print('...');
}
#override
Widget build(BuildContext context) {
return Card(
elevation: 5,
child: Container(
padding: EdgeInsets.all(10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
TextField(
decoration: InputDecoration(labelText: 'Title'),
controller: _titleController,
onSubmitted: (_) => _submitData(),
// onChanged: (val) {
// titleInput = val;
// },
),
TextField(
decoration: InputDecoration(labelText: 'Amount'),
keyboardType: TextInputType.number,
onSubmitted: (_) => _submitData(),
controller: _amountController,
// onChanged: (val) => amountInput = val,
),
Container(
height: 70,
child: Row(
children: <Widget>[
Expanded(
child: Text(
_selectedDate == null
? 'No Date Chosen!'
: 'Picked Date: ${DateFormat.yMd().format(_selectedDate)}',
),
),
TextButton(
child: Text(
'Choose Date',
style: TextStyle(
color: Theme.of(context).primaryColor,
fontWeight: FontWeight.bold,
),
),
onPressed: _presentDatePicker,
)
],
),
),
ElevatedButton(
child: Text(
'Add Transaction',
style: TextStyle(
color: Colors.white,
),
),
onPressed: _submitData,
style: ButtonStyle(
backgroundColor: MaterialStateProperty.resolveWith(
(states) {
return Colors.purple;
},
),
),
),
],
),
),
);
}
}
there's a redline on DateTime _selectedDate;
what should I do?
You're trying to make it null in the code so that you check over it with e _selectedDate == null.
well, you can tell Dart that it can be null from its declaration with a ?:
DateTime? _selectedDate; // add ?
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
class NewTransaction extends StatefulWidget {
final Function addTx;
NewTransaction(this.addTx);
#override
_NewTransactionState createState() => _NewTransactionState();
}
class _NewTransactionState extends State<NewTransaction> {
final _titleController = TextEditingController();
final _amountController = TextEditingController();
DateTime _selectedDate;
void _submitData() {
if (_amountController.text.isEmpty) {
return;
}
final enteredTitle = _titleController.text;
final enteredAmount = double.parse(_amountController.text);
if (enteredTitle.isEmpty || enteredAmount <= 0 || _selectedDate == null) {
return;
}
widget.addTx(
enteredTitle,
enteredAmount,
_selectedDate,
);
Navigator.of(context).pop();
}
void _presentDatePicker() {
showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2019),
lastDate: DateTime.now(),
).then((pickedDate) {
if (pickedDate == null) {
return;
}
setState(() {
_selectedDate = pickedDate;
});
});
print('...');
}
#override
Widget build(BuildContext context) {
return Card(
elevation: 5,
child: Container(
padding: EdgeInsets.all(10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
TextField(
decoration: InputDecoration(labelText: 'Title'),
controller: _titleController,
onSubmitted: (_) => _submitData(),
// onChanged: (val) {
// titleInput = val;
// },
),
TextField(
decoration: InputDecoration(labelText: 'Amount'),
controller: _amountController,
keyboardType: TextInputType.number,
onSubmitted: (_) => _submitData(),
// onChanged: (val) => amountInput = val,
),
Container(
height: 70,
child: Row(
children: <Widget>[
Expanded(
child: Text(
_selectedDate == null
? 'No Date Chosen!'
: 'Picked Date: ${DateFormat.yMd().format(_selectedDate)}',
),
),
// ignore: deprecated_member_use
FlatButton(
textColor: Theme.of(context).primaryColor,
child: Text(
'Choose Date',
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
onPressed: _presentDatePicker,
),
],
),
),
// ignore: deprecated_member_use
RaisedButton(
child: Text('Add Transaction'),
color: Theme.of(context).primaryColor,
textColor: Theme.of(context).primaryColor,
onPressed: _submitData,
),
],
),
),
);
}
}
Here in the above code. _selectedDate is giving me error. I just looked after adding late but its not also error and here is the error message,
Non-nullable instance field '_selectedDate' must be initialized.
Try adding an initializer expression, or a generative constructor that initializes it, or mark it 'late'.
You can make it nullable like this:
DateTime? _selectedDate;
I am retrieving a list of items by querying on input from textfield in firebase and showing it as list in UI in flutter as shown here
Here is my code-
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:flutter_app_news/screens/drawer/news_drawer.dart';
import 'package:flutter_app_news/service/search_service/search_service.dart';
class Watchlist extends StatefulWidget {
#override
_WatchlistState createState() => new _WatchlistState();
}
class _WatchlistState extends State<Watchlist> {
var tempSearchStore = [];
var queryResult = [];
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
CollectionReference users = FirebaseFirestore.instance.collection('Users');
initiateSearch(value) {
if (value.length == 0) {
setState(() {
queryResult = [];
tempSearchStore = [];
});
}
if (queryResult.length == 0 && value.length == 1) {
SearchService().searchByName(value).then((QuerySnapshot snapshot) {
for (int i = 0; i < snapshot.docs.length; i++) {
queryResult.add(snapshot.docs[i].data());
setState(() {
tempSearchStore.add(queryResult[i]);
});
}
});
} else {
tempSearchStore = [];
queryResult.forEach((element) {
if (element['name'].toString().startsWith(value)) {
setState(() {
tempSearchStore.add(element);
});
}
});
}
;
if (tempSearchStore.length == 0 && value.length > 1) {
setState(() {});
}
}
#override
Widget build(BuildContext context) {
return new Scaffold(
extendBodyBehindAppBar: true,
appBar: AppBar(
iconTheme: IconThemeData(color: Colors.black),
backgroundColor: Colors.white,
title: Text(
"Finbox",
style: TextStyle(color: Colors.black),
),
),
drawer: Drawer(child: NewsDrawer()),
body: ListView(children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
"Your WatchList",
style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500),
),
),
Padding(
padding: const EdgeInsets.all(10.0),
child: TextField(
onChanged: (val) {
initiateSearch(val);
},
decoration: InputDecoration(
prefixIcon: IconButton(
color: Colors.black,
icon: Icon(Icons.arrow_back),
iconSize: 20.0,
onPressed: () {
Navigator.of(context, rootNavigator: true).pop(context);
},
),
contentPadding: EdgeInsets.only(left: 25.0),
hintText: 'Search by name',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(4.0))),
),
),
SizedBox(height: 10.0),
ListView.builder(
shrinkWrap: true,
itemCount: tempSearchStore.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(tempSearchStore[index]['name']),
onTap: () {
users.doc(_firebaseAuth.currentUser.uid).update({
"subscribeTopic": FieldValue.arrayUnion(
[tempSearchStore[index]['name']])
});
Navigator.pop(context);
},
);
}),
SizedBox(
height: 10,
),
StreamBuilder(
stream: FirebaseFirestore.instance
.collection("Users")
.doc(_firebaseAuth.currentUser.uid)
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<DocumentSnapshot> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Center(
child: CircularProgressIndicator(
valueColor:
AlwaysStoppedAnimation<Color>(Colors.black)));
default:
return SingleChildScrollView(
child: GridView.builder(
shrinkWrap: true,
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(
childAspectRatio: MediaQuery.of(context)
.size
.width /
(MediaQuery.of(context).size.height / 7),
crossAxisCount: 2),
itemCount: snapshot.data.get('subscribeTopic').length,
itemBuilder: (context, index) {
return GestureDetector(
onLongPress: () {
showAlertDialog(context,
snapshot.data.get('subscribeTopic')[index]);
print(
snapshot.data.get('subscribeTopic')[index]);
},
child: Container(
child: Text(
snapshot.data.get('subscribeTopic')[index],
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w500),
),
margin: EdgeInsets.all(15),
decoration: BoxDecoration(
color: Color(0xFF92f7bb),
),
),
);
}),
);
}
})
]));
}
showAlertDialog(BuildContext context, value) {
// set up the buttons
Widget yesButton = ElevatedButton(
child: Text("Yes"),
onPressed: () async {
// await FirebaseMessaging.instance.unsubscribeFromTopic(value);
users.doc(_firebaseAuth.currentUser.uid).update({
"subscribeTopic": FieldValue.arrayRemove([value])
});
Navigator.of(context)
.push(MaterialPageRoute(builder: (context) => Watchlist()));
;
},
);
Widget noButton = ElevatedButton(
child: Text("No"),
onPressed: () {
Navigator.of(context).pop();
},
);
// set up the AlertDialog
AlertDialog alert = AlertDialog(
title: Text("Finbox"),
content: Text("Do you like to unsubscribe for " + value + " ?"),
actions: [
yesButton,
noButton,
],
);
// show the dialog
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
}
}
Here initiateSearch() is function which returns queries in firebase based on input given in textfield and tempSerachStore is list of key value pair. And I am fetching values whose key is "name".
Now the problem I am facing is-
When I write something in textfield and it queries and return a list, list is creating above those green boxes and boxes gets shifted downward as shown in image.
When I tap on one of these items in list or tap on back arrow in textfield , I am poping out to black screen.
What is the output I want-
I want to show this list above these green boxes(whose code is not included here).
When I tap on the item or tap on back arrow , list view should closed.
I will be grateful for help.
Use conditional statement along with Stack. It will work.
Stack(
children: [
// whatever you want to show. Positioned will come on top of it.
data.length == 0 // your search result length
? SizedBox(height: 0)
: Positioned(
top: 0,
width: double.infinity,
child: Container(
// whatever you want to show here
),
]
),
For a good UI experience, it is a good practice to create a separate page for search. On selecting an option, you can bring back the user on your previous page.
I hope you all are doing well today. I have another flutter issue that I have been stuck on for the past few days now. I'm attempting to upload this data to my firestore instance, but my post button never seems to be triggering. I have attempted to print a statement from the method that it evokes, but I can't seem to get that to work either. I'm attempting to create a social media app, and any and all help would be appreciated.
My main goal is to get the post button to execute in upload.dart.
I have also included home.dart since the two classes are connected in terms of performance.
upload.dart
import 'dart:io';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:fluttermedia/models/user.dart';
import 'package:fluttermedia/pages/home.dart';
import 'package:fluttermedia/widgets/progress.dart';
import 'package:image_picker/image_picker.dart';
import 'package:path_provider/path_provider.dart';
import 'package:image/image.dart' as Im;
import 'package:uuid/uuid.dart';
class Upload extends StatefulWidget {
final User currentUser;
Upload({this.currentUser});
#override
_UploadState createState() => _UploadState();
}
class _UploadState extends State<Upload> {
TextEditingController locationController = TextEditingController();
TextEditingController captionController = TextEditingController();
File file;
bool isUploading = false;
String postId = Uuid().v4();
handleChooseFromGallery() async{
Navigator.pop(context);
File file = await ImagePicker.pickImage(source: ImageSource.gallery);
setState(() {
this.file = file;
});
}
handleTakePhoto() async {
Navigator.pop(context);
File file = await ImagePicker.pickImage(source: ImageSource.camera,maxHeight: 675,maxWidth: 960);
setState(() {
this.file = file;
});
}
selectImage(parentContext){
return showDialog(
context: parentContext,
builder: (context) {
return SimpleDialog(
title: Text("Create Post"),
children: <Widget>[
SimpleDialogOption(
child: Text("Photo With Camera"),
onPressed: handleTakePhoto,
),
SimpleDialogOption(
child: Text("Image from Gallery"),
onPressed: handleChooseFromGallery,
),
SimpleDialogOption(
child: Text("Cancel"),
onPressed: () => Navigator.pop(context),
),
],
);
}
);
}
Container buildSplashScreen(){
return Container(
color: Theme.of(context).accentColor.withOpacity(0.6),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SvgPicture.asset('assets/images/upload.svg',height: 260.0,),
Padding(
padding: EdgeInsets.only(top:20.0),
child: RaisedButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
),
child: Text(
"Upload Image",
style: TextStyle(
color: Colors.white,
fontSize: 22.0,
),
),
color: Colors.deepOrange,
onPressed: () => selectImage(context),
),
)
],
),
);
}
clearImage(){
setState(() {
file = null;
});
}
//This compresses images for firebase
compressImage() async{
final tempDir = await getTemporaryDirectory();
final path = tempDir.path;
Im.Image imageFile = Im.decodeImage(file.readAsBytesSync());
final compressedImageFile = File('$path/img_$postId.jpg')..writeAsBytesSync(Im.encodeJpg(imageFile,quality: 85));
setState(() {
file = compressedImageFile;
});
}
Future<String> uploadImage(imageFile) async{
StorageUploadTask uploadTask = storageRef.child("post_$postId.jpg").putFile(imageFile);
StorageTaskSnapshot storageSnap = await uploadTask.onComplete;
String downloadUrl = await storageSnap.ref.getDownloadURL();
return downloadUrl;
}
//upload new info to firestore that creates a new collection
createPostInFirestore({String mediaUrl, String location, String description}){
postsRef.document(widget.currentUser.id)
.collection("userPosts")
.document(postId)
.setData({
"postId": postId,
"ownerId": widget.currentUser.id,
"username": widget.currentUser.username,
"mediaUrl": mediaUrl,
"description": description,
"location": location,
"timestamp": timeStamp,
"likes":{}
});
}
//Getting the info from the caption, location and pic
handleSubmit() async{
setState(() {
isUploading = true;
});
await compressImage();
String mediaUrl = await uploadImage(file);
createPostInFirestore(
mediaUrl: mediaUrl,
location: locationController.text,
description: captionController.text,
);
captionController.clear();
locationController.clear();
setState(() {
file = null;
isUploading = false;
});
}
Scaffold buildUploadForm(){
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white70,
leading: IconButton(
icon: Icon(Icons.arrow_back,color: Colors.black,),
onPressed: clearImage,
),
title: Text(
"Caption Post",
style: TextStyle(color: Colors.black),
),
actions: <Widget>[
FlatButton(
onPressed: () => isUploading ? null : () => handleSubmit(),
child: Text(
"Post",
style: TextStyle(
color: Colors.blueAccent,
fontWeight: FontWeight.bold,
fontSize: 20.0,
),
),
)
],
),
body: ListView(
children: <Widget>[
isUploading ? linearProgress(context):Text(""),
Container(
height: 220.0,
width: MediaQuery.of(context).size.width*0.8,
child: Center(
child: AspectRatio(
aspectRatio: 16/9,
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.cover,
image: FileImage(file),
)
),
),
),
),
),
Padding(
padding: EdgeInsets.only(top:10),
),
ListTile(
leading: CircleAvatar(
backgroundImage: CachedNetworkImageProvider(widget.currentUser.photoUrl),
),
title: Container(
width: 250.0,
child: TextField(
controller: captionController,
decoration: InputDecoration(
hintText: "Write a Caption...",
border: InputBorder.none,
),
),
),
),
Divider(),
ListTile(
leading: Icon(Icons.pin_drop,color: Colors.orange,size: 35.0),
title: Container(
width: 250.0,
child: TextField(
controller: locationController,
decoration: InputDecoration(
hintText: "Where was this photo taken",
border: InputBorder.none,
),
),
),
),
Container(
width: 200.0,
height: 100.0,
alignment: Alignment.center,
child: RaisedButton.icon(
label: Text(
"Use Current Location",
style: TextStyle(color: Colors.white),
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30.0),
),
color: Colors.blue,
onPressed: () => print("Get user location"),
icon: Icon(
Icons.my_location,
color: Colors.white,
),
),
)
],
),
);
}
#override
Widget build(BuildContext context) {
return file == null ? buildSplashScreen() : buildUploadForm();
}
}
home.dart
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:fluttermedia/models/user.dart';
import 'package:fluttermedia/pages/activity_feed.dart';
import 'package:fluttermedia/pages/create_account.dart';
import 'package:fluttermedia/pages/profile.dart';
import 'package:fluttermedia/pages/search.dart';
import 'package:fluttermedia/pages/upload.dart';
import 'package:google_sign_in/google_sign_in.dart';
final GoogleSignIn googleSignIn = GoogleSignIn();
final StorageReference storageRef = FirebaseStorage.instance.ref();
final usersRef = Firestore.instance.collection('users');
final postsRef = Firestore.instance.collection('posts');
final DateTime timeStamp = DateTime.now();
User currentUser;
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 if user signs in
googleSignIn.onCurrentUserChanged.listen((account) {
handleSignIn(account);
}, onError: (err){
print('Error sigining in: $err');
});
//Reauthenticate user when app is opened
googleSignIn.signInSilently(suppressErrors: false)
.then((account) =>
handleSignIn(account)).catchError((err){
print('Error signing in on retry: $err');
});
}
#override
Widget build(BuildContext context) {
return isAuth ? buildAuthScreen() : buildUnAuthScreen();
}
#override
void dispose(){
pageController.dispose();
super.dispose();
}
//Helper Functions
//The sign in section of the code
handleSignIn(GoogleSignInAccount account){
if(account != null){
createUserInFirestore();
setState(() {
isAuth = true;
});
}else{
setState(() {
isAuth = false;
});
}
}
login(){
googleSignIn.signIn();
}
logout(){
googleSignIn.signOut();
}
onPageChanged(int pageIndex){
setState(() {
this.pageIndex = pageIndex;
});
}
createUserInFirestore() async{
// 1) Check if user exists in users collection in database (According to id)
final GoogleSignInAccount user = googleSignIn.currentUser;
DocumentSnapshot doc = await usersRef.document(user.id).get();
if(!doc.exists){
// 2) If the user doesn't exist, take them to 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.document(user.id).setData({
"id":user.id,
"username":username,
"photoUrl": user.photoUrl,
"email":user.email,
"displayName": user.displayName,
"bio":"",
"timeStamp": timeStamp,
});
doc = await usersRef.document(user.id).get();
}
currentUser = User.fromDocument(doc);
//print(currentUser);
//print(currentUser.username);
}
onTap(int pageIndex){
//This what you would use to animate in between the different screens
pageController.animateToPage(
pageIndex,
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut
);
}
//UI Code
Widget buildAuthScreen() {
return Scaffold(
body: PageView(
children: <Widget>[
//Timeline(),
RaisedButton(
child: Text('Logout'),
onPressed: logout,
),
ActivityFeed(),
Upload(currentUser: currentUser),
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: 34.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(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topRight,
end: Alignment.bottomLeft,
colors: [
Theme.of(context).primaryColor,
Theme.of(context).accentColor,
]
)
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text('FlutterMedia',
style: TextStyle(
fontFamily: "Signatra",
fontSize: 90.0,
color: Colors.white
),
),
GestureDetector(
onTap:() => login(),
child: Container(
width: 260,
height: 60,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/images/google_signin_button.png'),
fit: BoxFit.cover,
),
),
),
)
],
),
),
);
}
}
onPressed: () => isUploading ? null : () => handleSubmit(),
Well, there's your problem. You meant to have a tertiary condition that makes onPressed call handleSubmit when isUploading is false. Instead, you have made onPressed into a function that returns a function.
To hopefully make that more clear, let's blow this function up into proper non-lambda functions and if/else blocks:
onPressed: () {
if (isUploading) {
return null;
} else {
return () {
handleUpload();
}
}
}
So consider what happens when the button is pressed. It calls the outer function, which checks isUploading. If true, the function returns null, and if false, it returns another function that, if called, calls handleUpload. So how this plays out is that onPressed will never be null (it just returns null sometimes) and handleUpload never gets called (since the inner function that is returned is never then called itself).
Remove the outer lambda and it will work:
onPressed: isUploading ? null : () => handleSubmit(),