problems when displaying information in a ListView.builder - flutter

I am trying to show some information brought from firebase in web flutter but I have problems implementing FutureBuilder, since it does not show the information, the widget is not rendering anything the page is completely blank, attached I send the complete code of the page that is presenting the problem since I have followed all the tutorials that I have found but I can not make it show the data, in the same way I also attach the Json that returns the complement that I am using to obtain the data from firebase, I hope for the collaboration, thank you very much
Complete Code
class DriverPages extends StatefulWidget {
static final routeName = 'DriverPage';
DriverPages({Key key}) : super(key: key);
#override
_DriverPagesState createState() => _DriverPagesState();
}
DatabaseRef driverRef =
FirebaseDatabaseWeb.instance.reference().child('drivers');
class _DriverPagesState extends State<DriverPages> {
GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
List prueba;
Future<List> driverItem() async {
DatabaseSnapshot snapshot = await driverRef.once();
Map<String, dynamic> json = snapshot.value;
return json.values.toList();
}
Widget driverList() {
return FutureBuilder<List>(
future: driverItem(),
builder: (contetx, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
final value = snapshot.data[index];
return ListTile(
title: Text(value['fullname']),
subtitle: Text(value['email']),
);
},
);
}
},
);
}
#override
Widget build(BuildContext context) {
if (userSnapshot != null) {
return Scaffold(
key: scaffoldKey,
drawer: Container(
width: 320,
color: Colors.white,
child: Drawer(
child: PatimovilMenu(),
),
),
body: Container(
width: double.infinity,
height: double.infinity,
padding: EdgeInsets.symmetric(
vertical: 25,
horizontal: 30,
),
child: Column(
children: <Widget>[
PatimovilHeader(
onPressed: () {
scaffoldKey.currentState.openDrawer();
},
titlePage: 'Driver Manager',
),
Container(
width: MediaQuery.of(context).size.width,
height: (MediaQuery.of(context).size.height) - 260,
alignment: Alignment.center,
child: Center(
child: driverList(),
),
),
PatimovilFooder(),
],
),
),
);
} else {
Navigator.pushNamedAndRemoveUntil(
context,
LoginPage.routeName,
(route) => false,
);
}
}
}
This is the Json that returns the dependency I am using
{VOVQv28SU2c0GgXJKcSp8UFchPz2:
{email: bibiana206#gmail.com, fullname: bibiana, identification: e8104970, phone: 2095770, status: Waiting,
vehicle_details:
{car_brand: Ferrari, car_color: rojo, car_model: cretta, car_plate: az12}
},
dBncphEFOZbzGEt5qn44sD9BYVK2:
{email: bolivia20192019#gmail.com, fullname: Gustavo Barrios, identification: 14326048, phone: 60697350, status: Active,
vehicle_details:
{car_brand: Renault, car_color: Gris, car_model: Duster, car_plate: AY6787,}
}
}
I can't get the data to show me, it doesn't render anything

You should check the current state of the future as there are four connection states:
ConnectionState.none
ConnectionState.waiting
ConnectionState.active
ConnectionState.done
You were returning a widget only when snapshot.hasData which will occur only when the connection state is done & there are no errors.
In all other cases, your method driverList will be returning nothing.
So, update your method as follows:
Widget driverList() {
return FutureBuilder<List>(
future: driverItem(),
builder: (contetx, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasError) {
return Center(child: Text('Something went wrong'));
}
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
final value = snapshot.data[index];
return ListTile(
title: Text(value['fullname']),
subtitle: Text(value['email']),
);
},
);
}
}
return Center(child: CircularProgressIndicator());
},
);
}

Related

Widget should be refresh when its data belongs to StreamBuilder's stream in flutter

