Show downloading progress bar only on one particular card in listview - flutter

I have implemented a listview.builder and i want to download a video file when pressed on the download icon and show the download progress bar only on that particular card.
Can't figure a way to do that.Any help would be great!!
I am getting the following when pressed on first card's download icon:
This is the code:
String downloadMessage = ' ';
bool _isDownloading = false;
Widget checkid(int index){
if(widget.data[index]["unit_id"].toString() == widget.unitid){
return articles(index);
}
}
Container articles(int index){
return Container(
child: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
GestureDetector(
child: Card(
child: Padding(
padding: const EdgeInsets.only(top: 20.0, bottom: 20, left: 13.0, right: 22.0),
child: Row(
children: <Widget>[
Column(
children: <Widget>[
Text(widget.data[index]["video_size"]+" MB"),
IconButton(
icon: Icon(Icons.file_download),
onPressed: () async {
_index = index;
bool exist = await db.checkdataid(widget.data[index]["data_video_id"]);
print(exist);
if(exist == false){
VideoDetails videoDetail = new VideoDetails(data_id: widget.data[index]["data_video_id"]);
await networkUtils.videodetail(url,body:videoDetail.toMap());
}
String videopath = await db.getvideopath();
videourl = baseurl + videopath;
print(videourl);
setState(() {
_isDownloading = !_isDownloading;
});
var dir = await getApplicationDocumentsDirectory();
Dio dio = Dio();
dio.download(
videourl,
'${dir.path}/sample.ehb',
onReceiveProgress: (actualbytes , totalbytes){
var percentage = actualbytes/totalbytes * 100;
setState(() {
downloadMessage = 'Downloading ${percentage.floor()} %';
});
}
);
}
),
Text(downloadMessage ?? '',style: TextStyle(fontSize: 12),)
],
),
Container(
width: 300,
child: Text(widget.data[index]["topic_name"],
style: TextStyle(fontSize: 15),
textAlign: TextAlign.start,),
),
],
),
),
),
onTap: () {},
)
],
),
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.indigo[700],
body: ListView(
children: <Widget>[
Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.fromLTRB(0, 10, 110, 0),
child: Container(
padding: EdgeInsets.fromLTRB(0, 30, 200, 0),
child: IconButton(icon: Icon(Icons.arrow_back),
color: Colors.black,
onPressed: (){
Navigator.pop(context);
},
),
),
),
SizedBox(height: 10,),
Text(" ",
style: TextStyle(color: Colors.white,
fontSize: 32,
fontWeight: FontWeight.bold),
),
Text("Unit " + widget.unitno,
style: TextStyle(color: Colors.white,
fontSize: 32,
fontWeight: FontWeight.bold),
),
SizedBox(height: 10,),
Text('',
style: TextStyle(color: Colors.white,
fontSize: 17),
),
],
),
SizedBox(height: 40,),
Container(
// height: MediaQuery.of(context).size.height - 185,
decoration: BoxDecoration(
color: Colors.white70,
borderRadius: BorderRadius.only(topLeft: Radius.circular(75.0),),
),
child: Padding(
padding: EdgeInsets.fromLTRB(0, 100, 0, 70),
child: ListView.builder(
physics: ScrollPhysics(),
shrinkWrap: true,
itemCount: widget.data.length,
itemBuilder: (BuildContext context,int index){
return Container(
child: checkid(index),
);
}),
),
)
],
),
);
}
}

first you need to refactor the widget that you return inside the articles method to its own Statefull widget which has knowledege of its download or not, like so
class ListItem extends StatefulWidget {
final int index;
dynamic item;
ListItem({
this.index,
this.item,
});
#override
_ListItemState createState() => _ListItemState();
}
class _ListItemState extends State<ListItem> {
bool _isDownloading = false;
String downloadMessage = "";
#override
Widget build(BuildContext context) {
return Container(
child: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
GestureDetector(
child: Card(
child: Padding(
padding: const EdgeInsets.only(
top: 20.0, bottom: 20, left: 13.0, right: 22.0),
child: Row(
children: <Widget>[
Column(
children: <Widget>[
Text(item["video_size"] + " MB"),
IconButton(
icon: Icon(Icons.file_download),
onPressed: () async {
_index = index;
bool exist = await db.checkdataid(
item["data_video_id"]);
print(exist);
if (exist == false) {
VideoDetails videoDetail = new VideoDetails(
data_id: widget.data[index]
["data_video_id"]);
await networkUtils.videodetail(url,
body: videoDetail.toMap());
}
String videopath = await db.getvideopath();
videourl = baseurl + videopath;
print(videourl);
setState(() {
_isDownloading = !_isDownloading;
});
var dir =
await getApplicationDocumentsDirectory();
Dio dio = Dio();
dio.download(videourl, '${dir.path}/sample.ehb',
onReceiveProgress:
(actualbytes, totalbytes) {
var percentage =
actualbytes / totalbytes * 100;
setState(() {
downloadMessage =
'Downloading ${percentage.floor()} %';
});
});
}),
Text(
downloadMessage ?? '',
style: TextStyle(fontSize: 12),
)
],
),
Container(
width: 300,
child: Text(
item["topic_name"],
style: TextStyle(fontSize: 15),
textAlign: TextAlign.start,
),
),
],
),
),
),
onTap: () {},
)
],
),
),
);
}
}
and when you render them inside the parent widget like so
Widget checkid(int index){
if(widget.data[index]["unit_id"].toString() == widget.unitid){
return articles(index);
}
}
Widget articles(index) {
return ListItem(
index: index,
item: widget.data[index]
);
}
}
now, each item has its own downloading state and will not interfere with the others

Related

only when use iphone simulator

