How can I update a specific field in my Datatable. I am using this package: https://pub.dev/packages/responsive_table
When I tap on one item in my Datatable it opens a Dialog where I have my TextController and a ElevatedButton for updating my values. All that seems to work, I click on one item, the right name of that item is stored in my TextController. But when I press my button, it's updating some random item in my Firebase and not the value that i clicked in the first place.
Here is my code:
class _UsersPageState extends State<UsersPage> {
var nameController = TextEditingController();
#override
Widget build(BuildContext context) {
final UserTableProvider usertablesProvider = Provider.of<UserTableProvider>(context);
return SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: [
Container(
margin: EdgeInsets.all(10),
padding: EdgeInsets.all(0),
constraints: BoxConstraints(
maxHeight: 700,
),
child: Card(
elevation: 1,
shadowColor: Colors.black,
clipBehavior: Clip.none,
child: ResponsiveDatatable(
title: TextButton.icon(
onPressed: () => {
addUserDialog(context),
},
icon: Icon(Icons.add),
label: Text("Legg til bruker"),
),
reponseScreenSizes: [ScreenSize.xs],
actions: [
if (usertablesProvider.isSearch)
Expanded(
child: TextField(
decoration: InputDecoration(
hintText: 'Søk på ansattnr..',
prefixIcon: IconButton(
icon: Icon(Icons.cancel),
onPressed: () {
setState(() {
usertablesProvider.isSearch = false;
});
}),
suffixIcon: IconButton(
icon: Icon(Icons.search), onPressed: () {})),
onSubmitted: (value) {
usertablesProvider.filterData(value);
},
)),
if (!usertablesProvider.isSearch)
IconButton(
icon: Icon(Icons.search),
onPressed: () {
setState(() {
usertablesProvider.isSearch = true;
});
}),
],
headers: usertablesProvider.userTableHeader,
source: usertablesProvider.source,
selecteds: usertablesProvider.selecteds,
showSelect: usertablesProvider.showSelect,
autoHeight: false,
onSort: (value) {
setState(() => usertablesProvider.isLoading = true);
setState(() {
usertablesProvider.sortColumn = value;
usertablesProvider.sortAscending = !usertablesProvider.sortAscending;
if (usertablesProvider.sortAscending) {
usertablesProvider.sourceFiltered.sort((a, b) =>
b["${usertablesProvider.sortColumn}"].compareTo(a["${usertablesProvider.sortColumn}"]));
} else {
usertablesProvider.sourceFiltered.sort((a, b) =>
a["${usertablesProvider.sortColumn}"].compareTo(b["${usertablesProvider.sortColumn}"]));
}
var _rangeTop = usertablesProvider.currentPerPage! < usertablesProvider.sourceFiltered.length
? usertablesProvider.currentPerPage!
: usertablesProvider.sourceFiltered.length;
usertablesProvider.source = usertablesProvider.sourceFiltered.getRange(0, _rangeTop).toList();
usertablesProvider.searchKey = value;
usertablesProvider.isLoading = false;
});
},
expanded: usertablesProvider.expanded,
sortAscending: usertablesProvider.sortAscending,
sortColumn: usertablesProvider.sortColumn,
isLoading: usertablesProvider.isLoading,
onTabRow: (data){
updateUserDialog(context);
print(data);
nameController.text = data['name'];
},
onSelect: (value, item) {
print("$value $item ");
if (value!) {
setState(() => usertablesProvider.selecteds.add(item));
} else {
setState(
() => usertablesProvider.selecteds.removeAt(usertablesProvider.selecteds.indexOf(item)));
}
},
onSelectAll: (value) {
if (value!) {
setState(() => usertablesProvider.selecteds =
usertablesProvider.source.map((entry) => entry).toList().cast());
} else {
setState(() => usertablesProvider.selecteds.clear());
}
},
footers: [
Container(
padding: EdgeInsets.symmetric(horizontal: 15),
child: Text("Rader per side:"),
),
if (usertablesProvider.perPages.isNotEmpty)
Container(
padding: EdgeInsets.symmetric(horizontal: 15),
child: DropdownButton<int>(
value: usertablesProvider.currentPerPage,
items: usertablesProvider.perPages
.map((e) => DropdownMenuItem<int>(
child: Text("$e"),
value: e,
))
.toList(),
onChanged: (dynamic value) {
setState(() {
usertablesProvider.currentPerPage = value;
usertablesProvider.currentPage = 1;
usertablesProvider.resetData();
});
},
isExpanded: false,
),
),
Container(
padding: EdgeInsets.symmetric(horizontal: 15),
child:
Text("${usertablesProvider.currentPage} - ${usertablesProvider.currentPerPage} of ${usertablesProvider.total}"),
),
IconButton(
icon: Icon(
Icons.arrow_back_ios,
size: 16,
),
onPressed: usertablesProvider.currentPage == 1
? null
: () {
var _nextSet = usertablesProvider.currentPage - usertablesProvider.currentPerPage!;
setState(() {
usertablesProvider.currentPage = _nextSet > 1 ? _nextSet : 1;
usertablesProvider.resetData(start: usertablesProvider.currentPage - 1);
});
},
padding: EdgeInsets.symmetric(horizontal: 15),
),
IconButton(
icon: Icon(Icons.arrow_forward_ios, size: 16),
onPressed: usertablesProvider.currentPage + usertablesProvider.currentPerPage! - 1 > usertablesProvider.total
? null
: () {
var _nextSet = usertablesProvider.currentPage + usertablesProvider.currentPerPage!;
setState(() {
usertablesProvider.currentPage = _nextSet < usertablesProvider.total
? _nextSet
: usertablesProvider.total - usertablesProvider.currentPerPage!;
usertablesProvider.resetData(start: _nextSet - 1);
});
},
padding: EdgeInsets.symmetric(horizontal: 15),
)
],
),
),
),
])
);
}
updateUserDialog(BuildContext context){
return showDialog(context: context, builder: (context) {
return Dialog(
child: Container(
child: SizedBox(
width: 600,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('Endre bruker', style: TextStyle(
fontWeight: FontWeight.bold
),),
SizedBox(
height: 10,
),
TextField(
controller: nameController,
decoration: const InputDecoration(
labelText: 'Navn',
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.grey,
),
),
border: OutlineInputBorder(
borderSide: BorderSide(color: Colors.grey)
)
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.grey,
),
),
border: OutlineInputBorder(
borderSide: BorderSide(color: Colors.grey)
)
),
),
ElevatedButton(
onPressed: () async {
var name = nameController.text.trim();
FirebaseFirestore firebaseFirestore = FirebaseFirestore.instance;
FirebaseAuth auth = FirebaseAuth.instance;
User? user = auth.currentUser;
await firebaseFirestore.collection('users').doc(user!.uid).update({
'navn': name,
});
nameController.text = '';
},
child: const Text('Save')),
const SizedBox(
height: 10,
),
],
),
),
),
);
});
}
class UserTableProvider with ChangeNotifier {
List<DatatableHeader> userTableHeader = [
DatatableHeader(
text: "Ansattnr",
value: "empnumber",
show: true,
flex: 1,
sortable: true,
textAlign: TextAlign.left),
DatatableHeader(
text: "Navn",
value: "name",
show: true,
flex: 6,
sortable: true,
textAlign: TextAlign.left),
DatatableHeader(
text: "Epost",
value: "email",
show: true,
flex: 4,
sortable: true,
textAlign: TextAlign.left),
DatatableHeader(
text: "",
value: "uid",
show: false,
sortable: true,
textAlign: TextAlign.center),
DatatableHeader(
text: "Tlf",
value: "tlfnumber",
show: true,
flex: 2,
sortable: true,
textAlign: TextAlign.left),
DatatableHeader(
text: "Eget verktøy",
value: "owntool",
show: false,
sortable: true,
textAlign: TextAlign.left),
];
final List<int> perPages = [10, 20, 50, 100];
int total = 100;
int? currentPerPage = 10;
int currentPage = 1;
List<bool>? expanded;
String? searchKey = "empnumber";
bool isSearch = false;
List<Map<String, dynamic>> sourceOriginal = [];
List<Map<String, dynamic>> sourceFiltered = [];
List<Map<String, dynamic>> source = [];
List<Map<String, dynamic>> selecteds = [];
// ignore: unused_field
final String selectableKey = "id";
String? sortColumn;
bool sortAscending = true;
bool isLoading = true;
final bool showSelect = true;
final UserServices _userServices = UserServices();
List<UserModel> _users = <UserModel>[];
List<UserModel> get users => _users;
Future _loadFromFirebase() async {
_users = await _userServices.getAllUsers();
}
List<Map<String, dynamic>> _getUsersData() {
isLoading = true;
List<Map<String, dynamic>> temps = [];
var i = users.length;
if (kDebugMode) {
print(i);
}
// ignore: unused_local_variable
for (UserModel userData in users) {
if (kDebugMode) {
print(userData.name);
}
if (kDebugMode) {
print(userData.email);
}
temps.add({
"empnumber": userData.empnumber,
"email": userData.email,
"name": userData.name,
"uid": userData.uid,
"owntool": userData.owntool,
"tlfnumber": userData.tlfnumber,
});
i++;
}
return temps;
}
_initData() async {
await _loadFromFirebase();
mockPullData();
notifyListeners();
}
mockPullData() async {
expanded = List.generate(currentPerPage!, (index) => false);
isLoading = true;
Future.delayed(const Duration(seconds: 3)).then((value) {
sourceOriginal.clear();
sourceOriginal.addAll(_getUsersData());
sourceFiltered = sourceOriginal;
total = sourceFiltered.length;
source = sourceFiltered.getRange(0, _users.length).toList();
isLoading = false;
notifyListeners();
});
}
resetData({start: 0}) async {
isLoading = true;
var expandedLen =
total - start < currentPerPage! ? total - start : currentPerPage;
Future.delayed(const Duration(seconds: 0)).then((value) {
expanded = List.generate(expandedLen as int, (index) => false);
source.clear();
source = sourceFiltered.getRange(start, start + expandedLen).toList();
isLoading = false;
notifyListeners();
});
}
filterData(value) {
isLoading = true;
try {
if (value == "" || value == null) {
source = sourceOriginal;
} else {
sourceFiltered = sourceOriginal
.where((data) => data[searchKey!]
.toString()
.toLowerCase()
.contains(value.toString().toLowerCase()))
.toList();
}
total = sourceFiltered.length;
var _rangeTop = total < currentPerPage! ? total : currentPerPage!;
expanded = List.generate(_rangeTop, (index) => false);
source = sourceFiltered.getRange(0, _rangeTop).toList();
} catch (e) {
print(e);
}
isLoading = false;
notifyListeners();
}
UserTableProvider.init() {
_initData();
}
}
The problem is the user id you are using to update the firestore document, the current user's id not the user id from the data.
Please edit your updateUserDialog method, add the data as a parameter for the function. Something like updateUserDialog(BuildContext context, Map<String, dynamic> data).
This will avail the whole data Map within the method. This will expose the uid for you to use to update the right document. You can then pass uid from the data Map as the doc as shown below.
await firebaseFirestore.collection('users').doc(userId).update({
'navn': name,
});
For the snippet above, the uid is stored in a variable userId.
Below is the complete updated method you can work with.
updateUserDialog(BuildContext context, Map<String, dynamic> data) {
nameController.text = data['name'];
String userId = data['uid'].toString();
return showDialog(
context: context,
builder: (context) {
return Dialog(
child: Container(
child: SizedBox(
width: 600,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text(
'Endre bruker',
style: TextStyle(fontWeight: FontWeight.bold),
),
const SizedBox(
height: 10,
),
TextField(
controller: nameController,
decoration: const InputDecoration(
labelText: 'Navn',
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.grey,
),
),
border: OutlineInputBorder(
borderSide: BorderSide(color: Colors.grey))),
),
ElevatedButton(
onPressed: () async {
var name = nameController.text.trim();
FirebaseFirestore firebaseFirestore =
FirebaseFirestore.instance;
await firebaseFirestore
.collection('users')
.doc(userId)
.update({
'navn': name,
});
nameController.text = '';
},
child: const Text('Save')),
const SizedBox(
height: 10,
),
],
),
),
),
);
},
);
}
You might as well update your onTabRow function to
onTabRow: (data) {
updateUserDialog(context, data);
},
That should solve your issue.
Related
I working on a project with Firebase-Flutter. I have problem with Image Upload to Firebase. When i select picture from gallery, it doesn't working. I catch this problem: " type 'XFile' is not a subtype of type 'File". Where is my error and how can i solve this problem?
Here is my codes.
class AddStatus extends StatefulWidget {
final String currentUserId;
const AddStatus({ Key? key,required this.currentUserId }) : super(key: key);
#override
State<AddStatus> createState() => _AddStatusState();
}
class _AddStatusState extends State<AddStatus> {
late String _tweetText;
i.File? _pickedImage;
bool _loading = false;
final ImagePicker _picker = ImagePicker();
handleImageFromGallery() async {
try {
i.File imageFile =
(await _picker.pickImage(source: ImageSource.gallery)) as dynamic;
if (imageFile != null) {
setState(() {
_pickedImage = i.File(_pickedImage!.path);
_pickedImage = i.File( imageFile.path );
});
}
} catch (e) {
print(e);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
centerTitle: true,
title: Text(
'Durum',
style: TextStyle(
color: Colors.white,
fontSize: 20,
),
),
),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Column(
children: [
SizedBox(height: 20),
TextField(
maxLength: 280,
maxLines: 7,
decoration: InputDecoration(
hintText: 'Ne düşünüyorsun...',
),
onChanged: (value) {
_tweetText = value;
},
),
SizedBox(height: 10),
_pickedImage == null
? SizedBox.shrink()
: Column(
children: [
Container(
height: 200,
decoration: BoxDecoration(
color: Colors.black,
image: DecorationImage(
fit: BoxFit.cover,
image: FileImage(_pickedImage!),
)),
),
SizedBox(height: 20),
],
),
GestureDetector(
onTap: handleImageFromGallery,
child: Container(
height: 70,
width: 70,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.white,
border: Border.all(
color: Colors.amber,
width: 2,
),
),
child: Icon(
Icons.camera_alt,
size: 50,
color: Colors.amber,
),
),
),
SizedBox(height: 20),
RoundedButton(
btnText: 'Paylaş',
onBtnPressed: () async {
setState(() {
_loading = true;
});
if (_tweetText != null && _tweetText.isNotEmpty) {
String image;
if (_pickedImage == null) {
image = '';
} else {
image =
await StorageService.uploadTweetPicture(_pickedImage!);
}
Status tweet = Status(
text: _tweetText,
image: image,
authorId: widget.currentUserId,
likes: 0,
timestamp: Timestamp.fromDate(
DateTime.now(),
), id: '',
);
DatabaseServices.createTweet(tweet);
Navigator.pop(context);
}
setState(() {
_loading = false;
});
},
),
SizedBox(height: 20),
_loading ? CircularProgressIndicator() : SizedBox.shrink()
],
),
),
);
}
}
here is my storage service codes`
static Future<String> uploadTweetPicture(File imageFile)
async {
String uniquePhotoId = Uuid().v4();
File? image = await compressImage(uniquePhotoId, imageFile);
UploadTask uploadTask = storageRef
.child('images/tweets/tweet_$uniquePhotoId.jpg')
.putFile(image!);
TaskSnapshot taskSnapshot = await uploadTask.whenComplete(() => null);
String downloadUrl = await taskSnapshot.ref.getDownloadURL();
return downloadUrl;
}
static Future<File?> compressImage(String photoId, File image) async {
final tempDirection = await getTemporaryDirectory();
final path = tempDirection.path;
File? compressedImage = await FlutterImageCompress.compressAndGetFile(
image.absolute.path,
'$path/img_$photoId.jpg',
quality: 70,
);
return compressedImage;
}`
I guess you need to create File from XFile
myFile = File(myXFile.path)
I am trying to fetch data from my create and showing those data into my update page data are coming. I am able to update from normal textformfield, but am not able to edit data from my drop down table. Please anyone give suggestion that where am doing mistake.
class UpdateEdic extends StatefulWidget {
final String docId;
const UpdateEdic({
Key? key,
required this.docId,
}) : super(key: key);
#override
_UpdateEdicState createState() => _UpdateEdicState();
}
class _UpdateEdicState extends State<UpdateEdic> {
final _formKey = GlobalKey<FormState>();
CollectionReference devtradecalls =
FirebaseFirestore.instance.collection('dev-trade-calls');
List<String> tslList = ['T', 'F'];
String imageUrl = "";
List<String> archivedList = ['T', 'F'];
List<String> targetAchivedList = ['T', 'F'];
DateTime tgtachtime = DateTime.now();
DateTime calltime = DateTime.now();
List tools = [];
List indicators = [];
String imageFilePath = "";
File? _imageFile;
final _picker = ImagePicker();
final format = DateFormat("HH:mm dd-MM-yyyy");
Future<void> updateEdicData(id, tsl, pattern, indicators, tools, img, notes,
targetAchieved, targetAchievedTime, archived, callTime) {
return devtradecalls
.doc(id)
.update({
'tsl': tsl,
'tslList': tslList,
'pattern': pattern,
'indicators': indicators,
'tools': tools,
'img': img,
'notes': notes,
'targetAchieved': targetAchieved,
'targetAchievedTime': targetAchievedTime,
'archived': archived,
'callTime': callTime
})
.then((value) => Navigator.pop(context))
.catchError((error) => print('Failed to update: $error'));
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Update Edic'),
),
body: Form(
key: _formKey,
child: FutureBuilder<DocumentSnapshot<Map<String, dynamic>>>(
future: FirebaseFirestore.instance
.collection('dev-trade-calls')
.doc(widget.docId)
.get(),
builder: (_, snapshot) {
if (snapshot.hasData) {
var data = snapshot.data!.data();
var tsl = data!['tsl'];
var pattern = data['pattern'];
List indicators = data['indicators'];
List tools = data['tools'];
var imgUrl = data['img'];
var notes = data['notes'];
var targetAchieved = data['targetAchieved'];
DateTime? targetAchievedTime =
(data['targetAchievedTime'] as Timestamp).toDate();
var archived = data['archived'];
DateTime? callTime = (data['callTime'] as Timestamp).toDate();
return Padding(
padding: const EdgeInsets.all(16.0),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Trailing Stop Loss',
style: labelStyle.copyWith(color: primaryColor)),
DropdownButton<String>(
isExpanded: true,
value: tsl,
items: tslList.map((String items) {
return DropdownMenuItem(
value: items,
child: Text(kEdictsl[items]!),
);
}).toList(),
onChanged: (String? newValue) {
setState(() {
tsl = newValue;
});
},
),
SizedBox(
height: 24,
),
Text('Pattern',
style: labelStyle.copyWith(color: primaryColor)),
TextFormField(
autocorrect: true,
enableSuggestions: true,
initialValue: pattern,
onChanged: (value) {
pattern = value;
},
keyboardType: TextInputType.multiline,
),
SizedBox(
height: 24,
),
Text('Indicators',
style: labelStyle.copyWith(color: primaryColor)),
MultiSelectDialogField(
listType: MultiSelectListType.CHIP,
searchable: true,
initialValue: indicators,
items: kEdicIndicators
.map((t) => MultiSelectItem<dynamic>(t, t))
.toList(),
buttonIcon: Icon(
Icons.candlestick_chart,
color: Colors.blue,
),
onConfirm: (value) => indicators = value,
chipDisplay: MultiSelectChipDisplay(
onTap: (value) {
indicators.remove(value);
},
),
),
SizedBox(height: 24.0),
Text('Tools',
style: labelStyle.copyWith(color: primaryColor)),
MultiSelectDialogField(
listType: MultiSelectListType.CHIP,
searchable: true,
items: kEdicTools
.map((t) => MultiSelectItem<dynamic>(t, t))
.toList(),
initialValue: tools,
buttonIcon: Icon(
Icons.settings_sharp,
color: Colors.blue,
),
onConfirm: (results) {
tools = results;
},
chipDisplay: MultiSelectChipDisplay(
onTap: (value) {
tools.remove(value);
},
),
),
SizedBox(
height: 24,
),
Text('Notes',
style: labelStyle.copyWith(color: primaryColor)),
TextFormField(
initialValue: notes,
autocorrect: true,
onChanged: (value) {
notes = value;
},
enableSuggestions: true,
keyboardType: TextInputType.multiline,
minLines: 1,
maxLines: 3,
),
SizedBox(
height: 24,
),
Text('Target Achived',
style: labelStyle.copyWith(color: primaryColor)),
DropdownButton<String>(
isExpanded: true,
value: targetAchieved,
hint: const Text('True/False'),
items: targetAchivedList.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(kEdicTargetAchived[value]!),
);
}).toList(),
onChanged: (value) => targetAchieved = value,
// {
// setState(() {
// // targetAchived = value!;
// });
// },
),
const SizedBox(height: 24.0),
Text('Target Achieved Time',
style: labelStyle.copyWith(color: primaryColor)),
SizedBox(
height: 24,
),
DateTimeField(
format: format,
onShowPicker: (context, currentValue) async {
final date = await showDatePicker(
context: context,
firstDate: DateTime(1900),
initialDate: currentValue ?? DateTime.now(),
lastDate: DateTime(2100));
if (date != null) {
final time = await showTimePicker(
context: context,
initialTime: TimeOfDay.fromDateTime(
currentValue ?? DateTime.now()),
);
return DateTimeField.combine(date, time);
} else {
return currentValue;
}
},
validator: (date) =>
date == null ? 'Invalid date' : null,
initialValue: targetAchievedTime,
onChanged: (value) => targetAchievedTime = value,
onSaved: (date) => setState(() {
// savedCount++;
}),
),
const SizedBox(
height: 24,
),
Text('Archived',
style: labelStyle.copyWith(color: primaryColor)),
DropdownButton<String>(
isExpanded: true,
value: archived,
hint: const Text('True/False'),
items: archivedList.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(kEdicArchived[value]!),
);
}).toList(),
onChanged: (value) => archived = value,
),
SizedBox(
height: 24,
),
Text('Call Time',
style: labelStyle.copyWith(color: primaryColor)),
SizedBox(
height: 24,
),
DateTimeField(
format: format,
onShowPicker: (context, currentValue) async {
final date = await showDatePicker(
context: context,
firstDate: DateTime(1900),
initialDate: currentValue ?? DateTime.now(),
lastDate: DateTime(2100));
if (date != null) {
final time = await showTimePicker(
context: context,
initialTime: TimeOfDay.fromDateTime(
currentValue ?? DateTime.now()),
);
return DateTimeField.combine(date, time);
} else {
return currentValue;
}
},
validator: (date) =>
date == null ? 'Invalid date' : null,
initialValue: callTime,
onChanged: (value) => callTime = value,
),
const SizedBox(
height: 24,
),
Text('Image URL',
style: labelStyle.copyWith(color: primaryColor)),
TextFormField(
autocorrect: true,
enableSuggestions: true,
initialValue: imgUrl,
onChanged: (value) {
imgUrl = value;
},
keyboardType: TextInputType.multiline,
minLines: 1,
maxLines: 3,
),
Text('Add Image',
style: labelStyle.copyWith(color: primaryColor)),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
ElevatedButton.icon(
onPressed: () async {
await pickImageFromCamera();
},
icon: Icon(
Icons.add_a_photo,
),
label: const Text('Camera'),
),
ElevatedButton.icon(
onPressed: () async {
await pickImageFromGallery();
},
icon: Icon(
Icons.collections,
),
label: const Text('Gallery'),
),
],
),
_previewImage(),
SizedBox(height: 24.0),
ElevatedButton(
style: ElevatedButton.styleFrom(
minimumSize: Size(
double.infinity,
48,
), // double.infinity is the width and 30 is the height
),
onPressed: () {
if (_formKey.currentState!.validate()) {
updateEdicData(
widget.docId,
tsl,
pattern,
indicators,
tools,
imgUrl,
notes,
targetAchieved,
targetAchievedTime,
archived,
callTime);
Navigator.pop(context);
}
},
child: Text('Update'),
)
],
),
),
);
}
if (snapshot.hasError) {
return const Text('"Something Went Wrong"');
}
return const Center(
child: CircularProgressIndicator(),
);
},
)),
);
}
}
My app is for the driver, who delivers orders through this application. In this, I am listening to all the orders assigned to the driver from the firebase realtime database. The onChildAdded.listen adds the order to a list named ongoingList of class Ongoing which means assigned to him. I am then displaying the length of the list as count in the dashboard. Now sometimes I am getting unexpected counts in the dashboard and also the list contains duplicate entries.
I am adding the code below
class Dashboard extends StatefulWidget {
const Dashboard({Key? key}) : super(key: key);
#override
_DashboardState createState() => _DashboardState();
}
class _DashboardState extends State<Dashboard>
with SingleTickerProviderStateMixin {
bool isLoading = false;
bool isOnline = true;
var OngoingDetails;
List<Ongoing> ongoingList = [];
List<OpenRequest> openRequestList = [];
DatabaseReference databaseReference = FirebaseDatabase.instance.reference();
late StreamSubscription<Event> ongoingStream;
late StreamSubscription<Event> openRequestStream;
late StreamSubscription<Event> removeOpenNodes;
late LocationData currentLoc;
double currentLat = 0;
double currentLng = 0;
DriverDetail _driverData = DriverDetail();
// late Timer timer;
#override
void initState() {
super.initState();
// timer =
// Timer.periodic(Duration(seconds: 300), (Timer t) => reloadPageAuto());
getDriverData();
countOpenRequest = 0;
ongoingCount = 0;
}
reloadPageAuto() {
// Navigator.pop(context); // pop current page
Navigator.pushReplacementNamed(context, '/dashboard'); // push it back in
}
getDriverData() async {
setState(() {
isLoading = true;
});
_driverData = await getDriverDetails();
print("${_driverData.driverName}");
setState(() {
isLoading = false;
});
//getCurrentLocation();
}
getCurrentLocation() async {
currentLoc = await getLoc();
setState(() {
currentLat = currentLoc.latitude!;
currentLng = currentLoc.longitude!;
isLoading = false;
});
// checkOpenRequest();
}
#override
void dispose() {
// timer.cancel();
super.dispose();
}
#override
Widget build(BuildContext context) {
DateTime timeBackPressed = DateTime.now();
return isLoading == false
? DefaultTabController(
child: WillPopScope(
onWillPop: () async {
final difference = DateTime.now().difference(timeBackPressed);
timeBackPressed = DateTime.now();
if (difference >= Duration(seconds: 1)) {
final String msg = 'Press the back button again to exit';
showToast(msg);
return false;
} else {
Fluttertoast.cancel();
SystemNavigator.pop();
return true;
}
},
child: Scaffold(
appBar: AppBar(
leading: Switch(
materialTapTargetSize: MaterialTapTargetSize.padded,
activeTrackColor: Colors.grey,
value: isOnline,
activeColor: kWagonColour,
onChanged: (value) async {
setState(() {
isOnline = value;
//print(isOnline);
Fluttertoast.showToast(
msg: isOnline == true
? "You are now Online"
: "You are now Offline",
gravity: ToastGravity.BOTTOM,
textColor: Colors.white,
backgroundColor: kWagonColour,
);
});
if (isOnline == true) {
await OneSignal.shared.disablePush(false);
OneSignal.shared
.setAppId("abd31b6b-5fc4-4769-8684-224a439e422e");
final status = await OneSignal.shared.getDeviceState();
var playerId = status!.userId;
OSCreateNotification notification =
OSCreateNotification(
androidChannelId:
"1ff1eb80-0316-419f-bd37-0992145ee37d",
heading: "Go Wagon Driver",
playerIds: [playerId.toString()],
content: "Background Service Active",
androidSound: "onesignal_default_sound.wav",
iosSound: "onesignal_default_sound.wav",
);
OneSignal.shared.postNotification(notification);
SystemChrome.setSystemUIOverlayStyle(
SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
));
} else {
//print('else');
await OneSignal.shared.disablePush(true);
}
},
),
actions: [
Padding(
padding: const EdgeInsets.only(right: 15.0),
child: GestureDetector(
child: CircleAvatar(
backgroundImage: NetworkImage(_driverData.image),
),
onTap: () {
Navigator.pushNamed(context, '/profile');
},
),
),
],
centerTitle: true,
title: Image.asset(
'images/app_icon.png',
height: 50.0,
width: 50.0,
),
bottom: TabBar(
tabs: [
Tab(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.home,
color: kWagonColour,
),
Padding(
padding: const EdgeInsets.only(left: 5),
child: Text('Home'),
),
],
),
),
Tab(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.price_change_outlined,
color: kWagonColour,
),
Padding(
padding: const EdgeInsets.only(left: 5),
child: Text('Earnings'),
),
],
),
),
],
labelColor: kWagonColour,
indicator: UnderlineTabIndicator(
borderSide: BorderSide(width: 2.0, color: kWagonColour),
insets: EdgeInsets.symmetric(horizontal: 25.0),
),
indicatorColor: kWagonColour,
unselectedLabelColor: Colors.grey,
indicatorSize: TabBarIndicatorSize.tab,
labelStyle: TextStyle(
fontSize: 16.0,
),
),
),
backgroundColor: Color(0xFFF5F5F5),
body: isLoading
? ColorLoader()
: GestureDetector(
child: TabBarView(
children: [
Status(
details: ongoingList,
),
Earnings(),
],
),
),
floatingActionButtonLocation:
FloatingActionButtonLocation.startFloat,
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => QRCodeScanner(),
));
},
child: Icon(
Icons.qr_code_scanner,
color: kWagonColour,
),
backgroundColor: Colors.white,
),
),
),
length: 2,
initialIndex: 0,
)
: Scaffold(
body: ColorLoader(),
);
}
}
class Status extends StatefulWidget {
const Status({Key? key, required this.details}) : super(key: key);
final List details;
#override
_StatusState createState() => _StatusState();
}
class _StatusState extends State<Status>
with AutomaticKeepAliveClientMixin<Status> {
#override
bool get wantKeepAlive => true;
var top = 10.0;
var left = 10.0;
DriverDetail _driverData = DriverDetail();
bool isLoading = false;
bool isLoadingCount = true;
List<Ongoing> ongoingList = [];
List<OpenRequest> openRequestList = [];
DatabaseReference databaseReference = FirebaseDatabase.instance.reference();
late StreamSubscription<Event> ongoingStream;
late StreamSubscription<Event> openRequestStream;
late StreamSubscription<Event> removeOpenNodes;
late LocationData currentLoc;
double currentLat = 0;
double currentLng = 0;
bool _enabled = true;
#override
void initState() {
super.initState();
getDriverData();
}
getCurrentLocation() async {
currentLoc = await getLoc();
setState(() {
currentLat = currentLoc.latitude!;
currentLng = currentLoc.longitude!;
});
setState(() {
isLoading = false;
});
checkOpenRequest();
}
checkOpenRequest() async {
print("checking open request");
if (countOpenRequest == 0) {
setState(() {
isOpenRequest = false;
});
}
print("open count $countOpenRequest");
countOpenRequest = 0;
openRequestStream = FirebaseDatabase.instance
.reference()
.child('shipments')
.child('open_requests')
.orderByChild('model_id')
.equalTo(double.parse(_driverData.modelId))
.onChildAdded.listen(_onEntryAddedOpen);
// .listen((event) {
// double total_distance = distance(
// currentLat,
// currentLng,
// double.parse(event.snapshot.value['pickup_latitude']),
// double.parse(event.snapshot.value['pickup_longitude']),
// 'K');
// print("distance for ${event.snapshot.key} $total_distance");
//
// if (total_distance <= 50) {
// HapticFeedback.heavyImpact();
// setState(() {
// // print("New request received: ${event.snapshot.value}");
// ++countOpenRequest;
// isOpenRequest = true;
// });
// }
// });
checkOngoing();
}
checkOngoing() async {
print("checking ongoing");
ongoingStream = FirebaseDatabase.instance
.reference()
.child('shipments')
.child('driver_shipments')
.child(_driverData.vehicleId)
.onChildAdded
.listen(_onEntryAddedOngoing);
setState(() {
isLoadingCount = false;
});
}
getDriverData() async {
setState(() {
isLoading = true;
});
_driverData = await getDriverDetails();
getCurrentLocation();
}
#override
void dispose() {
// timer.cancel();
openRequestStream.cancel();
ongoingStream.cancel();
super.dispose();
}
#override
Widget build(BuildContext context) {
super.build(context);
var deviceWidth = MediaQuery.of(context).size.width;
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 15.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
height: 100.0,
padding: EdgeInsets.symmetric(vertical: 8.0),
width: double.infinity,
child: Card(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AutoSizeText(
'Hi, ${_driverData.driverName}',
minFontSize: 16,
maxFontSize: 20,
overflow: TextOverflow.visible,
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
SizedBox(
height: 10.0,
),
Text(
'What do you want to do today?',
style: TextStyle(
fontSize: 16.0,
),
),
],
),
SizedBox(
width: 50.0,
),
GestureDetector(
child: Icon(
Icons.replay_circle_filled,
color: kWagonColour,
size: 40.0,
semanticLabel: 'Refresh',
),
onTap: _enabled ? refreshCounts : null,
),
],
),
),
),
Container(
height: 100,
width: MediaQuery.of(context).size.width,
child: Stack(
children: [
Positioned(
child: GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
OngoingShipments(ongoingDetails: ongoingList),
),
);
},
child: Row(
children: [
Container(
width: deviceWidth / 7,
height: 40,
child: Center(
child: isLoadingCount == false
? Text(
ongoingList.length.toString(),
style: TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.bold,
),
)
: CircularProgressIndicator(
color: Colors.white,
),
),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: kWagonColour,
),
),
SizedBox(
width: 5,
),
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => OngoingShipments(
ongoingDetails: ongoingList),
),
);
},
child: Text(
'ONGOING',
style: TextStyle(
color: kWagonColour,
),
),
style: ElevatedButton.styleFrom(
primary: Colors.white,
),
)
],
),
),
),
Positioned(
top: 20,
left: deviceWidth / 1.8,
child: GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => TestFirebase()));
},
child: Row(
children: [
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => TestFirebase()));
},
child: Text(
'OPEN',
style: TextStyle(
color: kWagonColour,
),
),
style: ElevatedButton.styleFrom(
primary: Colors.white,
),
),
SizedBox(
width: 5,
),
Container(
width: 60,
height: 40,
child: Center(
child: isLoadingCount == false
? Text(
openRequestList.length.toString(),
style: TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.bold,
),
)
: CircularProgressIndicator(
color: Colors.white,
),
),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: kWagonColour,
),
),
],
),
),
),
],
),
),
],
),
);
}
refreshCounts() {
setState(() {
isLoadingCount = true;
_enabled = false;
});
isLoadingCount = true;
ongoingList = [];
openRequestList = [];
ongoingCount = 0;
countOpenRequest = 0;
Timer(Duration(seconds: 1), () {
setState(() {
_enabled = true;
});
checkOpenRequest();
});
}
_onEntryAddedOngoing(Event event) async {
setState(() {
ongoingList.add(Ongoing.fromSnapshot(event.snapshot));
});
}
_onEntryAddedOpen(Event event) async {
double totalDistance = distance(
currentLat,
currentLng,
double.parse(event.snapshot.value['pickup_latitude']),
double.parse(event.snapshot.value['pickup_longitude']),
'K');
print("distance for ${event.snapshot.key} $totalDistance");
if (totalDistance <= 50) {
HapticFeedback.heavyImpact();
setState(() {
openRequestList.add(OpenRequest.fromFirebase(event.snapshot));
});
}
}
}
Below is the code for my class Ongoing
Ongoing(
{this.baseFare,
this.cancelationFee,
this.customerId,
this.customerName,
this.distanceTotal,
this.driverId,
this.driverImage,
this.driverName,
this.driverNumber,
this.driverNumberCode,
this.dropAddress,
this.dropLatitude,
this.dropLongitude,
this.estimatedTime,
this.helpCount,
this.helpTotal,
this.isPayable,
this.itemCount,
this.masterFlag,
this.minHr,
this.minKm,
this.modelId,
this.payableFlag,
this.paymentMethod,
this.paymentStatus,
this.perHr,
this.perKm,
this.pickPayPaymentStatus,
this.pickupAddress,
this.pickupCode,
this.pickupLatitude,
this.pickupLongitude,
this.pickupTime,
this.price,
this.pricePerHr,
this.pricePerKm,
this.receiverName,
this.receiverPhone,
this.receiverPhoneCode,
this.senderName,
this.senderPhone,
this.senderPhoneCode,
this.shipmentId,
this.shipmentLabel,
this.shipmentLabelAr,
this.shipmentOtp,
this.shipmentPackageInstruction,
this.shipmentPackageName,
this.shipmentPackagePropertyId,
this.shipmentPackageTypeId,
this.shipmentPolyline,
this.status,
this.total,
this.type,
this.vehicleId,
this.vehicleName,
this.wagoncut,
this.isCashShipment,
this.invoiceNumber,
this.invoiceDate,
this.invoiceAmount,
this.operationMobile, this.packageName});
Ongoing.fromSnapshot(DataSnapshot snapshot) {
baseFare = snapshot.value['base_fare'];
cancelationFee = snapshot.value['cancelation_fee'];
customerId = snapshot.value['customer_id'];
customerName = snapshot.value['customer_name'];
distanceTotal = snapshot.value['distance_total'];
driverId = int.parse(snapshot.value['driver_id'].toString());
driverImage = snapshot.value['driver_image'];
driverName = snapshot.value['driver_name'];
driverNumber = snapshot.value['driver_number'];
driverNumberCode = snapshot.value['driver_number_code'];
dropAddress = snapshot.value['drop_address'];
dropLatitude = snapshot.value['drop_latitude'];
dropLongitude = snapshot.value['drop_longitude'];
estimatedTime = snapshot.value['estimated_time'];
helpCount = snapshot.value['help_count'];
helpTotal = snapshot.value['help_total'];
isPayable = snapshot.value['is_payable'];
itemCount = snapshot.value['item_count'];
masterFlag = snapshot.value['master_flag'];
minHr = snapshot.value['min_hr'];
minKm = snapshot.value['min_km'];
modelId = snapshot.value['model_id'];
payableFlag = snapshot.value['payable_flag'];
paymentMethod = snapshot.value['payment_method'];
paymentStatus = snapshot.value['payment_status'];
perHr = snapshot.value['per_hr'];
perKm = snapshot.value['per_km'];
pickPayPaymentStatus = snapshot.value['pick_pay_payment_status'];
pickupAddress = snapshot.value['pickup_address'];
pickupCode = snapshot.value['pickup_code'];
pickupLatitude = snapshot.value['pickup_latitude'];
pickupLongitude = snapshot.value['pickup_longitude'];
pickupTime = snapshot.value['pickup_time'];
price = snapshot.value['price'];
pricePerHr = snapshot.value['price_per_hr'];
pricePerKm = snapshot.value['price_per_km'];
receiverName = snapshot.value['receiver_name'];
receiverPhone = snapshot.value['receiver_phone'];
receiverPhoneCode = snapshot.value['receiver_phone_code'].toString();
senderName = snapshot.value['sender_name'];
senderPhone = snapshot.value['sender_phone'];
senderPhoneCode = snapshot.value['sender_phone_code'].toString();
shipmentId = snapshot.value['shipment_id'];
shipmentLabel = snapshot.value['shipment_label'];
shipmentLabelAr = snapshot.value['shipment_label_ar'];
shipmentOtp = snapshot.value['shipment_otp'];
shipmentPackageInstruction = snapshot.value['shipment_package_instruction'];
shipmentPackageName = snapshot.value['shipment_package_name'];
shipmentPackagePropertyId = snapshot.value['shipment_package_property_id'];
shipmentPackageTypeId = snapshot.value['shipment_package_type_id'];
shipmentPolyline = snapshot.value['shipment_polyline'];
status = snapshot.value['status'];
total = snapshot.value['total'];
type = snapshot.value['type'];
vehicleId = snapshot.value['vehicle_id'];
vehicleName = snapshot.value['vehicle_name'];
wagoncut = snapshot.value['wagoncut'];
isCashShipment = snapshot.value['is_cash_shipment'].toString();
invoiceAmount = snapshot.value['invoice_amount'].toString();
invoiceNumber = snapshot.value['invoice_no'].toString();
invoiceDate = snapshot.value['invoice_date'].toString();
operationMobile = snapshot.value['operation_mobile'].toString();
}
}
If anyone has relevant suggestion regarding it, please help me through it. Thank you in advance
I have used flutter quill rich text editor in one of my project where user can create diary from flutter quill toolbar options and also can upload pictures also. So,
Q1) when user upload picture I immediately upload that picture to firesbase storage in _onImagePickCallback() method but if user hit backspace then that picture will be removed from document but it still there in firebase storage. So, how to tackle that backspace or any remove method listener inside flutter quill. (One Approach I tried is don't upload picture immediately but only at the end when user create document and want to create diary, but here I faced problem how to change all those local images to online through uploading to firebase storage.)
Q2) I have used _customEmbedBuilder() method, to draw custom design container for images in flutter quill but I also put a Cross icon on the top of picture so when user tap on that, that picture will be removed from the document regardless where the cursor is, and I don't know how to do this.
Here is the picture and cross icon I did
This is create diary screen code where I am doing all this.
class CreateDiaryScreen extends StatefulWidget {
const CreateDiaryScreen({Key? key}) : super(key: key);
#override
State<CreateDiaryScreen> createState() => _CreateDiaryScreenState();
}
class _CreateDiaryScreenState extends State<CreateDiaryScreen> {
bool isDaySelected = false;
QuillController quillController = QuillController.basic();
String imageUrl = '';
int imagesCount = 0;
final FocusNode _focusNode = FocusNode();
TextEditingController titleController = TextEditingController();
final ScrollController _scrollController = ScrollController();
final _formKey = GlobalKey<FormState>();
DateTime _focusedDay = DateTime.now();
DateTime? _selectedDay;
late CalendarFormatChangedCubit calendarFormatChangedCubit;
Future<String> _onImagePickCallback(File file) async {
String url =
await context.read<UploadImageCubit>().uploadDiaryImageToFirebase(file);
//print('image callback url :$url');
//
// final appDocDir = await getApplicationDocumentsDirectory();
// final copiedFile =
// await file.copy('${appDocDir.path}/${Path.basename(file.path)}');
// return copiedFile.path.toString();
return url;
}
createDiary() {
Document document = quillController.document;
var json = jsonEncode(quillController.document.toDelta().toJson());
print(
'Plain text: ${quillController.document.toPlainText().replaceAll(RegExp(r'[ \n]'), '
').trim()}');
if (_formKey.currentState!.validate()) {
///second check whether user enter some data or not in quill editor.
if (quillController.document.toPlainText().length > 1) {
if (isDaySelected) {
BlocProvider.of<CreateDiaryCubit>(context).uploadDiary(
diaryTitle: titleController.text,
diaryText: quillController.document
.toPlainText()
.replaceAll(RegExp(r'[]'), ' ')
/// to remove  and \n from string.
.trim(),
diaryImage: imageUrl,
diaryFullDocument: quillController.document,
diarySelectedDate: _selectedDay ?? DateTime.now(),
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Please select date'),
),
);
}
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Please enter description'),
),
);
}
}
}
Widget _customEmbedBuilder(BuildContext context, Embed embed, bool boooll) {
switch (embed.value.type) {
case BlockEmbed.imageType:
return _buildImage(context, embed);
case BlockEmbed.horizontalRuleType:
return const Divider();
default:
throw UnimplementedError(
'Embed "${embed.value.type}" is not supported.');
}
}
Widget _buildImage(BuildContext context, Embed embed) {
final imageUrl = embed.value.data;
return Container(
height: 160.sp,
width: 1.sw,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.sp),
),
child: imageUrl.startsWith('http')
? ClipRRect(
borderRadius: BorderRadius.circular(12.sp),
child: CachedNetworkImage(
imageUrl: imageUrl,
imageBuilder: (context, imageProvider) => Container(
decoration: BoxDecoration(
image: DecorationImage(
image: imageProvider,
fit: BoxFit.cover,
),
),
),
placeholder: (context, url) => Center(
child: SizedBox(
height: 50.sp,
width: 50.sp,
child: const CircularProgressIndicator(
color: AppColors.primaryColor,
),
),
),
errorWidget: (context, url, error) => const Icon(Icons.error),
),
)
: imageUrl.toString().isEmpty
? const SizedBox()
: isBase64(imageUrl)
? ClipRRect(
borderRadius: BorderRadius.circular(12.sp),
child: Image.memory(
base64.decode(imageUrl),
fit: BoxFit.cover,
),
)
: Stack(
children: [
SizedBox(
width: 1.sw,
child: ClipRRect(
borderRadius: BorderRadius.circular(12.sp),
child: Image.file(
File(imageUrl),
fit: BoxFit.cover,
),
),
),
Positioned(
right: 10.sp,
top: 5.sp,
child: Container(
width: 21.58.sp,
height: 21.58.sp,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.black.withOpacity(0.1),
),
child: Center(
child: InkWell(
onTap: () {
print('tapped');
},
child: Icon(
Icons.close,
size: 16.sp,
)),
),
),
),
],
),
);
}
#override
void initState() {
print('init called');
// TODO: implement initState
super.initState();
calendarFormatChangedCubit =
BlocProvider.of<CalendarFormatChangedCubit>(context);
_focusNode.addListener(() {
if (_focusNode.hasFocus) {
///when focus then change calendar format to week
calendarFormatChangedCubit.onCalendarFormatChanged(CalendarFormat.week);
}
});
}
#override
void dispose() {
super.dispose();
calendarFormatChangedCubit.resetCalendarStates();
}
showDiaryPostDiscardDialog(BuildContext context) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Discard Diary?'),
content: const Text('If you go back, you will lose your diary post.'),
actions: <Widget>[
TextButton(
child: const Text(
'Yes',
style: TextStyle(color: AppColors.primaryColor),
),
onPressed: () {
Navigator.pop(context);
Navigator.pop(context);
}),
TextButton(
child: const Text('No',
style: TextStyle(color: AppColors.primaryColor)),
onPressed: () => Navigator.of(context).pop(false)),
]),
);
}
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
if (quillController.document.toPlainText().length > 1 ||
_formKey.currentState!.validate()) {
/// if condition passed, mean that user typed something into textfield and also written
some description
/// and show alert dialog of diary discard alert.
showDiaryPostDiscardDialog(context);
return false;
} else {
Navigator.of(context).pop(true);
return true;
}
},
child: Scaffold(
resizeToAvoidBottomInset: true,
backgroundColor: const Color(0xffFCFEFF),
appBar: CustomAppBar(
appBarTitle: 'Add diary',
diaryAppBar: true,
onTickPressed: createDiary),
body: MultiBlocListener(
listeners: [
BlocListener<CreateDiaryCubit, CreateDiaryState>(
listener: (context, state) {
if (state is DiarySubmitionSuccess) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Diary created successfully'),
duration: Duration(milliseconds: 1500),
),
);
Navigator.pop(context);
titleController.clear();
quillController.clear();
}
if (state is DiarySubmitionNoInternetError) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content:
Text('No internet, please connect your internet'),
duration: Duration(milliseconds: 1500),
),
);
}
if (state is DiarySubmitionFailed) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Could not create diary, please try again'),
duration: Duration(milliseconds: 1500),
),
);
}
if (state is DiarySubmitionError) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('An error occured while uploading diary'),
duration: Duration(milliseconds: 1500),
),
);
}
},
),
BlocListener<CalendarDaySelectedCubit, CalendarDaySelectedState>(
listener: (context, state) {
if (state is CalendarDaySelected) {
isDaySelected = state.isDaySelected;
_focusedDay = state.focusDay;
_selectedDay = state.selectedDay;
}
},
),
BlocListener<UploadImageCubit, UploadImageState>(
listener: (context, state) {
if (state is UploadingImage) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: SizedBox(
height: 25.sp, child: SnackBarUploadingWidget()),
),
);
}
},
),
BlocListener<UploadImageCubit, UploadImageState>(
listener: (context, state) {
if (state is UploadedImage) {
///this is to check as to only first image url upload to firestore document, so
we create counter image check.
imagesCount++;
if (imagesCount == 1) {
imageUrl = state.url;
}
}
if (state is UploadingImageFiled) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Error occur'),
),
);
}
},
),
],
child: Column(
children: [
Expanded(
flex: 20,
child: Column(
children: [
///calendar, title and description
Align(
alignment: Alignment.topCenter,
child: Container(
width: 340.sp,
// height: 360 + 15.sp,
margin: EdgeInsets.symmetric(
horizontal: 16.sp, vertical: 0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16.0.r),
color: AppColors.backgroundColor,
),
child: Stack(
children: [
DiaryCalendarWidget(),
///calendar pin
Positioned(
top: 12.sp,
left: 1.sw / 2 - 80.sp,
child: const CalendarPins(),
),
///calendar pin
Positioned(
top: 12.sp,
left: 1.sw / 2 + 40.sp,
child: const CalendarPins(),
),
],
),
),
),
///title textField
Container(
margin:
EdgeInsets.symmetric(horizontal: 16.sp, vertical: 0),
child: Form(
key: _formKey,
child: CustomTextField(
validator: (titleText) {
RegExp titleRegExp = RegExp(r"^[a-zA-Z0-9]+//$");
if (titleText!.isEmpty) {
return 'Enter Title.';
} else if (titleText.length < 20) {
return 'Title should be more than 15 characters';
} else {
final title = titleText;
}
return null;
},
controller: titleController,
keyboardType: TextInputType.text,
textHeight: 20.sp,
),
),
),
///text customization section
Expanded(
flex: 10,
child: Container(
padding: EdgeInsets.only(left: 16.sp, right: 16.sp),
child: BlocBuilder<CreateDiaryCubit, CreateDiaryState>(
builder: (context, state) {
if (state is DiarySubmitting) {
return const CircularProgressIndicator(
color: AppColors.primaryColor,
);
} else {
return Scrollbar(
controller: _scrollController,
isAlwaysShown: true,
child: QuillEditor(
scrollController: _scrollController,
paintCursorAboveText: true,
embedBuilder: _customEmbedBuilder,
scrollPhysics:
const ClampingScrollPhysics(),
customStyleBuilder: (dynamic) {
return GoogleFonts.poppins(
fontSize: 14.sp,
color: const Color(0xff969798),
);
},
controller: quillController,
scrollable: true,
focusNode: _focusNode,
autoFocus: false,
readOnly: false,
placeholder:
'Your diary description here...',
expands: false,
padding:
EdgeInsets.symmetric(horizontal: 8.sp),
customStyles: DefaultStyles(
paragraph: DefaultTextBlockStyle(
GoogleFonts.poppins(
fontSize: 14.sp,
color: Colors.black,
height: 1.15,
fontWeight: FontWeight.w300,
),
const Tuple2(16, 0),
const Tuple2(0, 0),
null),
h1: DefaultTextBlockStyle(
GoogleFonts.poppins(
fontSize: 32.sp,
color: Colors.black,
height: 1.15,
fontWeight: FontWeight.w300,
),
const Tuple2(16, 0),
const Tuple2(0, 0),
null),
sizeSmall: const TextStyle(fontSize: 9),
)),
);
}
},
),
),
),
],
),
),
Expanded(
flex: 2,
child: Align(
alignment: Alignment.bottomCenter,
child: Container(
width: 1.sw,
padding:
EdgeInsets.symmetric(horizontal: 4.sp, vertical: 5.sp),
decoration: BoxDecoration(
color: AppColors.white,
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.08),
offset: Offset(0, 3.0),
blurRadius: 16.0,
),
],
),
child: QuillToolbar.basic(
multiRowsDisplay: false,
toolbarIconSize: 24.sp,
controller: quillController,
onImagePickCallback: _onImagePickCallback,
iconTheme: const QuillIconTheme(
iconUnselectedFillColor: Colors.white,
iconUnselectedColor: AppColors.grey,
iconSelectedFillColor: AppColors.primaryColor),
showVideoButton: false,
mediaPickSettingSelector: _selectMediaPickSetting,
showAlignmentButtons: true,
showHistory: false,
showItalicButton: false,
showStrikeThrough: false,
showCodeBlock: false,
showInlineCode: false,
showIndent: false,
showUnderLineButton: false,
showCameraButton: true,
showJustifyAlignment: false,
showListNumbers: false,
showDividers: false,
showClearFormat: false,
showHeaderStyle: false,
showLeftAlignment: false,
showCenterAlignment: false,
showRightAlignment: false,
showQuote: true,
showLink: false,
showListCheck: false,
showSmallButton: true,
),
),
),
),
],
),
),
),
);
}
Future<MediaPickSetting?> _selectMediaPickSetting(BuildContext context) =>
showDialog<MediaPickSetting>(
context: context,
builder: (ctx) => AlertDialog(
contentPadding: EdgeInsets.zero,
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
TextButton.icon(
icon: Icon(
DiaryIcons.image_gallery,
color: AppColors.primaryColor,
size: 18.sp,
),
label: Text(
'Gallery',
style: GoogleFonts.poppins(color: AppColors.primaryColor),
),
onPressed: () => Navigator.pop(ctx, MediaPickSetting.Gallery),
),
TextButton.icon(
icon: const Icon(Icons.link, color: AppColors.primaryColor),
label: Text(
' Link ',
style: GoogleFonts.poppins(color: AppColors.primaryColor),
),
onPressed: () => Navigator.pop(ctx, MediaPickSetting.Link),
)
],
),
),
);
}
I am not sure if you still need an answer to this but I found a solution to your Problem.
I went for the Q2 you had and upload all the images once it is saved.
This means we need a way to change the controller values.
They way to do is is to go over the operation that are done so far and find the position of the image you want to change.
I have to Maps one containing all the local Urls and one with the Urls i get from the bucket. The following function deals with the entire problem:
void replacesLinkFromController(QuillController controller, String imageName) {
var json = jsonEncode(controller.document.toDelta());
List<dynamic> delta = jsonDecode(json);
// Loop over the delta list and search for insert
for (Map<String, dynamic> line in delta) {
// Each line represents a Map -> line to insert
for (var key in line.keys) {
var value = line[key];
// Check if the key is insert
if (key == "insert") {
// Check if the value is a map
if (value is Map<String, dynamic>) {
// If it is a map then check if there is a image -> link pair
for (var newKey in value.keys) {
if (newKey == "image") {
// value == local storage
// Replaces the value
// Get the new value from the url map
String storageUrl = getUrlFromImageName(imageName);
String localUrl = getLocalUrlFromImageName(imageName);
if (storageUrl != "") {
value[newKey] = storageUrl;
var delta = controller.document.toDelta();
int position = 0;
int length = 0;
for (var operation in delta.toList()) {
position = position + operation.length!;
if (operation.key == 'insert') {
if (operation.data is Map) {
Map compare = operation.data as Map;
if (compare.containsKey('image')) {
if (compare['image'] == localUrl) {
length = operation.length!;
break;
}
}
}
}
}
Embeddable newImageWithUrl = Embeddable('image', storageUrl);
controller.document
.replace(position - 1, length, newImageWithUrl);
}
}
}
}
}
}
}
I hope you can take the part how I replace the images in the controller.
When user registration, authentication occurred but not added data to the firebase that asking while signup such as registration number, Name, and TP no, etc. in flutter. So, what's wrong with my code?
Below one is the code for signup_view.dart file
import 'package:auto_size_text/auto_size_text.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:uocportal/provider_widget.dart';
import 'package:uocportal/auth_service.dart';
import 'package:shared_preferences/shared_preferences.dart';
//import 'package:auto_size_text/auto_size_text.dart';
enum AuthFormType { signIn, signUp, reset }
class SignUpView extends StatefulWidget {
final AuthFormType authFormType;
SignUpView({Key key, #required this.authFormType}) : super(key: key);
#override
_SignUpViewState createState() =>
_SignUpViewState(authFormType: this.authFormType);
}
class _SignUpViewState extends State<SignUpView> {
AuthFormType authFormType;
_SignUpViewState({this.authFormType});
var reNumFieldController = new TextEditingController();
final formKey = GlobalKey<FormState>();
String
_email,
_password,
_name,
_regnumber,
_faculty,
_contact,
_warning;
final _userCollectionReference = Firestore.instance;
// prefs.setString('stringRegnumValue', _regnumber);
Future createUser() async {
try {
final prefs = await SharedPreferences.getInstance();
prefs.setString('my_regno_key', _regnumber.toString());
await _userCollectionReference
.collection('Users')
.document()
.setData({
'username': _name,
'email': _email,
'contact': _contact,
'faculty': _faculty,
'regnumber': _regnumber
});
} catch (e) {
return e.message;
}
}
void switchFormState(String state) {
formKey.currentState.reset();
if (state == "signUp") {
setState(() {
authFormType = AuthFormType.signUp;
});
} else {
setState(() {
authFormType = AuthFormType.signIn;
});
}
}
bool validate() {
final form = formKey.currentState;
form.save();
if (form.validate()) {
form.save();
return true;
} else {
return false;
}
}
void submit() async {
if (validate()) {
try {
final auth = Provider.of(context).auth;
if (authFormType == AuthFormType.signIn) {
String uid = await auth.signinWithEmailAndPassword(_email, _password);
print("$uid");
Navigator.of(context).pushReplacementNamed('/home');
} else if (authFormType == AuthFormType.reset) {
await auth.sendPasswordResetEmail(_email);
print("password reset mail sent");
_warning = "A Password reset link has been sent to $_email";
setState(() {
authFormType = AuthFormType.signIn;
});
} else {
String uid = await auth.createUserWithEmailAndPassword(
_email, _password, _name, _regnumber, _faculty, _contact);
print("$uid");
Navigator.of(context).pushReplacementNamed('/home');
createUser();
}
} catch (e) {
print(e);
// setState(() {
_warning = e.message;
// });
}
}
}
#override
Widget build(BuildContext context) {
final _height = MediaQuery.of(context).size.height;
final _width = MediaQuery.of(context).size.width;
return Scaffold(
appBar: AppBar(
title: Text('UOC Portal'),
backgroundColor: Color(0xFF4A184C),
),
body: new GestureDetector(
onTap: () {
// call this method here to hide soft keyboard
FocusScope.of(context).requestFocus(new FocusNode());
},
child: Container(
color: Colors.white,
height: _height,
width: _width,
child: SingleChildScrollView(
child: Container(
child: Column(
children: <Widget>[
showAlert(),
SizedBox(
height: _height * 0.05,
),
buildHeaderText(),
buildSubHeadText(),
SizedBox(
height: _height * 0.02,
),
Padding(
padding: const EdgeInsets.all(10.0),
child: Form(
key: formKey,
child: Column(
children: buildInputs() + buildButtons())),
)
],
),
),
),
)));
}
Widget showAlert() {
if (_warning != null) {
return Container(
color: Colors.amber,
width: double.infinity,
padding: EdgeInsets.all(8.0),
child: Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Icon(Icons.error_outline),
),
Expanded(
child: AutoSizeText(
_warning,
maxLines: 3,
)),
IconButton(
icon: Icon(Icons.close),
onPressed: () {
setState(() {
_warning = null;
});
})
],
),
);
}
return SizedBox(
height: 0,
);
}
AutoSizeText buildHeaderText() {
String _headerText;
if (authFormType == AuthFormType.signIn) {
_headerText = "Welcome !!";
} else if (authFormType == AuthFormType.reset) {
_headerText = "Reset Password";
} else {
_headerText = "Sign Up";
}
return AutoSizeText(
_headerText,
maxLines: 2,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 35, color: Color(0xFF4A184C), fontWeight: FontWeight.bold),
);
}
AutoSizeText buildSubHeadText() {
String _headerText;
if (authFormType == AuthFormType.signIn) {
_headerText = "Sign in to Continue";
} else if (authFormType == AuthFormType.reset) {
_headerText = "";
} else {
_headerText = "Create a new Account";
}
return AutoSizeText(
_headerText,
maxLines: 2,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 15, color: Color(0xFF4A184C)),
);
}
List<Widget> buildInputs() {
List<Widget> textFields = [];
if (authFormType == AuthFormType.reset) {
textFields.add(
TextFormField(
keyboardType: TextInputType.text,
validator: EmailValidator.validate,
style: TextStyle(fontSize: 18.0, color: Color(0xFF4A184C)),
decoration: buildSignUpInputDecoration("Email"),
onSaved: (value) => _email = value,
),
);
textFields.add(SizedBox(
height: 8,
));
return textFields;
}
//if were in the signup state add name
if (authFormType == AuthFormType.signUp) {
textFields.add(
TextFormField(
keyboardType: TextInputType.text,
validator: NameValidator.validate,
style: TextStyle(fontSize: 18.0, color: Color(0xFF4A184C)),
decoration: buildSignUpInputDecoration("Name"),
onSaved: (value) => _name = value,
),
);
textFields.add(SizedBox(
height: 8,
));
textFields.add(
DropdownButtonFormField<String>(
validator:(value) {
if (value == null) {
return "Faculty can't be empty";
}
},
value: _faculty,
items: ["Science",
"Management",
"Law",
"Art",
"UCSC",
"Medicine",
"Technology",
"Nursing",
"IIM"
].map((label) => DropdownMenuItem(
child: Text(label),
value: label,
))
.toList(),
onChanged: (value) {
setState(() => _faculty = value);
},
style: TextStyle(fontSize: 18.0, color: Color(0xFF4A184C)),
decoration: buildSignUpInputDecoration("Faculty"),
),
);
textFields.add(SizedBox(
height: 8,
));
textFields.add(
TextFormField(
validator: RegNoValidator.validate,
style: TextStyle(fontSize: 18.0, color: Color(0xFF4A184C)),
decoration: buildSignUpInputDecoration("Registration No."),
controller: reNumFieldController,
onSaved: (value) => _regnumber = value,
),
);
textFields.add(SizedBox(
height: 8,
));
textFields.add(
TextFormField(
keyboardType: TextInputType.phone,
validator: ContactValidator.validate,
style: TextStyle(fontSize: 18.0, color: Color(0xFF4A184C)),
decoration: buildSignUpInputDecoration("Contact Number"),
onSaved: (value) => _contact = value,
),
);
textFields.add(SizedBox(
height: 8,
));
}
//add email and Password
textFields.add(
TextFormField(
keyboardType: TextInputType.emailAddress,
validator: EmailValidator.validate,
style: TextStyle(fontSize: 18.0, color: Color(0xFF4A184C)),
decoration: buildSignUpInputDecoration("Email"),
onSaved: (value) => _email = value,
),
);
textFields.add(SizedBox(
height: 8,
));
textFields.add(
TextFormField(
validator: PasswordValidator.validate,
style: TextStyle(fontSize: 18.0, color: Color(0xFF4A184C)),
decoration: buildSignUpInputDecoration("Password"),
obscureText: true,
onSaved: (value) => _password = value,
),
);
return textFields;
}
InputDecoration buildSignUpInputDecoration(String hint) {
return InputDecoration(
//hintText: hint,
fillColor: Colors.white,
filled: true,
focusColor: Colors.amber,
labelText: hint,
errorMaxLines: 1,
hintStyle:
TextStyle(color: Color(0xFF4A184C), fontWeight: FontWeight.bold),
labelStyle:
TextStyle(color: Color(0xFF4A184C), fontWeight: FontWeight.bold),
enabledBorder: OutlineInputBorder(
borderSide: const BorderSide(color: Colors.amber),
borderRadius: new BorderRadius.circular(25.0)),
contentPadding:
const EdgeInsets.only(left: 14.0, bottom: 10.0, top: 10.0),
);
}
List<Widget> buildButtons() {
String _switchButton, _newFormState, _submitButtonText;
bool _showForgotPasowrd = false;
if (authFormType == AuthFormType.signIn) {
_switchButton = "Don't have an account? | Sign Up";
_newFormState = "signUp";
_submitButtonText = "Sign In";
_showForgotPasowrd = true;
} else if (authFormType == AuthFormType.reset) {
_switchButton = "Return to Sign In";
_newFormState = "signIn";
_submitButtonText = "Submit";
_showForgotPasowrd = false;
} else {
_switchButton = "Have an Account? Sign In";
_newFormState = "signIn";
_submitButtonText = "Sign Up";
}
return [
SizedBox(
height: 10,
),
Container(
width: MediaQuery.of(context).size.width * 0.7,
child: RaisedButton(
onPressed: () {
submit();
},
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30.0)),
color: Color(0xFF4A184C),
textColor: Colors.amber,
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Text(
_submitButtonText,
style: TextStyle(fontSize: 20),
),
)),
),
showForgotPassowrd(_showForgotPasowrd),
FlatButton(
onPressed: () {
switchFormState(_newFormState);
},
child: Text(
_switchButton,
style: TextStyle(color: Colors.amber),
)),
];
}
Widget showForgotPassowrd(bool visibale) {
return Visibility(
child: FlatButton(
onPressed: () {
setState(() {
authFormType = AuthFormType.reset;
});
},
child: Text(
"Forgot Password?",
style: TextStyle(color: Colors.amber),
)),
visible: visibale,
);
}
}
Below one is the code for auth_service.dart file
import 'package:firebase_auth/firebase_auth.dart';
class AuthService {
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
Stream<String> get onAuthStateChanged => _firebaseAuth.onAuthStateChanged.map(
(FirebaseUser user )=> user?.uid,
);
//Email & Password Sign Up
Future <String> createUserWithEmailAndPassword(String email, String password,
String name, String regnumber, String faculty, String contact) async {
final currentUser = await _firebaseAuth.createUserWithEmailAndPassword(
email: email, password: password,);
//update the username
var userUpdateInfo = UserUpdateInfo();
userUpdateInfo.displayName = name;
await currentUser.updateProfile(userUpdateInfo);
await currentUser.reload();
return currentUser.uid;
}
//Email & Password Sign In
Future <String> signinWithEmailAndPassword(String email, String password) async{
return (await _firebaseAuth.signInWithEmailAndPassword(email: email, password: password)).uid;
}
//Sign Out
signOut(){
return _firebaseAuth.signOut();
}
//reset password
Future sendPasswordResetEmail(String email) async {
return _firebaseAuth.sendPasswordResetEmail(email: email);
}
}
These are the codes that I've used for creating user registration, sign-in, and reset the password. Here sign-in, sign-up and password reset appear within one page according to its state.
Please give me a better soution for this. :)
You'd need to execute createUser() before navigating to a different screen. It's best to add debugPrint() on the requests to be executed to verify that they're being called as expected.
Then update user data.
CollectionReference _userCollectionReference = FirebaseFirestore.instance
.collection('Users');
var userDocData = new Map<String, dynamic>();
userDocData['username'] = _name;
userDocData['email'] = _email;
userDocData['contact'] = _contact;
userDocData['faculty'] = _faculty;
userDocData['regnumber'] = _regnumber;
await _userCollectionReference.add(userDocData);
Also, make sure that the rules ables users to write to Firestore.