I am creating a simple EXPENSE MANAGER app
I have divided screen in two section
Top Section for showing Two card of TotalIncome and TotalExpense
and other section is showing All Transactions
Here, I have taken Streambuilder for showing all transaction, and with the help of this stream builder I have created Tow Global Variable totalincome and totalexpense
and showing total income and totalexpense to top section's Card
When I add any transaction, List of transaction refresh properly as it is due to Stream Builder but total income and expense card not refreshing...
here I want the proper way to do it...(
like creating a method that fetch records from firebase and store into a List and to use this list for various needs...
here Is my code
Widget headerSummary(Size size) {
return Container(
height: size.height * 0.15,
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.only(
bottomRight: Radius.circular(30), bottomLeft: Radius.circular(30)),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: SummaryCard(
color: Colors.green,
amount: totalincome.toString(),
icondata: Icons.arrow_upward,
title: 'Income',
),
),
Expanded(
child: SummaryCard(
color: Colors.red,
amount: totalexpense.toString(),
icondata: Icons.arrow_downward,
title: 'Expense',
),
),
],
),
);
}
transaction
Widget showTransactions(Size size) {
return Container(
height: size.height * .65,
// color: Colors.red,
child: StreamBuilder(
stream: FirebaseFirestore.instance
.collection('users')
.doc(widget.loggeduser.userid)
.collection('expenses').orderBy("date",descending: true)
.snapshots(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
if (snapshot.hasData) {
QuerySnapshot querysnapshot =
snapshot.data as QuerySnapshot;
if (querysnapshot.docs.length > 0) {
List<Map<String, dynamic>> transactionlist = [];
for (int x = 0; x < querysnapshot.docs.length; x++) {
Map<String, dynamic> expensemap = querysnapshot.docs[x]
.data() as Map<String, dynamic>;
transactionlist.add(expensemap);
}
var x=transactionlist.where((element) => element['isexpense']==true).toList();
totalexpense=x.fold(0, (previousValue, element) => previousValue+element['amount']);
var y=transactionlist.where((element) => element['isexpense']==false).toList();
totalincome=y.fold(0, (previousValue, element) => previousValue+element['amount']);
//I have edited this lines...
return ListView.builder(
//reverse: true,
padding: EdgeInsets.symmetric(vertical: 10),
itemCount: transactionlist.length,
itemBuilder: (context, index) {
final trans=TransactionModel.fromjson(transactionlist[index]);
print(trans.toString());
return TransactionCard(
amount: trans.amount.toStringAsFixed(2),
datetime: trans.date.toString(),
paymentby: trans.paymentmode,
category: trans.category.title,
categoryicon: trans.category.iconurl,
isexpense: trans.isexpense,
);
});//listview end
} else {
return Container(
child: Center(
child: Text('No Transaction Found...')));
}
} else {
if (snapshot.hasError) {
return Text('error found');
} else {
return Text('empty..');
}
}
} else {
return Center(child: CircularProgressIndicator());
}
}),
);
}
StreamBuilder will refresh its child UI, not the upper widget.
You can use ValueNotifier with ValueListenableBuilder. This snippet will help you to clear the concept.
class MyHomePage extends StatefulWidget {
MyHomePage({
Key? key,
}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
int? myGlobalValue1;
ValueNotifier<int?> globalValueNotifier = ValueNotifier(null);
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("myGlobalValue1 $myGlobalValue1"),
ValueListenableBuilder<int?>(
valueListenable: globalValueNotifier,
builder: (context, value, child) {
return Text("globalValueNotifier $value");
}),
StreamBuilder<int>(
stream: Stream<int>.periodic(const Duration(seconds: 1), (x) => x)
.take(15),
builder: (BuildContext context, AsyncSnapshot snapshot) {
myGlobalValue1 = snapshot.data;
WidgetsBinding.instance.addPostFrameCallback((_) {
globalValueNotifier.value = snapshot.data; // to skip initOn frame build
});
return Column(
children: [
Text(
snapshot.data != null ? snapshot.data.toString() : "0",
),
Text("myGlobalValue1 inside streamB $myGlobalValue1"),
],
);
},
)
],
),
),
);
}
}
You could move StreamBuilder further up in the widget tree, so that showTransactions and headerSummary get built within the Stream Builder.
If you want to keep the layout as it is, then you could look into ValueNotifier to update Income and Expense variables when stream builder has an update.

flutter/dart: streambuilder inside list view not scrolling