I have this error:
The following FileSystemException was thrown resolving an image codec:
Cannot open file, path = '/Users/todo/Library/Developer/CoreSimulator/Devices/82205CEC-3D83-4A29-BF17-01C5B0515F71/data/Containers/Data/Application/035B9913-BEC5-46BA-84A5-8C1FE3C4E671/tmp/image_picker_B8D488A3-2790-4D53-A5D8-52E57E2C4108-76094-000003172DF085D2.jpg' (OS Error: No such file or directory, errno = 2)
When the exception was thrown, this was the stack
only when use iphone simulator while android emulator no problem
import 'dart:convert';
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../app/utility.dart';
import '../db/aql_db.dart';
import '../globals.dart';
import '../menu_page.dart';
import '../model/aql_model.dart';
import '../widget/input_text.dart';
import 'dart:io';
import 'package:http/http.dart' as http;
var _current = resultsFld[0];
class AqlPg extends StatefulWidget {
const AqlPg({Key? key}) : super(key: key);
#override
State<AqlPg> createState() => _AqlPgState();
}
class _AqlPgState extends State<AqlPg> {
final List<TextEditingController> _criticalController = [];
final List<TextEditingController> _majorController = [];
final List<TextEditingController> _minorController = [];
final List<TextEditingController> _imgCommintControllers = [];
final _irController = TextEditingController();
bool clickedCentreFAB =
false; //boolean used to handle container animation which expands from the FAB
int selectedIndex =
0; //to handle which item is currently selected in the bottom app bar
//call this method on click of each bottom app bar item to update the screen
void updateTabSelection(int index, String buttonText) {
setState(() {
selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
//
_irController.text = aqltbl.ir ?? '';
String _current = aqltbl.result ?? resultsFld[0];
//
return Scaffold(
body: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(
children: [
//---- stack for FloatingActionButton
Stack(
children: <Widget>[
//this is the code for the widget container that comes from behind the floating action button (FAB)
Align(
alignment: FractionalOffset.bottomCenter,
child: AnimatedContainer(
child: const Text(
'Hello',
style: TextStyle(fontSize: 18, color: whiteColor),
),
duration: const Duration(milliseconds: 250),
//if clickedCentreFAB == true, the first parameter is used. If it's false, the second.
height: clickedCentreFAB
? MediaQuery.of(context).size.height
: 10.0,
width: clickedCentreFAB
? MediaQuery.of(context).size.height
: 10.0,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(
clickedCentreFAB ? 0.0 : 300.0),
color: Colors.blue),
),
),
],
),
// --- Top Page Title
const SizedBox(
height: 50,
),
const Center(
child: Text(
'Aql page',
style: TextStyle(
fontSize: 30,
fontWeight: FontWeight.bold,
color: medBlueColor),
),
),
Text(
'Shipment no:$shipmentId',
style: const TextStyle(color: medBlueColor),
),
//--- input container
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: [
Container(
height: 270,
width: 300,
margin: const EdgeInsets.only(top: 5),
child: ListView.builder(
itemCount: (aqltbl.aql ?? []).length,
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {
//here
var _aqlList = aqltbl.aql![index];
//
if (aqltbl.aql!.length > _criticalController.length) {
_criticalController.add(TextEditingController());
_majorController.add(TextEditingController());
_minorController.add(TextEditingController());
}
//
_criticalController[index].text =
_aqlList.critical ?? '';
_majorController[index].text = _aqlList.major ?? '';
_minorController[index].text = _aqlList.minor ?? '';
//
return Column(
children: [
Container(
height: 260,
width: 200,
margin: const EdgeInsets.only(left: 10),
padding: const EdgeInsets.all(10),
// ignore: prefer_const_constructors
decoration: BoxDecoration(
color: lightBlue,
borderRadius: BorderRadius.circular(10),
),
child: Column(
children: [
Text(
(_aqlList.name ?? '').toString(),
style: const TextStyle(
color: medBlueColor,
fontSize: 13,
),
),
// ignore: prefer_const_constructors
MyInputField(
title: 'critical',
hint: 'write critical ',
borderColor: borderColor,
textColor: textColor,
hintColor: hintColor,
controller: _criticalController[index],
onSubmit: (value) {
setState(() {
aqltbl.aql![index].critical = value;
_save();
});
},
),
MyInputField(
title: 'majority',
hint: 'write majority ',
borderColor: borderColor,
textColor: textColor,
hintColor: hintColor,
controller: _majorController[index],
onSubmit: (value) {
setState(() {
aqltbl.aql![index].major = value;
_save();
});
},
),
MyInputField(
title: 'minority',
hint: 'write minority ',
borderColor: borderColor,
textColor: textColor,
hintColor: hintColor,
controller: _minorController[index],
onSubmit: (value) {
setState(() {
aqltbl.aql![index].minor = value;
_save();
});
},
),
],
),
),
],
);
}),
),
Container(
height: 270,
width: 300,
margin: const EdgeInsets.only(right: 10, left: 20),
padding: const EdgeInsets.only(
left: 10, bottom: 3, right: 10, top: 5),
decoration: const BoxDecoration(
color: lightBlue,
),
child: Column(
children: [
const Text(
'Summery results',
style: TextStyle(color: medBlueColor),
),
Container(
margin: const EdgeInsets.only(top: 10, bottom: 5),
alignment: Alignment.centerLeft,
child: const Text(
'Results',
style: TextStyle(color: medBlueColor),
),
),
Container(
padding: const EdgeInsets.only(right: 5, left: 5),
decoration: BoxDecoration(
color: whiteColor,
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: borderColor,
)),
child: DropdownButtonHideUnderline(
child: DropdownButton<String>(
focusColor: whiteColor,
value: aqltbl.result,
hint: const Text('select result'),
isExpanded: true,
iconSize: 36,
icon: const Icon(Icons.arrow_drop_down),
items: resultsFld.map((res) {
return DropdownMenuItem<String>(
value: res,
child: Text(
res,
style: const TextStyle(
fontFamily: 'tajawal',
fontSize: 15,
color: medBlueColor),
),
);
}).toList(),
onChanged: (val) {
setState(() {
aqltbl.result = val;
});
_save();
},
),
),
),
// aqltbl.result = selRes;
MyInputField(
width: 300,
title: 'information remarks (ir)',
hint: '',
maxLines: 3,
borderColor: borderColor,
textColor: textColor,
hintColor: hintColor,
controller: _irController,
onSubmit: (value) {
aqltbl.ir = value;
_save();
},
),
],
),
),
],
),
),
//Images Container
Container(
height: 400,
padding: const EdgeInsets.all(0),
margin: const EdgeInsets.only(bottom: 10, left: 10),
decoration: const BoxDecoration(color: lightGrey),
child: (aqltbl.images ?? []).isEmpty
? Column(
children: [
Image.asset(
'images/empty-photo.jpg',
height: 300,
),
Container(
margin: const EdgeInsets.only(top: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Text('Click'),
Text(
'Camera button',
style: TextStyle(fontWeight: FontWeight.bold),
),
Text(' to add new photo'),
],
)),
],
)
: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: (aqltbl.images ?? []).length,
itemBuilder: (context, imgIndex) {
String? _image =
(aqltbl.images ?? [])[imgIndex].name.toString();
inspect('aql image file');
File(_image).exists() == true
? inspect('image exist')
: inspect('not exist: ' + _image);
inspect('_image: ' + _image);
if (aqltbl.images!.length >
_imgCommintControllers.length) {
_imgCommintControllers.add(TextEditingController());
}
_imgCommintControllers[imgIndex].text =
aqltbl.images![imgIndex].imgComment!;
inspect(_imgCommintControllers.length);
return Container(
margin: const EdgeInsets.only(left: 5),
height: 300,
child: Column(
children: [
Stack(
children: [
Image.file(
File(_image),
height: 300,
),
Container(
decoration: const BoxDecoration(
color: medBlueColor,
),
child: IconButton(
onPressed: () {
inspect('clear');
String imgName =
aqltbl.images![imgIndex].name ??
'';
aqltbl.images!.removeAt(imgIndex);
// aqltbl.images!.removeWhere(
// (item) => item.name == imgName);
_imgCommintControllers
.removeAt(imgIndex);
setState(() {});
},
color: whiteColor,
icon: const Icon(Icons.clear)),
)
],
),
MyInputField(
title: 'Write remarks about image',
hint: '',
controller: _imgCommintControllers[imgIndex],
onSubmit: (value) {
aqltbl.images![imgIndex].imgComment = value;
aqltbl.images![imgIndex].name = _image;
_save();
},
),
],
));
}),
),
],
),
),
// --- FloatingActionButton
floatingActionButtonLocation: FloatingActionButtonLocation
.centerDocked, //specify the location of the FAB
floatingActionButton: FloatingActionButton(
backgroundColor: medBlueColor,
onPressed: () {
inspect(aqltbl);
setState(() {
clickedCentreFAB =
!clickedCentreFAB; //to update the animated container
});
},
tooltip: "Centre FAB",
child: Container(
margin: const EdgeInsets.all(15.0),
child: const Icon(Icons.send),
),
elevation: 4.0,
),
// --- bottom action bar
bottomNavigationBar: BottomAppBar(
child: Container(
margin: const EdgeInsets.only(left: 12.0, right: 12.0),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
//to leave space in between the bottom app bar items and below the FAB
IconButton(
//update the bottom app bar view each time an item is clicked
onPressed: () {
// updateTabSelection(0, "Home");
Get.to(const MainMenu());
},
iconSize: 27.0,
icon: Image.asset(
'images/logo.png',
color: medBlueColor,
),
),
const SizedBox(
width: 50.0,
),
IconButton(
onPressed: () async {
// updateTabSelection(2, "Incoming");
await _takeImage('gallery');
},
iconSize: 27.0,
icon: const Icon(
Icons.image_outlined,
color: medBlueColor,
),
),
IconButton(
onPressed: () async {
// updateTabSelection(1, "Outgoing");
await _takeImage('camera');
},
iconSize: 27.0,
icon: const Icon(
Icons.camera_alt,
color: medBlueColor,
),
),
],
),
),
//to add a space between the FAB and BottomAppBar
shape: const CircularNotchedRectangle(),
//color of the BottomAppBar
color: Colors.white,
),
);
}
_save() {
inspect('submit');
saveAql(shipmentId, aqltbl);
box.write('aql'+shipmentId.toString(), aqltbl);
}
_takeImage(method) async {
String _imgPath = await imageFromDevice(method);
if (_imgPath != empty) {
ImagesModel imgMdl = ImagesModel();
imgMdl.name = _imgPath;
imgMdl.imgComment = '';
if (aqltbl.images != null) {
// _saveLocaly(close: 0);
setState(() {
aqltbl.images!.add(imgMdl);
_save();
});
}
}
}
Future _sendAqlToServer() async {
try {
var response = await http.post(urlSendProductPhoto, body: {
'aql': json.encode(aqltbl).toString(),
'id': shipmentId.toString(),
});
if (response.statusCode == 200) {
Get.snackbar('Success', 'Image successfully uploaded');
return response.body;
} else {
Get.snackbar('Fail', 'Image not uploaded');
inspect('Request failed with status: ${response.statusCode}.');
return 'empty';
}
} catch (socketException) {
Get.snackbar('warning', 'Image not uploaded');
return 'empty';
}
}
}
Try following the path that it is saying it cannot find in your computer, I had a similar issue, I tried opening an Iphone 12 instead of an Iphone 13 and it worked out the issue, my problem was I inadvertently deleted a few files I shouldn't have.

