I am facing an issue when I pull to refresh, I need that after pull to refresh gesture new articles available will be visible in the news page, but this doesn't happen.
I created the cache system, but once I pull to refresh new data, the news page doesn't load new articles.
What can I do to solve this issue?
News page
class _NewsPageState extends State<NewsPage> {
/// News service
final ApiNewsPage categoryNews = ApiNewsPage();
late bool isLoading;
/// Start Refresh indicator upload new articles function
final NewArticlesPage updateArticles = NewArticlesPage();
ScrollController scrollController = ScrollController();
//final List<ArticleModel> _posts = [];
//
#override
void initState() {
super.initState();
refreshArticles();
}
/// Call this when the user pull down the screen
Future<void> refreshArticles() async {
Future.delayed(const Duration(seconds: 2), () {
setState(() {
updateArticles.updateArticles();
print("Uploading new articles");
//_posts.add();
});
return updateArticles.updateArticles();
});
}
/// End Refresh indicator upload new articles function
///
///
#override
Widget build(BuildContext context) {
return RefreshIndicator(
displacement: 0,
color: assofacileMainColor,
backgroundColor: Colors.white,
onRefresh: refreshArticles,
child: Container(
child: FutureBuilder<List?>(
future: categoryNews.getNewsArticles(),
builder: (context, snapshot) { // AsyncSnapshot
if (snapshot.hasData) {
if (snapshot.data?.length == 0) {
return const NoArticle();
}
return ListView.builder(
controller: scrollController,
itemCount: snapshot.data?.length++,
physics: const AlwaysScrollableScrollPhysics(),
itemBuilder: (context, i) {
return Card(
margin: const EdgeInsets.all(8),
elevation: 5,
shadowColor: Colors.black26,
color: Colors.white,
child: InkWell(
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
height: 190,
width: double.infinity,
child:
Image(
image: AdvancedNetworkImage(
snapshot.data![i]["_embedded"]['wp:featuredmedia'][0]["media_details"]["sizes"]["medium"]["source_url"],
useDiskCache: true,
cacheRule: const CacheRule(maxAge: Duration(days: 1)),
),
fit: BoxFit.cover,
),
// CachedNetworkImage(
// cacheManager: CacheManager(
// Config(snapshot.data![i]["_embedded"]['wp:featuredmedia'][0]["media_details"]["sizes"]["medium"]["source_url"],
// stalePeriod: const Duration(days: 1),
// )
// ),
// //cacheManager: DioCacheManager.instance,
// imageUrl: snapshot.data![i]["_embedded"]['wp:featuredmedia'][0]["media_details"]["sizes"]["medium"]["source_url"],
// // checkIfUrlContainsPrefixHttps(
// // _post != null
// // ? _post[0].urlImageSource
// // : "https:" + snapshot.data![i]["_embedded"]['wp:featuredmedia'][0]["media_details"]["sizes"]["thumbnail"]["source_url"],
// // ),
// //snapshot.data![i]["_embedded"]["wp:featuredmedia"][0]["link"],
// //"https:" + snapshot.data![i]["_embedded"]['wp:featuredmedia'][0]["media_details"]["sizes"]["medium"]["source_url"],
// fit: BoxFit.cover,
// placeholder: (context, url) => Image.asset("assets/gif/shimmer.gif",
// width: double.infinity,
// height: 190,
// fit: BoxFit.cover,
// ),
// errorWidget: (context, url, error) => Image.asset("assets/images/unloadedImage.png",
// width: 250, height: 250),
// ),
),
// Title article
Column(
children: [
Padding(
padding: const EdgeInsets.only(left:16, top: 16, bottom: 16),
child: Row(
children: [
Expanded(
child: Text(
snapshot.data![i]["title"]["rendered"]
.replaceAll("’", "'")
.replaceAll("<p>", "")
.replaceAll("</p>", ""),
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
fontFamily: "Raleway",
),
overflow: TextOverflow.ellipsis,
maxLines: 2,
//softWrap: false,
),
),
],
),
)
],
),
],
),
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ArticlePage(data: snapshot.data?[i]),
),
);
},
),
);
},
);
} else if (snapshot.hasError) {
return const NoInternet();
} else {
/// Shimmer
return ShimmerEffect();
}
}
),
),
);
}
}
Fetch and create cache
class ApiNewsPage {
final String url = newsJsonLink;
Future<List?> getNewsArticles() async {
String nameDB = "articledata.json";
var dir = await getTemporaryDirectory();
File file = File(dir.path + "/" + nameDB);
if(file.existsSync()) {
print("Loading Articles from cache");
var jsonDB = file.readAsStringSync();
List response = json.decode(jsonDB);
return response;
} else {
var response = await http.get(Uri.parse(url));
if (response.statusCode == 200) {
//await File(nameDB).exists();
var jsonResponse = response.body;
List newArticles = json.decode(jsonResponse);
//File(nameDB).deleteSync(recursive: true);
/// save json in local file
file.writeAsStringSync(jsonResponse, flush: true, mode: FileMode.write);
print("Fetching new articles from internet");
return newArticles;
}
}
}
}
// Create new db
class NewArticlesPage {
final String url = newsJsonLink;
Future<List?> updateArticles() async {
try {
var response = await http.get(Uri.parse(url));
if (response.statusCode == 200) {
print("Fetching new articles from internet");
return jsonDecode(response.body);
} else {
return Future.error("Impossibile ricevere i dati, prova a controllare la connessione");
}// ignore: non_constant_identifier_names
} catch (SocketException) {
return Future.error("Impossibile caricare gli articoli");
}
}
}
Related
hello guys!
i am using flutter story view package to implement story functionality in my app and i am using camera plugin to record video and upload, however the content type of the uploaded video is application/octet-stream when i receive it on my server and only when it is sent from ios. for android everything is fine and i get the content type as video/mp4.
can you guys please help me.
i have not done anything fancy i have just used the basic functionality of start recording and stop recording of the camra plugin.
// ignore_for_file: use_build_context_synchronously
List<CameraDescription>? cameras;
class CameraScreen extends StatefulWidget {
const CameraScreen({Key? key}) : super(key: key);
#override
State<CameraScreen> createState() => _CameraScreenState();
}
class _CameraScreenState extends State<CameraScreen> {
CameraController? _cameraController;
Future<void>? cameraValue;
bool flash = false;
bool iscamerafront = true;
static const _maxSeconds = 30;
ValueNotifier<bool> visible = ValueNotifier(false);
ValueNotifier<bool> isRecoring = ValueNotifier(false);
TimerProvider? _timerProvider;
#override
void initState() {
super.initState();
_timerProvider = Provider.of(context, listen: false);
_cameraController = CameraController(cameras![0], ResolutionPreset.veryHigh,
imageFormatGroup: ImageFormatGroup.bgra8888);
cameraValue = _cameraController?.initialize();
_cameraController?.prepareForVideoRecording();
lockCamera();
}
lockCamera() async {
await _cameraController!.lockCaptureOrientation();
}
#override
void dispose() {
super.dispose();
visible.dispose();
isRecoring.dispose();
_cameraController?.dispose();
}
#override
Widget build(BuildContext context) {
print("camera_screen");
final size = MediaQuery.of(context).size;
return Scaffold(
extendBodyBehindAppBar: true,
appBar: AppBar(
centerTitle: true,
title: ValueListenableBuilder<bool>(
valueListenable: visible,
builder: (context, value, child) {
return Visibility(
visible: value,
child: Container(
padding: const EdgeInsets.all(10),
width: 50,
height: 50,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.black.withOpacity(0.5)),
child: Center(
child: Consumer<TimerProvider>(
builder: (context, value, child) {
if (value.seconds == 0) {
if (_cameraController!.value.isRecordingVideo) {
stopRecording();
}
}
return Text(
value.seconds.toString(),
style: CustomStyles.fixAppBarTextStyle
.copyWith(color: Colors.white),
);
}),
),
));
}),
backgroundColor: Colors.transparent,
leadingWidth: 100,
leading: InkWell(
onTap: () {
Navigator.pop(context);
},
child: Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
"Cancel",
style: CustomStyles.fixMediumBodyTextStyle
.copyWith(color: Colors.white),
),
)),
),
),
body: SafeArea(
top: false,
child: Stack(
children: [
FutureBuilder(
future: cameraValue,
builder: (context, snapshot) {
_cameraController?.lockCaptureOrientation();
if (snapshot.connectionState == ConnectionState.done) {
return Transform.scale(
scale: size.aspectRatio *
_cameraController!.value.aspectRatio <
1
? 1 /
(size.aspectRatio *
_cameraController!.value.aspectRatio)
: size.aspectRatio *
_cameraController!.value.aspectRatio,
child: Center(child: CameraPreview(_cameraController!)),
);
} else {
return const Center(
child: CircularProgressIndicator.adaptive(),
);
}
}),
Positioned(
bottom: 0.0,
child: Container(
padding: const EdgeInsets.only(top: 5, bottom: 5),
width: Dimensions.screenWidth,
child: Column(
children: [
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Container(
padding: const EdgeInsets.all(4),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.black.withOpacity(0.5)),
child: Center(
child: IconButton(
icon: Icon(
flash ? Icons.flash_on : Icons.flash_off,
color: Colors.white,
size: 28,
),
onPressed: () {
setState(() {
flash = !flash;
});
flash
? _cameraController!
.setFlashMode(FlashMode.torch)
: _cameraController!
.setFlashMode(FlashMode.off);
}),
),
),
ValueListenableBuilder<bool>(
valueListenable: isRecoring,
builder: (context, value, child) {
return GestureDetector(
onLongPress: () {
startRecording();
},
onLongPressUp: () {
stopRecording();
},
onTap: () {
if (value == false) {
takePhoto(context);
}
},
child: value == true
? const CircleAvatar(
radius: 50,
backgroundColor: Colors.white30,
child: CircleAvatar(
radius: 35,
backgroundColor: Colors.red,
),
)
: const CircleAvatar(
radius: 35,
backgroundColor: Colors.white30,
child: CircleAvatar(
radius: 25,
backgroundColor: Colors.white,
),
));
}),
Container(
padding: const EdgeInsets.all(4),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.black.withOpacity(0.5)),
child: IconButton(
icon: const Icon(
Icons.flip_camera_ios,
color: Colors.white,
size: 28,
),
onPressed: () async {
setState(() {
iscamerafront = !iscamerafront;
});
int cameraPos = iscamerafront ? 0 : 1;
_cameraController = CameraController(
cameras![cameraPos], ResolutionPreset.high);
cameraValue = _cameraController?.initialize();
}),
),
],
),
addVerticalSpace(6),
const Text(
"Hold for Video, tap for photo",
style: TextStyle(
color: Colors.white,
),
textAlign: TextAlign.center,
),
addVerticalSpace(5),
],
),
),
),
],
),
),
);
}
void startRecording() async {
visible.value = true;
_timerProvider?.startCountDown(_maxSeconds);
isRecoring.value = true;
await _cameraController?.startVideoRecording();
}
void stopRecording() async {
XFile videopath = await _cameraController!.stopVideoRecording();
_timerProvider?.stopTimer();
isRecoring.value = false;
Navigator.push(
context,
MaterialPageRoute(
builder: (builder) => CameraResult(
image: File(videopath.path),
isImage: false,
))).then((value) {
visible.value = false;
});
}
void takePhoto(BuildContext context) async {
XFile file = await _cameraController!.takePicture();
SystemSound.play(SystemSoundType.click);
final img.Image? capturedImage =
img.decodeImage(await File(file.path).readAsBytes());
final img.Image orientedImage = img.flipHorizontal(capturedImage!);
File finalImage =
await File(file.path).writeAsBytes(img.encodeJpg(orientedImage));
Navigator.push(
context,
CupertinoPageRoute(
builder: (builder) => CameraResult(
image: finalImage,
isImage: true,
))).then((value) {
visible.value = false;
});
}
}
the upload function is as bellow
{File? shouts,
File? thumbnail,
int? duration,
bool? isImage,
required String userId,
required OnUploadProgressCallback onUploadProgress}) async {
assert(shouts != null);
try {
var url = Constants.POSTSHOUTURL;
final httpClient = FileService.getHttpClient();
final request = await httpClient.postUrl(Uri.parse(url));
int byteCount = 0;
var multipart;
if (isImage == false) {
multipart = await http.MultipartFile.fromPath(
'story-media', shouts!.path,
contentType: MediaType("video", "mp4"));
} else {
multipart = await http.MultipartFile.fromPath(
'story-media',
shouts!.path,
);
}
var multipart2 =
await http.MultipartFile.fromPath('thumbnail', thumbnail!.path);
var requestMultipart = http.MultipartRequest('POST', Uri.parse(url));
requestMultipart.fields["userid"] = userId.toString();
requestMultipart.fields["duration"] = duration.toString();
requestMultipart.headers['Content-type'] = 'multipart/form-data';
requestMultipart.files.add(multipart);
requestMultipart.files.add(multipart2);
var msStream = requestMultipart.finalize();
var totalByteLength = requestMultipart.contentLength;
request.contentLength = totalByteLength;
request.headers.add(HttpHeaders.authorizationHeader,
"Bearer ${Hive.box<UserData>(Constants.userDb).get(Hive.box<UserData>(Constants.userDb).keyAt(0))!.token}");
request.headers.set(HttpHeaders.contentTypeHeader,
requestMultipart.headers[HttpHeaders.contentTypeHeader]!);
Stream<List<int>> streamUpload = msStream.transform(
StreamTransformer.fromHandlers(
handleData: (data, sink) {
sink.add(data);
byteCount += data.length;
if (onUploadProgress != null) {
onUploadProgress(byteCount, totalByteLength);
// CALL STATUS CALLBACK;
}
},
handleError: (error, stack, sink) {
throw error;
},
handleDone: (sink) {
sink.close();
// UPLOAD DONE;
},
),
);
await request.addStream(streamUpload);
final httpResponse = await request.close();
var statusCode = httpResponse.statusCode;
if (statusCode ~/ 100 == 2) {
onUploadProgress(0, 0);
return await FileService.readResponseAsString(httpResponse);
}
return "";
} on SocketException {
throw FetchDataException("No internet to upload Shout!");
}
}```
i have used necessary info.plist setting also
I need help in this please i have a RefreshIndicator that loads a ListView when there is data in the database however I want to make the widget return Text widget 'no data' if the table empty instead of the ListView.
but the when the table is empty it error appear
_TypeError (type 'String' is not a subtype of type 'Iterable')
and that will appear in the refresh method i don't know what should i do i appreciate any help thanks in advance.
this the UI for the page
class _onlineResultTab extends State<onlineResultTab> {
List<ResultsModelClass> resultslist = new List();
List<CoursesModelClass> coursesist = new List();
ResultsModelClass resultsModelClass;
CoursesModelClass courseModelClass;
final GlobalKey<RefreshIndicatorState> refreshKey = GlobalKey<RefreshIndicatorState>();
DataApiProvider dataApiProvider = new DataApiProvider();
Widget build(BuildContext context) {
return Scaffold(
body: RefreshIndicator(
key: refreshKey,
onRefresh: refreshList,
child: _resultwidget(context),
),
);
}
fetchResultDetails() async {
final allRows = await DbHelper.mydb.getOnlineResultList();
allRows.forEach((row) => {
resultsModelClass = ResultsModelClass(
"",
row["Res_Stud_Index"],
row["Res_Stud_Name"],
row["Res_Sem"].toString(),
"",
"",
row["Res_Batch"],
row["Res_Courses"],
row["Res_Grade"],
row["Res_GPA"].toString(),
row["Res_CGPA"],
row["Res_Status"],
row["faculty_desc_e"],
row["major_desc_e"]),
resultslist.add(resultsModelClass)
});
if (this.mounted) {
setState(() {});
}
}
fetchCourse() async {
final allRows = await DbHelper.mydb.getResultcoursesList();
allRows.forEach((row) => {
courseModelClass = CoursesModelClass(
row["Res_Courses"],
row["Res_Grade"],
),
coursesist.add(courseModelClass)
});
if (this.mounted) {
setState(() {});
}
}
List<String> listHeader = ['Student Results Details'];
Widget _resultwidget(context) {
final _size = MediaQuery.of(context).size;
return resultslist.length == 0
? ListView.builder(
controller: ScrollController(),
itemCount: 1,
itemBuilder: (context, index) {
return Center(
child: Padding(
padding: EdgeInsets.only(top: _size.height * 0.4),
child: Text(
"No Result Details !",
style: TextStyle(fontSize: 20),
),
));
},
physics: AlwaysScrollableScrollPhysics(),
)
: new ListView.builder(
itemCount: listHeader.length,
itemBuilder: (context, index) {
//var _size = MediaQuery.of(context).size;
return SingleChildScrollView(
child: Center(
child: Padding(
padding: EdgeInsets.only(bottom: _size.height * 0.02),
child: Column(
children: [
Card(
elevation: 20,
margin: EdgeInsets.symmetric(
horizontal: _size.width * 0.005,
),
child: Container(
height: 50.0,
decoration: BoxDecoration(
color: Theme.of(context).backgroundColor),
child: Center(
child: Text(
'Semester ' +
resultslist[0].Res_Sem +
' Result ',
style: TextStyle(
color: Colors.white,
fontSize: 17,
fontWeight: FontWeight.bold),
),
),
),
),
],
))),
);
},
);
}
the refresh method
Future<String> refreshList() async {
refreshKey.currentState?.show(atTop: false);
//await Future.delayed(Duration(seconds: 10));
var studentIndex;
final allRows = await DbHelper.mydb.MainInfoCehck();
allRows.forEach((row) => {
row["Stud_Index"],
studentIndex = row["Stud_Index"].toString()
//print( row["Stud_Index"].toString())
});
print(studentIndex);
//void refreshResult(String studentIndex)async{
await dataApiProvider.refresh_result(studentIndex);
// }
if (this.mounted) {
setState(() {
coursesist.clear();
resultslist.clear();
fetchResultDetails();
fetchCourse();
});
}
return null;
}
Future<String> refresh_result(String studentIndex) async {
var url = main_urll + "?studentIndex=" + studentIndex.trim();
final response = await http.get(url);
List<ResultDetailsModel> ResulttDetailsListModel = [];
final resultInfo = jsonDecode(response.body)['result_info'];
DbHelper.mydb.deleteTableData("Result");
print('Result informations : ' + resultInfo.toString());
for (Map i in resultInfo) {
ResulttDetailsListModel.add(ResultDetailsModel.fromJson(i));
DbHelper.mydb.saveResultDetails(ResultDetailsModel.fromJson(i));
}
return "";
}
Please check if
resultslist == null or resultslist.isNotEmpty()
for Handling empty list
When I'm trying to remove item(like 0 index item) from this listview using provider it removed the last item from the list. while I'm deleting last element from the list successfully remove last item. I'm bit confusing why this kind of problem is happening to me.
Here I'm posting some code please check give your valuable suggestion. Also demonstrate on this video what issue is happening
Link:https://drive.google.com/file/d/1UYl8Z7vEj_tZCaYzqe0VqZL2iMla5nIZ/view?usp=sharing
Expected result: Whenever user press delete button then delete that particular row(item).
Delete method:- This is the delete method It'll be call when user press delete button from the list.
Future<void> acceptdeclinerequest(String requestStatus,int requestId) async{
String token = await CustomPreferences.getpreferences('token');
Map<String, String> requestHeaders;
if (token.isNotEmpty) {
requestHeaders = {
'Accept': 'application/json',
'Authorization': 'Bearer ' + token
};
} else {
requestHeaders = {
'Accept': 'application/json',
};
}
var reqdata = {
"request_id":requestId.toString(),
"status":requestStatus
};
print('accept request data is $reqdata');
try
{
final response =
await http.post(Connection.url + 'respond-place-request', headers: requestHeaders,body: reqdata);
if (response.statusCode == 200) {
Map<String, dynamic> responseJson = json.decode(response.body);
final existingProductIndex = _items.indexWhere((prod) => prod.id == requestId);
var existingProduct = _items[existingProductIndex];
_items.removeAt(existingProductIndex);
notifyListeners();
return responseJson;
} /*else if (response.statusCode == 500) {
return servererrorresponse;
}*/
} catch (exception) {
throw exception;
}
}
Main Widget class: This is the main widget class where I define Listview widget. I've used provider to get data from api which is written in modal class to populate in Listview and Listview child widgets is in seprate class which is RequestWidgets. In this class I've passed rowitems data to show in listview.
GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
var connectionstatus;
var product;
var _isInit = true;
var _isLoading = false;
#override
void initState() {
super.initState();
}
#override
void didChangeDependencies() {
// TODO: implement didChangeDependencies
if (_isInit) {
setState(() {
_isLoading = true;
});
Provider.of<BandRequestModal>(context).getBandRequestList().then((_) {
setState(() {
_isLoading = false;
});
});
}
_isInit = false;
super.didChangeDependencies();
}
#override
Widget build(BuildContext context) {
connectionstatus = Provider.of<ConnectivityResult>(context);
product = Provider.of<BandRequestModal>(context, listen: false);
// getRequestData();
return WillPopScope(
onWillPop: _onWillPop,
child: Scaffold(
key: _scaffoldKey,
appBar: CustomAppbar(
_scaffoldKey, Constants.requests, 100.0, filterRecord),
endDrawer: MenuDrawer(),
body:
/*(connectionstatus == ConnectivityResult.wifi ||
connectionstatus == ConnectivityResult.mobile)
? */
Consumer<BandRequestModal>(builder: (context, modal, child) {
return !_isLoading
? Container(child: LayoutBuilder(builder:
(BuildContext context, BoxConstraints constraints) {
return Container(
height: constraints.maxHeight,
child: modal.item.length > 0
? ListView.builder(
padding:
EdgeInsets.only(top: 10.0, bottom: 0.0),
itemCount: modal.item.length,
shrinkWrap: true,
// physics: NeverScrollableScrollPhysics(),
itemBuilder: (context, int i) {
return RequestWidgets(data: modal.item[i]);
})
: Center(
child: new Text(
Constants.norecordfound,
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold),
),
),
// ],
// ),
);
}))
: Comman.loadingIndicator(Theme.of(context).primaryColor);
})
// : Comman.nointernetconnection(context)
// FutureBuilder<BandRequestModal>(
// future: Connection.bandRequestList(),
// builder: (context, snapshot) {
// switch (snapshot.connectionState)
// {
// case ConnectionState.none:
// break;
// case ConnectionState.waiting:
// return Comman.loadingIndicator(
// Theme.of(context).primaryColor);
// break;
// case ConnectionState.active:
// break;
// case ConnectionState.done:
// if (snapshot.hasError) {
// return Center(
// child: new Text(Constants.servererror),
// );
// }else if(snapshot.data==null){
// return Center(
// child: new Text(Constants.servererror),
// );
// } else if (snapshot.data.data.length == 0) {
// return Center(
// child: new Text(
// Constants.norecordfound,
// style: TextStyle(
// fontSize: 20.0, fontWeight: FontWeight.bold),
// ),
// );
// } else {
// return ListView.builder(
// padding:
// EdgeInsets.only(top: 10.0, bottom: 60.0),
// itemCount: snapshot.data.data.length,
// shrinkWrap: true,
// physics: NeverScrollableScrollPhysics(),
// itemBuilder: (context, int i) {
// return RequestWidgets(data:snapshot.data.data[i]);
// });
// }
// break;
// }
// }):Comman.nointernetconnection(context)
));
}
Child widget class: This is the row items class of listview In this class we used many widgets to show place data.
class _RequestWidgetsState extends State<RequestWidgets> {
var getData;
var product;
#override
void initState() {
// TODO: implement initState
getData = widget.data;
super.initState();
}
#override
Widget build(BuildContext context) {
product = Provider.of<BandRequestModal>(context, listen: false);
return Container(
// alignment: Alignment.topLeft,
margin: EdgeInsets.only(top: 5.0),
child: ListTile(
// contentPadding: EdgeInsets.zero,
key: ObjectKey(getData),
leading: CircleAvatar(
radius: 30,
backgroundColor: Colors.transparent,
child: ClipOval(
child: (getData.placeDetails.image != null &&
getData.placeDetails.image != '')
? Image.network(
getData.placeDetails.image,
width: 90,
height: 90,
fit: BoxFit.cover,
)
: Image.asset(
Res.defaultImage,
width: 90,
height: 90,
fit: BoxFit.cover,
)),
),
title: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Expanded(
child: Text(getData.placeDetails.name,
style: TextStyle(
fontSize: 16.0,
fontFamily: 'Metropolis',
color: CustomColors.commentTitleColor))),
],
),
subtitle: Container(
margin: EdgeInsets.only(top: 1.0),
child: Column(children: <Widget>[
Container(
margin: EdgeInsets.only(top: 1.0),
child: Row(children: <Widget>[
Expanded(
child: Text(getData.placeDetails.address,
style: TextStyle(
fontSize: 15.0,
height: 1.2,
fontFamily: 'Metropolis',
color: CustomColors.commentSubtitleColor))),
]),
),
Container(
margin: EdgeInsets.only(top: 15.0, bottom: 15.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[],
)),
Divider(
color: CustomColors.commentlineColor,
thickness: 0.8,
)
])),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
GestureDetector(
child: CircleAvatar(
radius: 20,
backgroundColor: Colors.green,
child: Icon(
Icons.check,
color: Colors.white,
),
),
onTap: () {
acceptrejectpopup('1');
// {
// print('accept data $data');
// Comman.hideLoading(context);
// Comman.showSnakBar(data['message'],context);
// });
},
),
SizedBox(
width: 15.0,
),
GestureDetector(
child: CircleAvatar(
backgroundColor: Colors.red,
child: Icon(
Icons.clear,
color: Colors.white,
),
),
onTap: () {
// Comman.showLoading(context);
acceptrejectpopup('0');
/*product.acceptdeclinerequest('0',getData.id.toString()).then((data){
print('decline data $data');
Comman.hideLoading(context);
Comman.showSnakBar(data['message'],context);
});*/
},
)
],
),
),
);
}
//accept and reject
void acceptRejectRequest(String requestStatus) async {
try {
var response =
await product.acceptdeclinerequest(requestStatus, getData.id);
if (response['status'] == Constants.status_true) {
Comman.hideLoading(context);
Comman.showSnakBar(response['message'], context);
// setState(() {});
} else {
Comman.hideLoading(context);
}
} catch (exception) {
Comman.hideLoading(context);
Comman.showSnakBar(Constants.servererror, context);
}
}
//request accept/reject popup
Future<void> acceptrejectpopup(String reqStatus) {
return showDialog(
context: context,
builder: (context) => new AlertDialog(
title: new Text('Alert!',
style: TextStyle(color: Colors.black, fontWeight: FontWeight.bold)),
content: new Text(reqStatus == '1'
? Constants.reqAcceptmessage
: Constants.reqRejectemessage),
actions: <Widget>[
new FlatButton(
onPressed: () => Navigator.of(context).pop(),
child: new Text(Constants.notxt),
),
new FlatButton(
onPressed: () {
Navigator.of(context).pop();
Comman.showLoading(context);
acceptRejectRequest(reqStatus);
},
child: new Text(Constants.yestxt),
),
],
),
);
}
The provider is working just fine, the problem is when the provider notify the consumer the ListView updates the children, but the StatefulWidget check they're the same type (They're all RequestWidget) so they just update themselves (if you don't provide a key to the StatefulWidget they will try to check if they're the same element and update via the didChangeDependencies method), but you're updating the getData var in initState (which will call only once) so even if the consumer updates the value won't. Try it like this
#override
void initState() {
// TODO: implement initState
//getData = widget.data; not here
super.initState();
}
#override
void didChangeDependencies() {
// TODO: implement initState
getData = widget.data; //update it here
super.didChangeDependencies();
}
Other option would be just to give a specific key when building your widget in the itemBuilder so when the consumer updates it changes them accordingly
return RequestWidgets(key: ValueKey(modal.item[i].id),data: modal.item[i]);
// Or some value unique for each item
The problem here I guess is, in your Child widget class, since I can't see any requestId of the selected card being passed to the acceptdeclinerequest().
Your acceptdeclinerequest() expects two unique arguments to be passed when called:
String requestStatus
int requestId
If you look closely into the Child widget class, you are just passing requestStatus. I wonder from where are you getting this getData.id, and how is it identifying that some particular card is selected.
// look here, only requestStatus is being passed
onTap: () {
acceptrejectpopup('0');
}
// and here
onTap: () {
acceptrejectpopup('1');
}
And in your acceptRejectRequest, you are only passing requestStatus
acceptRejectRequest(reqStatus);
And then you call your acceptdeclinerequest() with this data
// machine is confused, where are we getting the getData.id
// it assumes the id as per it's need, hence the error
await product.acceptdeclinerequest(requestStatus, getData.id);
The machine is trying to figure out, which element you selected. Try to give the id from the selected card, and pass it to the method with correct getData.id of that particular element.
Suggestion: Pass in your id of the selected card when you are tapping on it, and then call your methods, and then pass it along to get the right requestId and remove it. Let your methods acceptrejectpopup() and acceptRejectRequest() accept the id of the selected item and then finally pass it to your acceptdeclinerequest()
// first step
onTap: () => acceptrejectpopup('0', your_card_reuqest_id);
// second step, pass data from the above popup method to acceptRejectRequest()
acceptRejectRequest(reqStatus, requestId);
//finally from acceptRejectRequest(reqStatus, requestId), pass it to the final method acceptdeclinerequest
acceptdeclinerequest(requestStatus, requestId);
I am new in Flutter. Currently learning and developing a flutter project. Here is my code. But my list view is not updating. Advance thanks for pointing out any mistake
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import 'package:stacked/stacked.dart';
import 'package:sx_tvapp_app/data/network/models/favourite_item.dart';
import 'package:sx_tvapp_app/ui/views/favourite_items/favourite_items_viewmodel.dart';
class FavouriteItemsView extends StatefulWidget {
final FavouriteItemsViewType type = FavouriteItemsViewType.one;
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return _FavouriteItemsViewState();
}
}
class _FavouriteItemsViewState extends State<FavouriteItemsView> {
final String title = 'お気に入り';
List<String> items = ['A', 'B', 'C', 'D', 'E', 'F'];
FavouriteItemPage page;
List<FavouriteItemContent> contents = List();
bool isLoading = false;
FavouriteItemsViewModel viewModel;
void showLoading() {
setState(() {
isLoading = true;
});
}
Future loadData() async {
print('loadData');
print(contents.length.toString());
if (page == null) {
this.viewModel.getFavouriteItemForLoggedOutUser();
showLoading();
} else {
if (page.totalPages > page.number) {
this.viewModel.getFavouriteItemForLoggedOutUser(page: page.number + 1);
showLoading();
}
}
}
void bindModel(FavouriteItemsViewModel viewModel) {
viewModel.pageSubject.listen((value) {
print(value);
page = value;
});
viewModel.favouriteItemSubject.listen((value) {
print(' content is going to be added');
print(value.contents.length);
setState(() {
// contents.addAll(value.contents);
for (int i = 0; i < value.contents.length; i++) {
var commonItem = contents.where((element) {
return element.id == value.contents[i].id;
}).toList();
if (commonItem.length == 0) {
print('item is being addedf');
contents.add(value.contents[i]);
}
}
// contents = contents.toSet().toList();
isLoading = false;
});
});
}
#override
Widget build(BuildContext context) {
// TODO: implement build
return ViewModelBuilder<FavouriteItemsViewModel>.reactive(
viewModelBuilder: () => GetIt.instance.get<FavouriteItemsViewModel>(),
onModelReady: (model) {
this.viewModel = model;
this.bindModel(model);
// this.loadData();
model.getFavouriteItemForLoggedOutUser();
},
builder: (context, viewModel, child) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Column(
children: <Widget>[
Expanded(
child: NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification scrollInfo) {
if (!isLoading &&
scrollInfo.metrics.pixels ==
scrollInfo.metrics.maxScrollExtent) {
loadData();
}
},
child: buildListView(),
),
),
Container(
height: isLoading ? 50.0 : 0,
color: Colors.transparent,
child: Center(
child: new CircularProgressIndicator(),
),
),
],
),
);
},
);
}
Widget buildListView() {
return ListView.builder(
itemCount: contents.length,
padding: EdgeInsets.fromLTRB(0, 9, 0, 9),
itemBuilder: (context, index) {
return buildRow(contents[index]);
});
}
Widget buildRow(FavouriteItemContent content) {
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Container(
width: 15,
height: 0,
),
//TODO: Handle this after null image url issue is fixed
// CachedNetworkImage(
// fit: BoxFit.fill,
// height: 25,
// width: 25,
// imageUrl: content.channelIconUrl,
// placeholder: (context, url) => CircularProgressIndicator(),
// errorWidget: (context, url, error) => new Icon(Icons.error),
// ),
getImage(content.channelIconUrl, 25, 25),
Container(
width: 9,
height: 0,
),
Container(
child: Text(
content.name,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
letterSpacing: -0.25,
color: Color.fromRGBO(96, 96, 96, 1.0),
),
),
),
],
),
Container(
width: 0,
height: 30,
),
//TODO: Handle this after null image url issue is fixed
// CachedNetworkImage(
// fit: BoxFit.fill,
// height: 211,
// width: double.infinity,
// imageUrl: content.imageUrl,
// placeholder: (context, url) => CircularProgressIndicator(),
// errorWidget: (context, url, error) => new Icon(Icons.error),
// ),
getImage(content.imageUrl, double.infinity, 211),
Container(
width: 0,
height: 13,
),
Padding(
padding: EdgeInsets.fromLTRB(15, 12.5, 15, 9),
child: Text(content.details),
)
],
);
}
Widget getImage(String url, double width, double height) {
if (url != null) {
return CachedNetworkImage(
imageUrl: url,
placeholder: (context, url) => CircularProgressIndicator(),
errorWidget: (context, url, error) => Image(
image: AssetImage(
'assets/images/product_detail/product_detail_placeholder.png'),
),
);
} else {
return Image(
width: width,
height: height,
image: AssetImage(
'assets/images/product_detail/product_detail_placeholder.png'),
);
}
}
}
enum FavouriteItemsViewType { one, two, three, four }
The thing I do here is, I request for a get API at the beginning which gives me data for the first page. Then I request again after scrolling down to the bottom. This is a ListView with pagination.
What you would likely want to use is a StreamBuilder which will rebuild whatever it contains when new data arrives from the stream. Streambuilder will become your go to for most data in flutter.
https://api.flutter.dev/flutter/widgets/StreamBuilder-class.html
Here is my code:
class CategoryHomeScreen extends StatefulWidget {
#override
_CategoryHomeScreenState createState() => _CategoryHomeScreenState();
}
class _CategoryHomeScreenState extends State<CategoryHomeScreen> {
List<CategoriesOnly> categoriesOnlyList =[];
List<CategoryItems> categoryItemList = [];
#override
void initState() {
// TODO: implement initState
getCategoriesName();
super.initState();
}
Future<void> getCategoriesName() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
var userPin = prefs.getString('pin');
var CategoryName = FirebaseDatabase.instance.reference().child('CategoryNames').child(userPin).once()
.then((DataSnapshot dataSnapshot){
var key = dataSnapshot.value.keys;
for(var i in key)
{
// print(dataSnapshot.value[i]['Name']);
CategoriesOnly categoriesOnly = new CategoriesOnly(
dataSnapshot.value[i]['Name']
);
categoriesOnlyList.add(categoriesOnly);
}
});
var categoryItemDetails = FirebaseDatabase.instance.reference().child('Categories').child(userPin).once()
.then((DataSnapshot dataSnapshot){
var key = dataSnapshot.value.keys;
for(var i in key)
{
CategoryItems categoryItems = new CategoryItems(
dataSnapshot.value[i]['CategoryName'],
dataSnapshot.value[i]['MarketPrice'],
dataSnapshot.value[i]['Name'],
dataSnapshot.value[i]['OurPrice'],
dataSnapshot.value[i]['TotalDiscount'],
dataSnapshot.value[i]['Weight']
);
categoryItemList.add(categoryItems);
}
});
}
#override
Widget build(BuildContext context) {
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
statusBarColor: Colors.black, //or set color with: Color(0xFF0000FF)
));
return Scaffold(
backgroundColor: Colors.black,
body: SafeArea(
child:
ListView.builder(
itemCount: categoriesOnlyList.length,
itemBuilder: (context, index1) =>
Column(children: [
Text(categoriesOnlyList[index1].Name,style: TextStyle(color:Colors.white),),
Container(
height: 200,
decoration: BoxDecoration(borderRadius: BorderRadius.circular(8)),
child:
ListView.builder(itemCount: categoryItemList.length,
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {
return categoriesOnlyList[index1].Name == categoryItemList[index].CategoryName?
Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
height: 200,
width: 200,
decoration: BoxDecoration(borderRadius: BorderRadius.circular(8)),
child: Card(
color: Colors.white,
child: Text(categoryItemList[index]
.Name,style: TextStyle(),overflow: TextOverflow.ellipsis,)),
),
):Container(
);
}),
),
]),
)
),
);
}
}
This is my app where I'm loading data from firebase realtime database. When I install the app it doesn't show any data but when I reload(hot reload) the app it shows all the data and when I quit the app and again launch it I'm not able to see the data again. But I want my page to show some loading widget until it loads the data and then return the page. Or simple the page with data.
Likely because categoryItemList.length is 0.
you can check this using the debugger or display something when the length is 0.
class CategoryHomeScreen extends StatefulWidget {
#override
_CategoryHomeScreenState createState() => _CategoryHomeScreenState();
}
class _CategoryHomeScreenState extends State<CategoryHomeScreen> {
List<CategoriesOnly> categoriesOnlyList = [];
List<CategoryItems> categoryItemList = [];
#override
void initState() {
// TODO: implement initState
getCategoriesName();
super.initState();
}
Future<void> getCategoriesName() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
var userPin = prefs.getString('pin');
var CategoryName = FirebaseDatabase.instance
.reference()
.child('CategoryNames')
.child(userPin)
.once()
.then((DataSnapshot dataSnapshot) {
var key = dataSnapshot.value.keys;
for (var i in key) {
// print(dataSnapshot.value[i]['Name']);
CategoriesOnly categoriesOnly =
new CategoriesOnly(dataSnapshot.value[i]['Name']);
categoriesOnlyList.add(categoriesOnly);
}
});
var categoryItemDetails = FirebaseDatabase.instance
.reference()
.child('Categories')
.child(userPin)
.once()
.then((DataSnapshot dataSnapshot) {
var key = dataSnapshot.value.keys;
for (var i in key) {
CategoryItems categoryItems = new CategoryItems(
dataSnapshot.value[i]['CategoryName'],
dataSnapshot.value[i]['MarketPrice'],
dataSnapshot.value[i]['Name'],
dataSnapshot.value[i]['OurPrice'],
dataSnapshot.value[i]['TotalDiscount'],
dataSnapshot.value[i]['Weight']);
categoryItemList.add(categoryItems);
}
});
}
#override
Widget build(BuildContext context) {
SystemChrome.setSystemUIOverlayStyle(
SystemUiOverlayStyle(
statusBarColor: Colors.black, //or set color with: Color(0xFF0000FF)
),
);
return Scaffold(
backgroundColor: Colors.black,
body: SafeArea(
child: ListView.builder(
itemCount: categoriesOnlyList.length, // place breakpoint here
itemBuilder: (context, index1) {
if (categoriesOnlyList == null || categoriesOnlyList.length == 0) {
return CircularProgressIndicator(); // you should see loading animation if list is empty
}
return Column(
children: [
Text(
categoriesOnlyList[index1].Name,
style: TextStyle(color: Colors.white),
),
Container(
height: 200,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
),
child: ListView.builder(
itemCount: categoryItemList.length, // place breakpoint here
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {
if (categoryItemList == null || categoryItemList.length == 0) {
return CircularProgressIndicator(); // you should see loading animation if list is empty
}
return categoriesOnlyList[index1].Name ==
categoryItemList[index].CategoryName
? Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
height: 200,
width: 200,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8)),
child: Card(
color: Colors.white,
child: Text(
categoryItemList[index].Name,
style: TextStyle(),
overflow: TextOverflow.ellipsis,
),
),
),
)
: Container();
},
),
),
],
),
}
),
),
);
}
}
The solution would be to use a FutureBuilder
Word of warning: The application will run the build method multiple times, it's good practice to place anything you don't want to be repeatedly called (like database calls or changing system overlays) outside of the method.
SystemChrome.setSystemUIOverlayStyle(
SystemUiOverlayStyle(
statusBarColor: Colors.black, // or set color with: Color(0xFF0000FF)
),
);