Is there anyway to making a streambuilder inside a list view scrollable?
I tried wrapping the form on a column widget but its not working out. none of the information is displayed on the screen for some reason.
here is a mini video of the issue:: https://youtu.be/_GIZkwzeH0Y
is there a different way to display this information?
return Scaffold(
appBar: AppBar(
title: const Text('Job details'),
),
body: FutureBuilder(
future: createOrGetExistingJob(context),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.done:
_setupTextControllerListener();
// at this point we want to start to listening for changes
return Form(
key: _formKey,
child: ListView(
padding: const EdgeInsets.all(32.0),
children: [
getStateChevrons(_jobState),
const Divider(
height: 20,
thickness: 5,
indent: 0,
endIndent: 0,
color: Colors.blue,
),
//
//
//
// Below is the streambuilder I would like to correctly display
getStateDependentButtons(context),
_jobDocumentId != null && _jobState != jobStateNew
? StreamBuilder(
stream: _jobsService
.getJobApplication(_jobDocumentId as String),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
case ConnectionState.active:
if (snapshot.hasData) {
final allJobApplications = snapshot.data
as Iterable<CloudJobApplication>;
return JobApplicationsListView(
jobApplications: allJobApplications,
onTap: (job) {
Navigator.of(context).pushNamed(
myJobApplicationsRoute,
arguments: job,
);
},
);
} else {
return const CircularProgressIndicator();
}
default:
return const CircularProgressIndicator();
}
},
)
: const Text(
'Once job is submitted, job applications will be available')
]
.map((child) => Padding(
padding: EdgeInsets.symmetric(vertical: 8.0),
child: child,
))
.toList(),
),
);
default:
return const CircularProgressIndicator();
}
},
),
//
//
);
Here is what jobApplicationListView looks like::
import 'package:flutter/material.dart';
import '../../services/cloud/cloud_job_application.dart';
import '/services/cloud/cloud_job.dart';
import '/utilities/dialogs/delete_dialog.dart';
/*
source: https://www.youtube.com/watch?v=VPvVD8t02U8&t=59608s
class creation :: 22:02:54
*/
typedef JobCallback = void Function(CloudJobApplication jobApplication);
class JobApplicationsListView extends StatelessWidget {
final Iterable<CloudJobApplication>
jobApplications; // list of jobApplications
//final JobCallback onDeleteJob;
final JobCallback onTap;
const JobApplicationsListView({
Key? key,
required this.jobApplications,
//required this.onDeleteJob,
required this.onTap,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return ListView.builder(
shrinkWrap: true,
itemCount: jobApplications.length,
itemBuilder: (context, index) {
final jobApplication = jobApplications.elementAt(index);
return ListTile(
onTap: () {
onTap(jobApplication);
},
title: Text(
jobApplication.jobApplicationDescription,
maxLines: 1,
softWrap: true,
overflow: TextOverflow.ellipsis,
),
trailing: IconButton(
onPressed: () async {
final shouldDelete = await showDeleteDialog(context);
if (shouldDelete) {
"onDeleteJob(job)";
}
},
icon: const Icon(Icons.delete),
),
);
},
);
}
}
On your JobApplicationsListView's ListView.builder( set physics: NeverScrollableScrollPhysics(), The parent already handling scroll event.
#override
Widget build(BuildContext context) {
return ListView.builder(
physics: NeverScrollableScrollPhysics(),
itemCount: jobApplications.length,
itemBuilder: (context, index) {
final jobApplication = jobApplications.elementAt(index);
Also you can try with primary: false, on it.
Or you can use Column widget instead.

flutter listview builder inside a listview builder

I don't have much experience with flutter.
I would like to use the language_tool library (https://pub.dev/packages/language_tool) for Dart and Flutter.
To show the data obtained from the tool() function, I created a FutureBuilder with a ListView.builder inside, which returns a Column.
I would like there to be 2 children inside the column:
1- a Text with mistake.issueDescription as text (for each "mistake")
2- another ListView that returns the elements of the List mistake.replacements for each "mistake"
Anyone know how I can fix it?
Below I put the code I created, which works fine until I put the Listview builder inside the first ListView builder.
import 'package:flutter/material.dart';
import 'package:language_tool/language_tool.dart';
void main() => runApp(mainApp());
class mainApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: Chat(),
);
}
}
class Chat extends StatefulWidget {
const Chat({Key? key}) : super(key: key);
#override
_ChatState createState() => _ChatState();
}
class _ChatState extends State<Chat> {
String text = 'Henlo i am Gabriele';
Future<List<WritingMistake>> tool(String text) async {
var tool = LanguageTool();
var result = tool.check(text);
var correction = await result;
List<WritingMistake> mistakes = [];
for (var m in correction) {
WritingMistake mistake = WritingMistake(
message: m.message,
offset: m.offset,
length: m.length,
issueType: m.issueType,
issueDescription: m.issueDescription,
replacements: m.replacements,
);
mistakes.add(mistake);
}
print(mistakes.length);
print(mistakes);
return mistakes;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
children: [
Container(
color: Colors.red,
height: 150.0,
width: double.infinity,
child: Center(
child: Text(text, style: const TextStyle(fontSize: 20.0))),
),
FutureBuilder(
future: tool(text),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.data == null) {
return const Center(
child: Text('Loading...'),
);
} else {
return SizedBox(
height: 200.0,
child: ListView.builder(
itemCount: snapshot.data.length,
itemBuilder:
(BuildContext context, int mistakeIdIndex) {
return Column(
children: [
Text(snapshot
.data[mistakeIdIndex].issueDescription),
// this is where the problems begin
ListView.builder(
itemCount: snapshot.data[mistakeIdIndex]
.replacements.length,
scrollDirection: Axis.horizontal,
itemBuilder:
(BuildContext context, int index) {
return Text(snapshot.data[mistakeIdIndex]
.replacements[index]);
}),
],
);
}),
);
}
}),
],
),
),
);
}
}
I hope I was clear and that someone can help me.
Thank you :)
You cannot give a listview-builder as a child for a column try changing the Column widget to a ListView and set its shrinkWrap property to true.
ListView(
children: [
Container(
color: Colors.red,
height: 150.0,
width: double.infinity,
child: Center(
child: Text(text, style: const TextStyle(fontSize: 20.0))),
),
FutureBuilder(
future: tool(text),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.data == null) {
return const Center(
child: Text('Loading...'),
);
} else {
return SizedBox(
height: 200.0,
child: ListView.builder(
shrinkWrap:true,
itemCount: snapshot.data.length,
itemBuilder:
(BuildContext context, int mistakeIdIndex) {
return ListView(
shrinkWrap:true,
children: [
Text(snapshot
.data[mistakeIdIndex].issueDescription),
// this is where the problems begin
ListView.builder(
shrinkWrap:true,
itemCount: snapshot.data[mistakeIdIndex]
.replacements.length,
scrollDirection: Axis.horizontal,
itemBuilder:
(BuildContext context, int index) {
return Text(snapshot.data[mistakeIdIndex]
.replacements[index]);
}),
],
);
}),
);
}
}),
],
),
),