How to usage Obx() with getx in flutter?

On my UI screen, I have 2 textfields in a column. If textFormFieldEntr is empty, hide textFormFiel. If there is a value in the textFormFieldEntr, let the other textfield be visible. After I set the bool active variable to false in the Controller class, I checked the value in the textFormFieldEntr in the showText class. I'm wrong using obx on the UI screen. The textfields are listed in the _formTextField method. Can you answer by explaining the correct obx usage on the code I shared?
class WordController extends GetxController {
TextEditingController controllerInput1 = TextEditingController();
TextEditingController controllerInput2 = TextEditingController();
bool active = false.obs();
final translator = GoogleTranslator();
RxList data = [].obs;
#override
void onInit() {
getir();
super.onInit();
}
void showText() {
if (!controllerInput1.text.isEmpty) {
active = true;
}
}
ekle(Word word) async {
var val = await WordRepo().add(word);
showDilog("Kayıt Başarılı");
update();
return val;
}
updateWord(Word word) async {
var val = await WordRepo().update(word);
showDilog("Kayıt Başarılı");
return val;
}
deleteWord(int? id) async {
var val = await WordRepo().deleteById(id!);
return val;
}
getir() async {
//here read all data from database
data.value = await WordRepo().getAll();
print(data);
return data;
}
translateLanguage(String newValue) async {
if (newValue == null || newValue.length == 0) {
return;
}
List list = ["I", "i"];
if (newValue.length == 1 && !list.contains(newValue)) {
return;
}
var translate = await translator.translate(newValue, from: 'en', to: 'tr');
controllerInput2.text = translate.toString();
//addNote();
return translate;
}
showDilog(String message) {
Get.defaultDialog(title: "Bilgi", middleText: message);
}
addNote() async {
var word =
Word(wordEn: controllerInput1.text, wordTr: controllerInput2.text);
await ekle(word);
getir();
clear();
}
clear() {
controllerInput2.clear();
controllerInput1.clear();
}
updateNote() async {
var word =
Word(wordEn: controllerInput1.text, wordTr: controllerInput2.text);
await updateWord(word);
await getir();
update();
}
}
UI:
class MainPage extends StatelessWidget {
bool _active=false.obs();
String _firstLanguage = "English";
String _secondLanguage = "Turkish";
WordController controller = Get.put(WordController());
final _formKey = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
controller.getir();
return Scaffold(
drawer: _drawer,
backgroundColor: Colors.blueAccent,
appBar: _appbar,
body: _bodyScaffold,
floatingActionButton: _floattingActionButton,
);
}
SingleChildScrollView get _bodyScaffold {
return SingleChildScrollView(
child: Column(
children: [
chooseLanguage,
translateTextView,
],
),
);
}
AppBar get _appbar {
return AppBar(
backgroundColor: Colors.blueAccent,
centerTitle: true,
title: Text("TRANSLATE"),
elevation: 0.0,
);
}
get chooseLanguage => Container(
height: 55.0,
decoration: buildBoxDecoration,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
firstChooseLanguage,
changeLanguageButton,
secondChooseLanguage,
],
),
);
get buildBoxDecoration {
return BoxDecoration(
color: Colors.white,
border: Border(
bottom: BorderSide(
width: 3.5,
color: Colors.grey,
),
),
);
}
refreshList() {
controller.getir();
}
get changeLanguageButton {
return Material(
color: Colors.white,
child: IconButton(
icon: Icon(
Icons.wifi_protected_setup_rounded,
color: Colors.indigo,
size: 30.0,
),
onPressed: () {},
),
);
}
get secondChooseLanguage {
return Expanded(
child: Material(
color: Colors.white,
child: InkWell(
onTap: () {},
child: Center(
child: Text(
this._secondLanguage,
style: TextStyle(
color: Colors.blue[600],
fontSize: 22.0,
),
),
),
),
),
);
}
get firstChooseLanguage {
return Expanded(
child: Material(
color: Colors.white,
child: InkWell(
onTap: () {},
child: Center(
child: Text(
this._firstLanguage,
style: TextStyle(
color: Colors.blue[600],
fontSize: 22.0,
),
),
),
),
),
);
}
get translateTextView => Column(
children: [
Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(8.0)),
),
margin: EdgeInsets.only(left: 2.0, right: 2.0, top: 2.0),
child: _formTextField,
),
Container(
height: Get.height/1.6,
child: Obx(() {
return ListView.builder(
itemCount: controller.data.length,
itemBuilder: (context, index) {
return Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(5.0)),
),
margin: EdgeInsets.only(left: 2.0, right: 2.0, top: 0.8),
child: Container(
color: Colors.white30,
height: 70.0,
padding:
EdgeInsets.only(left: 8.0, top: 8.0, bottom: 8.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
firstText(controller.data, index),
secondText(controller.data, index),
],
),
historyIconbutton,
],
),
),
);
},
);
}),
)
],
);
get _formTextField {
return Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
color: Colors.white30,
height: 120.0,
padding: EdgeInsets.only(left: 16.0, top: 8.0, bottom: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
textFormFieldEntr,
favoriIconButton,
],
),
),
textFormField //burası kapandı
],
),
);
}
get textFormFieldEntr {
return Flexible(
child: Container(
child: TextFormField(
onTap: () {
showMaterialBanner();
},
// onChanged: (text) {
// controller.translateLanguage(text);
// },
controller: controller.controllerInput1,
maxLines: 6,
validator: (controllerInput1) {
if (controllerInput1!.isEmpty) {
return "lütfen bir değer giriniz";
} else if (controllerInput1.length > 22) {
return "en fazla 22 karakter girebilirsiniz";
}
return null;
},
decoration: InputDecoration(
hintText: "Enter",
contentPadding: const EdgeInsets.symmetric(vertical: 5.0),
),
),
),
);
}
void showMaterialBanner() {
ScaffoldMessenger.of(Get.context!).showMaterialBanner(MaterialBanner(
backgroundColor: Colors.red,
content: Padding(
padding: const EdgeInsets.only(top: 30.0),
child: Column(
children: [
TextFormField(
controller: controller.controllerInput1,
maxLines: 6,
onChanged: (text) {
controller.translateLanguage(text);
controller.showText();
},
validator: (controllerInput2) {
if (controllerInput2!.length > 22) {
return "en fazla 22 karakter girebilirsiniz";
}
return null;
},
decoration: InputDecoration(
suffixIcon: IconButton(onPressed: () {
FocusScope.of(Get.context!).unfocus();
closeBanner();
}, icon: Icon(Icons.clear),),
contentPadding: const EdgeInsets.symmetric(vertical: 5.0),
),
),
SizedBox(height: 80.0),
TextFormField(
controller: controller.controllerInput2,
maxLines: 6,
validator: (controllerInput2) {
if (controllerInput2!.length > 22) {
return "en fazla 22 karakter girebilirsiniz";
}
return null;
},
decoration: InputDecoration(
contentPadding: const EdgeInsets.symmetric(vertical: 5.0),
),
),
],
),
),
actions: [
IconButton(
onPressed: () {
closeBanner();
},
icon: Icon(
Icons.arrow_forward_outlined,
color: Colors.indigo,
size: 36,
),
),
]));
}
void closeBanner() {
ScaffoldMessenger.of(Get.context!).hideCurrentMaterialBanner();
}
// } controller.controllerInput1.text==""?_active=false: _active=true;
get textFormField {
return Visibility(
visible: controller.active,
child: Container(
color: Colors.white30,
height: 120.0,
padding: EdgeInsets.only(left: 16.0, right: 42.0, top: 8.0, bottom: 8.0),
child: Container(
child: TextFormField(
controller: controller.controllerInput2,
maxLines: 6,
validator: (controllerInput2) {
if (controllerInput2!.length > 22) {
return "en fazla 22 karakter girebilirsiniz";
}
return null;
},
decoration: InputDecoration(
contentPadding: const EdgeInsets.symmetric(vertical: 5.0),
),
),
),
),
);
}
FutureBuilder<dynamic> get historyWordList {
return FutureBuilder(
future: controller.getir(),
builder: (context, AsyncSnapshot snapShot) {
if (snapShot.hasData) {
var wordList = snapShot.data;
return ListView.builder(
itemCount: wordList.length,
itemBuilder: (context, index) {
return Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(5.0)),
),
margin: EdgeInsets.only(left: 8.0, right: 8.0, top: 0.8),
child: Container(
color: Colors.white30,
height: 70.0,
padding: EdgeInsets.only(left: 8.0, top: 8.0, bottom: 8.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
firstText(wordList, index),
secondText(wordList, index),
],
),
historyIconbutton,
],
),
),
);
},
);
} else {
return Center();
}
},
);
}
IconButton get historyIconbutton {
return IconButton(
onPressed: () {},
icon: Icon(Icons.history),
iconSize: 30.0,
);
}
Text firstText(wordList, int index) {
return Text(
"İngilizce: ${wordList[index].wordEn ?? ""}",
style: TextStyle(
fontWeight: FontWeight.w600,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
);
}
Text secondText(wordList, int index) {
return Text(
"Türkçe: ${wordList[index].wordTr ?? ""}",
style: TextStyle(
fontWeight: FontWeight.w400,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
);
}
get favoriIconButton {
return IconButton(
alignment: Alignment.topRight,
onPressed: () async {
bool validatorKontrol = _formKey.currentState!.validate();
if (validatorKontrol) {
String val1 = controller.controllerInput1.text;
String val2 = controller.controllerInput2.text;
print("$val1 $val2");
await controller.addNote();
await refreshList();
}
await Obx(() => textFormField(
controller: controller.controllerInput2,
));
await Obx(() => textFormField(
controller: controller.controllerInput1,
));
},
icon: Icon(
Icons.forward,
color: Colors.blueGrey,
size: 36.0,
),
);
}
FloatingActionButton get _floattingActionButton {
return FloatingActionButton(
onPressed: () {
Get.to(WordListPage());
},
child: Icon(
Icons.app_registration,
size: 30,
),
);
}
Drawer get _drawer {
return Drawer(
child: ListView(
// Important: Remove any padding from the ListView.
padding: EdgeInsets.zero,
children: <Widget>[
userAccountsDrawerHeader,
drawerFavorilerim,
drawersettings,
drawerContacts,
],
),
);
}
ListTile get drawerContacts {
return ListTile(
leading: Icon(Icons.contacts),
title: Text("Contact Us"),
onTap: () {
Get.back();
},
);
}
ListTile get drawersettings {
return ListTile(
leading: Icon(Icons.settings),
title: Text("Settings"),
onTap: () {
Get.back();
},
);
}
ListTile get drawerFavorilerim {
return ListTile(
leading: Icon(
Icons.star,
color: Colors.yellow,
),
title: Text("Favorilerim"),
onTap: () {
Get.to(FavoriListPage());
},
);
}
UserAccountsDrawerHeader get userAccountsDrawerHeader {
return UserAccountsDrawerHeader(
accountName: Text("UserName"),
accountEmail: Text("E-mail"),
currentAccountPicture: CircleAvatar(
backgroundColor: Colors.grey,
child: Text(
"",
style: TextStyle(fontSize: 40.0),
),
),
);
}
}
You can simple do it like this
class ControllerSample extends GetxController{
final active = false.obs
functionPass(){
active(!active.value);
}
}
on page
final sampleController = Get.put(ControllerSample());
Obx(
()=> Form(
key: yourkeyState,
child: Column(
children: [
TextFormField(
//some other needed
//put the function on onChanged
onChanged:(value){
if(value.isEmpty){
sampleController.functionPass();
}else{
sampleController.functionPass();
}
}
),
Visibility(
visible: sampleController.active.value,
child: TextFormField(
//some other info
)
),
]
)
)
)

how can make listview builder to a reorderable listview in flutter (a priority task app (todo app))

how can i convert listview to reorderedableListview to make a priority task app
this is my application design output
i see many solutions but in most of them i found error
Here is initstate code
class _TodoListScreenState extends State<TodoListScreen> {
late List<Task> taskList = [];
#override
void initState() {
super.initState();
_updateTaskList();
}
_updateTaskList() async {
print('--------->update');
this.taskList = await DatabaseHelper.instance.getTaskList();
print(taskList);
setState(() {});
}
this is method where listtile created
Widget _buildTask(Task task) {
return Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 25.0),
child: ListTile(
title: Text(
task.title!,
style: TextStyle(
fontSize: 18.0,
decoration: task.status == 0
? TextDecoration.none
: TextDecoration.lineThrough,
),
),
subtitle: Text(
'${DateFormat.yMMMEd().format(task.date!)} • ${task.priority}',
style: TextStyle(
fontSize: 18.0,
decoration: task.status == 0
? TextDecoration.none
: TextDecoration.lineThrough,
),
),
trailing: Checkbox(
onChanged: (value) {
task.status = value! ? 1 : 0;
DatabaseHelper.instance.updateTask(task);
_updateTaskList();
},
value: task.status == 1 ? true : false,
activeColor: Theme.of(context).primaryColor,
),
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AddTask(
task: task,
updateTaskList: () {
_updateTaskList();
},
),
),
),
),
),
Divider(),
],
);
}
this is method build
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
backgroundColor: Theme.of(context).primaryColor,
onPressed: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AddTask(updateTaskList: _updateTaskList),
),
),
),
in this body tag i want to create reorderable listview
body: ListView.builder(
padding: EdgeInsets.symmetric(vertical: 80.0),
itemCount: taskList.length + 1,
itemBuilder: (BuildContext context, int index) {
if (index == 0) {
return Padding(
padding:
const EdgeInsets.symmetric(horizontal: 40.0, vertical: 20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"My Tasks",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 40.0,
color: Colors.black),
),
SizedBox(height: 15.0),
Text(
'${taskList.length} of ${taskList.where((Task task) => task.status == 1).toList().length} task complete ',
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 20.0,
color: Colors.grey,
),
),
],
),
);
} else {
return _buildTask(taskList[index - 1]);
}
},
),
);
}
}
this is whole code i want to change
There is a widget like ReorderableListView and library like Reorderables are available that you can use.
Updated
Sample Code:
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:reorderables/reorderables.dart';
class ReorderablesImagesPage extends StatefulWidget {
ReorderablesImagesPage({
Key key,
this.title,
#required this.size,
}) : super(key: key);
final String title;
final double size;
#override
State<StatefulWidget> createState() => _ReorderablesImagesPageState();
}
class _ReorderablesImagesPageState extends State<ReorderablesImagesPage> {
List<Widget> _tiles;
int maxImageCount = 30;
double iconSize;
final int itemCount = 3;
final double spacing = 8.0;
final double runSpacing = 8.0;
final double padding = 8.0;
#override
void initState() {
super.initState();
iconSize = ((widget.size - (itemCount - 1) * spacing - 2 * padding) / 3)
.floor()
.toDouble();
_tiles = <Widget>[
Container(
child: Image.network('https://picsum.photos/250?random=1'),
width: iconSize,
height: iconSize,
),
Container(
child: Image.network('https://picsum.photos/250?random=2'),
width: iconSize,
height: iconSize,
),
Container(
child: Image.network('https://picsum.photos/250?random=3'),
width: iconSize,
height: iconSize,
),
Container(
child: Image.network('https://picsum.photos/250?random=4'),
width: iconSize,
height: iconSize,
),
Container(
child: Image.network('https://picsum.photos/250?random=5'),
width: iconSize,
height: iconSize,
),
Container(
child: Image.network('https://picsum.photos/250?random=6'),
width: iconSize,
height: iconSize,
),
Container(
child: Image.network('https://picsum.photos/250?random=7'),
width: iconSize,
height: iconSize,
),
Container(
child: Image.network('https://picsum.photos/250?random=8'),
width: iconSize,
height: iconSize,
),
Container(
child: Image.network('https://picsum.photos/250?random=9'),
width: iconSize,
height: iconSize,
),
];
}
#override
Widget build(BuildContext context) {
void _onReorder(int oldIndex, int newIndex) {
setState(() {
Widget row = _tiles.removeAt(oldIndex);
_tiles.insert(newIndex, row);
});
}
var wrap = ReorderableWrap(
minMainAxisCount: itemCount,
maxMainAxisCount: itemCount,
spacing: spacing,
runSpacing: runSpacing,
padding: EdgeInsets.all(padding),
children: _tiles,
onReorder: _onReorder,
onNoReorder: (int index) {
//this callback is optional
debugPrint(
'${DateTime.now().toString().substring(5, 22)} reorder cancelled. index:$index');
},
onReorderStarted: (int index) {
//this callback is optional
debugPrint(
'${DateTime.now().toString().substring(5, 22)} reorder started: index:$index');
});
var column = Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Expanded(
child: SingleChildScrollView(
child: wrap,
),
),
ButtonBar(
buttonPadding: EdgeInsets.all(16),
alignment: MainAxisAlignment.end,
children: <Widget>[
if (_tiles.length > 0)
IconButton(
iconSize: 50,
icon: Icon(Icons.remove_circle),
color: Colors.teal,
padding: const EdgeInsets.all(0.0),
onPressed: () {
setState(() {
_tiles.removeAt(0);
});
},
),
if (_tiles.length < maxImageCount)
IconButton(
iconSize: 50,
icon: Icon(Icons.add_circle),
color: Colors.deepOrange,
padding: const EdgeInsets.all(0.0),
onPressed: () {
var rand = Random();
var newTile = Container(
child: Image.network(
'https://picsum.photos/250?random=${rand.nextInt(100)}'),
width: iconSize,
height: iconSize,
);
setState(() {
_tiles.add(newTile);
});
},
),
],
),
],
);
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: column,
);
}
}

Flutter Firebase realtime database issue

here is my problem:
database I use has several categories.
Each category has several number of cases.
What I want, is to extract the information from the database, how many cases each category has and to show this information in a list. That means - to show the names of the categories AND how many children (cases) EACH category has (totalCases)...
I read data from database by using feature getNumberOfNodes and so i can get the number of children (cases) of one category by using future builder.
BUT:
If I execute code, I get now the names of all categories and the number of children (cases) which the FIRST category has.
What do I do wrong?
Here is my code, I'm struggling with:
import '../components/category_list_tile.dart';
import 'package:firebase_database/ui/firebase_animated_list.dart';
import 'package:flutter/material.dart';
import '../constants.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_database/firebase_database.dart';
import 'dart:async';
class ScreenCategoryList extends StatefulWidget {
static String id = 'screen_category_list';
final FirebaseApp app;
ScreenCategoryList({this.app});
#override
_ScreenCategoryListState createState() => _ScreenCategoryListState();
}
class _ScreenCategoryListState extends State<ScreenCategoryList> {
final referenceDatabase = FirebaseDatabase.instance;
//final _dbRef = FirebaseDatabase.instance.reference().child("de");
int counter = 0;
bool showSpinner = false;
DatabaseReference _databaseReference;
#override
void initState() {
final FirebaseDatabase database = FirebaseDatabase(app: widget.app);
_databaseReference = database.reference().child("de");
super.initState();
}
Future<int> getNumberOfNodes(int counter) async {
final response = await FirebaseDatabase.instance
.reference()
.child('de')
.child('category')
.child('$counter')
.child('cases')
.once();
var nodes = [];
response.value.forEach((v) => nodes.add(v));
print(nodes.length);
counter++;
return nodes.length;
}
#override
Widget build(BuildContext context) {
int counter = 0;
return Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.white, Colors.white],
),
image: const DecorationImage(
image: AssetImage("images/background.png"), fit: BoxFit.cover),
),
child: Scaffold(
backgroundColor: Colors.transparent,
appBar: AppBar(
toolbarHeight: 60.0,
elevation: 0.0,
backgroundColor: Colors.black12,
leading: Padding(
padding: EdgeInsets.only(left: 12.0, top: 12.0, bottom: 12.0),
child: Image(image: AssetImage('images/lexlogo_black.png'))),
title: Center(
child: Column(
children: [
Text(
'Kategorien',
style: TextStyle(
color: kMainDarkColor,
fontFamily: 'Roboto',
fontSize: 21.0,
fontWeight: FontWeight.bold),
),
],
),
),
actions: [
Padding(
padding: EdgeInsets.only(right: 8.0),
child: IconButton(
icon: Icon(Icons.more_vert_rounded),
iconSize: 30.0,
color: kMainDarkColor,
onPressed: () {},
//onPressed: onPressMenuButton,
),
),
],
),
body: FutureBuilder<int>(
future: getNumberOfNodes(counter),
builder: (BuildContext context, AsyncSnapshot<int> casesSnapshot) {
if (casesSnapshot.hasData) {
return FirebaseAnimatedList(
reverse: false,
padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 20.0),
query: _databaseReference.child('category'),
itemBuilder: (
BuildContext context,
DataSnapshot categorySnapshot,
Animation<double> animation,
int index,
) {
int numberOfCases = casesSnapshot.data;
print('$counter, $numberOfCases');
return CategoryListTile(
title: categorySnapshot.value['name'].toString(),
successfulCases: 10,
totalCases: casesSnapshot.data,
onTitleClick: () {},
onInfoButtonClick: () {},
);
},
);
} else if (casesSnapshot.hasError) {
return Center(
child: Column(
children: [
Icon(
Icons.error_outline,
color: Colors.red,
size: 60,
),
Padding(
padding: const EdgeInsets.only(top: 16),
child: Text('Error: ${casesSnapshot.error}'),
)
],
),
);
} else {
return Center(
child: Column(
children: [
SizedBox(
child: CircularProgressIndicator(),
width: 60,
height: 60,
),
Padding(
padding: EdgeInsets.only(top: 16),
child: Text('Awaiting result...'),
)
],
),
);
}
},
),
),
);
}
}
got it...
here is the code, that worked for me:
class _ScreenCategoryListState extends State<ScreenCategoryList> {
final referenceDatabase = FirebaseDatabase.instance;
bool showSpinner = false;
DatabaseReference _databaseReference;
#override
void initState() {
final FirebaseDatabase database = FirebaseDatabase(app: widget.app);
_databaseReference = database.reference().child("de");
super.initState();
}
Future<Map<int, int>> getNumberOfNodes() async {
Map<int, int> caseNumbers = new Map<int, int>();
// read number of category nodes
final categoriesNumbersResponse = await FirebaseDatabase.instance
.reference()
.child('de')
.child('category')
// .child('0')
// .child('cases')
.once();
var categoryNodes = [];
categoriesNumbersResponse.value.forEach((v) => categoryNodes.add(v));
int numberOfCategories = categoryNodes.length;
//read number of cases in category
for (int i = 0; i < numberOfCategories; i++) {
final caseResponse = await FirebaseDatabase.instance
.reference()
.child('de')
.child('category')
.child('$i')
.child('cases')
.once();
var caseNodes = [];
caseResponse.value.forEach((v) => caseNodes.add(v));
int numberOfCases = caseNodes.length;
caseNumbers[i] = numberOfCases;
}
return caseNumbers;
}
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.white, Colors.white],
),
image: const DecorationImage(
image: AssetImage("images/background.png"), fit: BoxFit.cover),
),
child: Scaffold(
backgroundColor: Colors.transparent,
appBar: AppBar(
toolbarHeight: 60.0,
elevation: 0.0,
backgroundColor: Colors.black12,
leading: Padding(
padding: EdgeInsets.only(left: 12.0, top: 12.0, bottom: 12.0),
child: Image(image: AssetImage('images/lexlogo_black.png'))),
title: Center(
child: Column(
children: [
Text(
'Kategorien',
style: TextStyle(
color: kMainDarkColor,
fontFamily: 'Roboto',
fontSize: 21.0,
fontWeight: FontWeight.bold),
),
],
),
),
actions: [
Padding(
padding: EdgeInsets.only(right: 8.0),
child: IconButton(
icon: Icon(Icons.more_vert_rounded),
iconSize: 30.0,
color: kMainDarkColor,
onPressed: () {},
//onPressed: onPressMenuButton,
),
),
],
),
body: FutureBuilder<Map<int, int>>(
future: getNumberOfNodes(),
builder: (BuildContext context,
AsyncSnapshot<Map<int, int>> casesSnapshot) {
if (casesSnapshot.hasData) {
return FirebaseAnimatedList(
reverse: false,
padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 20.0),
query: _databaseReference.child('category'),
itemBuilder: (
BuildContext context,
DataSnapshot categorySnapshot,
Animation<double> animation,
int index,
) {
int numberOfCases = casesSnapshot.data[index];
//print('number of cases $_counter, $numberOfCases');
return CategoryListTile(
title: categorySnapshot.value['name'].toString(),
successfulCases: 10,
totalCases: numberOfCases,
onTitleClick: () {},
onInfoButtonClick: () {},
);
},
);
} else if (casesSnapshot.hasError) {
return Center(
child: Column(
children: <Widget>[
Icon(
Icons.error_outline,
color: Colors.red,
size: 60,
),
Padding(
padding: const EdgeInsets.only(top: 16),
child: Text('Error: ${casesSnapshot.error}'),
)
],
),
);
} else {
return Center(
child: Column(
children: <Widget>[
SizedBox(
child: CircularProgressIndicator(),
width: 60,
height: 60,
),
Padding(
padding: EdgeInsets.only(top: 16),
child: Text('Awaiting result...'),
)
],
),
);
}
},
),
),
);
}
}