Passing data to another screen with Flutter Provider

I'm trying to pass the data to another screen using Provider, but it seems I'm always passing on the same data unless I sort the List and then pass the different data (meaning I'm probably switching the index by sorting the list so that is why it's passing different data now). In short, I call the API, populate the list, setting up the provider too for the next page, and on click I list out the the information from the previous screen, but the problem is I display the same item always unless I sort the list. Here is the code:
Calling the API and displaying the list:
var posts = <RideData>[];
var streamController = StreamController<List<RideData>>();
#override
void initState() {
_getRideStreamList();
super.initState();
}
_getRideStreamList() async {
await Future.delayed(Duration(seconds: 3));
var _vehicleStreamData = await APICalls.instance.getRides();
var provider = Provider.of<RideStore>(context, listen: false);
posts = await _vehicleStreamData
.map<RideData>((e) => RideData.fromJson(e))
.toList();
streamController.add(posts);
provider.setRideList(posts, notify: false);
}
bool isSwitched = true;
void toggleSwitch(bool value) {
if (isSwitched == false) {
posts.sort((k1, k2) => k1.rideId.compareTo(k2.rideId));
} else {
posts.sort((k1, k2) => k2.rideId.compareTo(k1.rideId));
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: SafeArea(
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(
children: [
TextButton(
child: Text('sort ascending'),
onPressed: () {
setState(() {
toggleSwitch(isSwitched = !isSwitched);
});
}),
Container(
height: 1000,
child: StreamBuilder<List<RideData>>(
initialData: posts,
stream: streamController.stream,
builder: (context, snapshot) {
return ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return Column(
children: [
Row(
children: [
Padding(
padding: const EdgeInsets.only(left: 15.0),
child: Text(
'Ride #${snapshot.data[index].rideId}',
),
),
FlatButton(
textColor: Colors.blue[700],
minWidth: 0,
child: Text('View'),
onPressed: () {
// here is where I pass the data to the RideInfo screen
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => RideInfo(
rideId: snapshot
.data[index].rideId,
)));
},
),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
'${snapshot.data[index].pickupTime}',
),
Text(
'${snapshot.data[index].jobArrived}',
),
],
),
],
);
},
);
}),
),
],
),
),
),
);
}
After pressing the View button and passing the data to another screen (RideInfo):
class RideInfo extends StatefulWidget {
static const String id = 'ride_info_screen';
String rideId;
RideInfo({#required this.rideId});
#override
_RideInfoState createState() => _RideInfoState();
}
class _RideInfoState extends State<RideInfo> {
String rideID = '';
#override
void initState() {
super.initState();
setState(() {
rideID = widget.rideId;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
title: Text(
'Ride #$rideID',
),
),
body: SafeArea(
child: SingleChildScrollView(
child: Consumer<RideStore>(
builder: (context, rideStore, child) {
return Column(
children: [
ListView.builder(
shrinkWrap: true,
itemCount: 1,
itemBuilder: (context, index) {
RideData rides = rideStore.getRideByIndex(index);
return Column(
children: [
Expanded(
flex: 2,
child: Column(
children: [
Text(
"PICK UP",
),
// here I display the pickUpTime but it is always the same and I wanted to display the time based on the ID
Text(
'${rides.pickupTime}AM',
),
],
),
),
],
);
}),
],
);
},
),
),
),
);
}
}
The data (pickUpTime in this case) doesn't change when I press to see the View of a single item, but like I said, when I change the order of the list with the sort method, then I get the different data.
Here is the Provider model:
class RideStore extends ChangeNotifier {
List<RideData> _rideList = [];
List<RideData> get rideList => _rideList;
setRideList(List<RideData> list, {bool notify = true}) {
_rideList = list;
if (notify) notifyListeners();
}
RideData getRideByIndex(int index) => _rideList[index];
int get rideListLength => _rideList.length;
}
How do I display the correct information based on the ID from the List that I pressed and passed in the Ride Info screen so it doesn't give back always the same data? Thanks in advance for the help!
The offending code is in RideInfo:
ListView.builder(
shrinkWrap: true,
itemCount: 1,
itemBuilder: (context, index) {
RideData rides = rideStore.getRideByIndex(index);
The index is always 1, so you are always showing the first RideData. There are various options to fix it, e.g. pass the index, or even pass the RideData, to the RideInfo constructor:
class RideInfo extends StatefulWidget {
static const String id = 'ride_info_screen';
String rideId;
final int index;
RideInfo({#required this.rideId, #required this.index, Key key})
: super(key: key) {
and:
RideData rides = rideStore.getRideByIndex(widget.index);
I have some additional comments on the code. Firstly, the ListView is serving no purpose in RideInfo, so remove it.
Secondly, there is no need to construct the streamController and to use StreamBuilder in the parent form. Your list is available in the RideStore. So your parent form could have:
Widget build(BuildContext context) {
var data = Provider.of<RideStore>(context).rideList;
...
Container(
height: 1000,
child:
// StreamBuilder<List<RideData>>(
// initialData: posts,
// stream: streamController.stream,
// builder: (context, snapshot) {
// return
ListView.builder(
shrinkWrap: true,
itemCount: data.length,
I hope these comments help.
Edit:
It is simple to edit your code to use FutureBuilder. Firstly, make _getRideStreamList return the data it read:
_getRideStreamList() async {
...
return posts;
}
Remove the call to _getRideStreamList in initState and wrap the ListView in the FutureBuilder that invokes _getRideStreamList:
Container(
height: 1000,
child: FutureBuilder(
future: _getRideStreamList(),
builder: (ctx, snapshot) {
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
} else {
var data = snapshot.data;
return ListView.builder(
...
);
}
},
),
),
This displays the CircularProgressIndicator while waiting for the data.
Note that this is a quick hack - you do not want to read the data everytime that the widget rebuilds. So _getRideStreamList could check if the data has already been read and just return it rather than rereading.

How to load more items to a list when reach the bottom of results flutter

I have the code below which feed a list with 10 results from firebase. In this case it shows only the 10 items, now I wanna, when user gets the bottom of results, it loads more 10 items and add it to the list. I already have the scrollController and it works.. I receive the log "LOAD HERE" when I get the bottom of the results.
My doubt is how to add the new 10 items in the list?
scrollListener() async {
if (scrollController.position.maxScrollExtent == scrollController.offset) {
print('LOAD HERE');
}
}
#override
void initState() {
scrollController.addListener(scrollListener);
super.initState();
}
#override
void dispose() {
scrollController.removeListener(scrollListener);
super.dispose();
}
loadList(submenu ,callback, context, deviceSize){
return FutureBuilder(
future: ctrlLab.loadList(submenu, 10),
builder: (ctx, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
} else if (snapshot.error != null) {
print(snapshot.error);
return Center(child: Text('ERROR!'));
}else {
return GridView.builder(
padding: EdgeInsets.all(10.0),
controller: scrollController,
itemCount: snapshot.data.length,
itemBuilder: (ctx, i) {
Item item = snapshot.data[i];
if (i < snapshot.data.length) {
return Dismissible(
key: UniqueKey(),
direction: DismissDirection.endToStart,
background: Container(
padding: EdgeInsets.all(10.0),
color: Colors.grey[800],
child: Align(
alignment: AlignmentDirectional.centerEnd,
child: Icon(
Icons.delete,
color: Colors.white,
size: 40,
),
),
),
onDismissed: (DismissDirection direction) {
ctrl.onDismissed(callback, item);
},
child: GestureDetector(
child: Card(
elevation: 5.0,
child: Padding(
padding: EdgeInsets.all(10.0),
child: GridTile(
child: Hero(
tag: "${item}",
child: item.imageUrl == null
? setIconLab(item)
: CachedNetworkImage(
fit: BoxFit.cover,
imageUrl: setIconLab(item),
placeholder: (ctx, url) =>
Center(child: CircularProgressIndicator()),
errorWidget: (context, url, error) =>
Image.asset('assets/images/noPhoto.jpg',
fit: BoxFit.cover),
),
),
footer: Container(
padding: EdgeInsets.all(8.0),
color: Colors.white70,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
item.name
),
),
],
),
),
),
),
),
),
);
}
},
gridDelegate: SliverGridDelegateWithFixedCrossAxisCountAndLoading(
itemCount: snapshot.data.length + 1,
crossAxisCount: deviceSize.width < 600 ? 2 : 3,
childAspectRatio: 0.7,
crossAxisSpacing: 10.0,
mainAxisSpacing: 10.0,
),
);
}
},
);
}
Infinite Scrolling in ListView
I have achieved this case by using the local field instead of getting data from firebase. Hope it will give you some idea.
import 'package:flutter/material.dart';
class ListViewDemo extends StatefulWidget {
ListViewDemo({Key key}) : super(key: key);
#override
_ListViewDemoState createState() => _ListViewDemoState();
}
class _ListViewDemoState extends State<ListViewDemo> {
ScrollController controller;
int count = 15;
#override
void initState() {
super.initState();
controller = ScrollController()..addListener(handleScrolling);
}
void handleScrolling() {
if (controller.offset >= controller.position.maxScrollExtent) {
setState(() {
count += 10;
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('List view'),
),
body: ListView.builder(
controller: controller,
itemCount: count,
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text('Item $index'),
);
},
),
);
}
#override
void dispose() {
controller.removeListener(handleScrolling);
super.dispose();
}
}
You have to add another 10 data to the crtLap.loadList(subMenu, 20) and call setState inside the scrollListener to rebuild the widget about the changes.
var data = crtLap.loadList(subMenu, 10);
scrollListener() async {
if (scrollController.position.maxScrollExtent == scrollController.offset) {
setState((){
data = crtLap.loadList(subMenu, 20);
});
}
}
and use this data field to the FutureBuilder directly,
loadList(submenu ,callback, context, deviceSize){
return FutureBuilder(
future: data,
builder: (ctx, snapshot) {
.....
...
..
}