How to call Provider on initState?

I have a Provider which changes my UI if the value isLiked is true or false. The function works fine but I noticed that every time I restart the app the UI returns to its original state. How can I save its state so it doesn't restart when I kill the app?
here is my class:
class Selfie extends ChangeNotifier {
Selfie.fromDocument(DocumentSnapshot doc) {
selfieId = doc['selfieId'] as String;
ownerId = doc['ownerId'] as String;
displayName = doc['displayName'] as String;
photoUrl = doc['photoUrl'] as String;
mediaUrl = doc['mediaUrl'] as String;
timestamp = doc['timestamp'] as Timestamp;
likes = doc['likes'] as Map;
likesCount = doc['likesCount'] as int;
}
String selfieId;
String ownerId;
String displayName;
String photoUrl;
String mediaUrl;
Timestamp timestamp;
Map likes;
int likesCount;
bool _isLiked = false;
bool get isLiked => _isLiked;
set isLiked(bool value) {
_isLiked = value;
notifyListeners();
}
bool _showHeart = false;
bool get showHeart => _showHeart;
set showHeart(bool value) {
_showHeart = value;
notifyListeners();
}
void handleLikePost(String userId, AuthUser authUser) {
bool _isLiked = likes[userId] == true;
if (_isLiked) {
selfiesRef.doc(selfieId).update({
'likes.$userId': false,
'likesCount': FieldValue.increment(-1),
});
//removeLikeFromActivityFeed();
//likeCount -= 1;
isLiked = false;
likes[userId] = false;
selfiesRef.doc(selfieId).collection('likes').doc(userId).delete();
} else if (!_isLiked) {
selfiesRef.doc(selfieId).update({
'likes.$userId': true,
'likesCount': FieldValue.increment(1),
});
selfiesRef.doc(selfieId).collection('likes').doc(userId).set({
'displayName': authUser.displayName,
'userId': userId,
'photoUrl': authUser.photoUrl
});
//addLikeToActivityFed();
//likeCount += 1;
isLiked = true;
likes[userId] = true;
showHeart = true;
Timer(Duration(milliseconds: 500), () {
showHeart = false;
});
}
notifyListeners();
}
}
here is my UI:
class SelfieCard extends StatelessWidget {
final Selfie selfie;
final String userId;
SelfieCard({this.selfie, this.userId});
#override
Widget build(BuildContext context) {
final AuthUser authUser =
Provider.of<UserManager>(context, listen: false).authUser;
return ChangeNotifierProvider.value(
value: selfie,
child: Consumer<Selfie>(
builder: (_, selfie, __) {
return Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15)),
clipBehavior: Clip.antiAliasWithSaveLayer,
elevation: 7,
margin: EdgeInsets.symmetric(horizontal: 16, vertical: 4),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Consumer<UserManager>(
builder: (_, userManager, __) {
bool isPostOwner = userManager.isLoggedIn
? userManager.authUser.id == selfie.ownerId
: null;
return Padding(
padding: const EdgeInsets.only(top: 0.0, bottom: 0),
child: GestureDetector(
onTap: () {},
child: ListTile(
leading: CircleAvatar(
backgroundColor: Colors.grey,
backgroundImage: NetworkImage(selfie.photoUrl),
),
title: Text(
selfie.displayName,
style: TextStyle(
fontFamily: 'BebasNeue',
fontSize: 19,
color: Colors.black,
),
),
trailing: userManager.isLoggedIn && isPostOwner
? IconButton(
onPressed: () {},
icon: Icon(Icons.more_vert),
)
: Text('')),
),
);
},
),
GestureDetector(
onDoubleTap: () {},
child: Stack(
alignment: Alignment.center,
children: <Widget>[
cachedNetworkImage(selfie.mediaUrl),
selfie.showHeart
? Animator(
duration: Duration(milliseconds: 500),
tween: Tween(begin: 0.8, end: 1.4),
cycles: 0,
curve: Curves.bounceOut,
builder: (context, animatorState, chil) =>
Transform.scale(
scale: animatorState.value,
child: Icon(
Icons.favorite,
color: Colors.red,
size: 80,
),
),
)
: Text('')
],
),
),
Padding(
padding: const EdgeInsets.only(top: 8.0, bottom: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Padding(
padding: const EdgeInsets.only(left: 20.0),
child: Text(
timeago.format(selfie.timestamp.toDate(),
locale: 'en_short'),
style:
TextStyle(fontWeight: FontWeight.bold),
),
),
Expanded(
child: Container(),
),
GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => LikesScreen(
postId: selfie.selfieId)));
},
child: Container(
margin: EdgeInsets.only(right: 10),
child: Text(
selfie.likesCount.toString(),
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold),
),
),
),
Padding(
padding: EdgeInsets.only(
top: 40,
),
),
GestureDetector(
onTap: () {
selfie.handleLikePost(userId, authUser);
},
child: Icon(
selfie.isLiked
? Icons.favorite
: Icons.favorite_border,
color: Colors.red,
size: 25,
),
),
Padding(
padding: EdgeInsets.only(top: 40, right: 10),
),
SizedBox(
width: 20,
)
],
),
],
)),
],
),
)
],
),
);
},
));
}
}
I know I should have it on initState but I also know that initState can't access the context. Do you guys have any idea how I can solve it?
final provider = Provider.of(context, listen:false);
The problem is that your isLiked variable will initially be false always. After the user likes the image, it is being stored but you're only storing it locally and temporarily.
In order to keep it consistent, you can store it permanently either locally or in your Firestore. If you want to store it locally, you can store it using "Shared Preferences", "SQfLite", "Hive", etc. However, it won't be efficient as if the user deletes the app or clears data, all of the data might get lost.
you can use it the same way you've done in the build method... the only thing is you must set listen to false
final provider = Provider.of<T>(context, listen: false);
here T is the type of State you're expecting
check this for further explanation