I have a list of some categories and recipes (food recipes app). So, I have two questions:
Why I see empty space, when open the Category? I think it gives SizedBox(), but I can't write null at this place, because of crash.
Did I make the correct condition for checking a recipe for a category?
Recipes Tab
class RecipesTab extends StatefulWidget {
#override
_RecipesTabState createState() => _RecipesTabState();
}
class _RecipesTabState extends State<RecipesTab> {
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
initialIndex: 1,
child: Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: Text('Книга рецептов'),
bottom: TabBar(
isScrollable: true,
indicatorPadding: EdgeInsets.all(10),
indicatorColor: Color(0xFF0FA3B1),
labelColor: Color(0xFF0FA3B1),
tabs: [
Tab(text: 'Избранное'),
Tab(text: 'Категории'),
],
),
),
body: Padding(
padding: const EdgeInsets.only(top: 5.0),
child: TabBarView(
children: [
Favorite(),
Categories(),
],
),
),
),
);
}
}
Categories
class Categories extends StatefulWidget {
#override
_CategoriesState createState() => _CategoriesState();
}
class _CategoriesState extends State<Categories> {
Future<List<Category>> _getCategories() async {
var data = await http.get(
"https://ibragimov.xyz:7000/categories/?format=json",
headers: {'Content-Type': 'application/json; charset=utf-8'},
);
var jsonData = json.decode(utf8.decode(data.bodyBytes));
List<Category> categories = [];
for (var i in jsonData) {
Category category = Category(i['id'], i['name']);
categories.add(category);
}
return categories;
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: _getCategories(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.data == null) {
return Container(
child: Padding(
padding: const EdgeInsets.all(50.0),
child: Center(
child: CircularProgressIndicator(),
),
),
);
} else {
return Container(
child: ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int id) {
return Card(
elevation: 0,
margin: EdgeInsets.symmetric(horizontal: 10, vertical: 5),
child: ListTile(
title: Text(snapshot.data[id].name),
trailing: Icon(Icons.arrow_forward_ios_rounded),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
Recipes(snapshot.data[id])));
},
),
);
},
),
);
}
},
);
}
}
class Category {
final int id;
final String name;
Category(this.id, this.name);
}
Recipes
class Recipes extends StatefulWidget {
final Category category;
Recipes(this.category);
#override
_RecipesState createState() => _RecipesState();
}
class _RecipesState extends State<Recipes> {
Future<List<Recipe>> _getRecipes() async {
var data = await http.get("https://ibragimov.xyz:7000/recipes/?format=json",
headers: {'Content-Type': 'application/json; charset=utf-8'});
var jsonData = json.decode(utf8.decode(data.bodyBytes));
List<Recipe> recipes = [];
for (var i in jsonData) {
Recipe recipe =
Recipe(i['id'], i['name'], i['cost'], i["category"], i["photo"]);
recipes.add(recipe);
}
return recipes;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.category.name),
),
body: FutureBuilder(
future: _getRecipes(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.data == null) {
return Container(
child: Padding(
padding: const EdgeInsets.all(50.0),
child: Center(
child: CircularProgressIndicator(),
),
),
);
} else {
return Container(
child: GridView.builder(
shrinkWrap: true,
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int id) {
return snapshot.data[id].category == widget.category.name
? Card(
elevation: 0,
margin:
EdgeInsets.symmetric(horizontal: 10, vertical: 5),
child: GridTile(
child: Image.network(
'https://2recepta.com/recept/omlet-s-pomidorami/omlet-s-pomidorami.jpg',
fit: BoxFit.cover,
height: 100,
width: 100,
),
footer: Text(snapshot.data[id].name),
header: Text('data'),
),
)
: SizedBox.shrink();
},
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 250,
mainAxisSpacing: 4.0,
crossAxisSpacing: 4.0,
),
),
);
}
},
),
);
}
}
class Recipe {
final int id;
final String name;
final int cost;
final String category;
final String photo;
Recipe(this.id, this.name, this.cost, this.category, this.photo);
}
Screenshots:
Categories
Chinese kitchen
Russian kitchen
Related
Here I need to create a list of maps.
I'm using provider to keep data.
But when I commanded to list.add() it also replacing the 1st element.
Here's my code.
I'm fetching data from firestore collection.
add_inv_stream.dart
class AddInvStream extends StatelessWidget {
const AddInvStream({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
FirebaseServices services = FirebaseServices();
final provider = Provider.of<InventoryProvider>(context);
return StreamBuilder<QuerySnapshot>(
stream: services.inventory
.where('fishType', isEqualTo: provider.inventoryData['fishType'])
.snapshots(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return const Center(child: Text('Something wrong!'));
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
}
if (snapshot.data!.size == 0) {
return const Center(
child: Text('No inventories'),
);
}
return AddInvData(snapshot: snapshot, services: services);
},
);
}
}
add_inv_data.dart
class AddInvData extends StatefulWidget {
final AsyncSnapshot<QuerySnapshot<Object?>> snapshot;
final FirebaseServices services;
const AddInvData({Key? key, required this.snapshot, required this.services})
: super(key: key);
#override
State<AddInvData> createState() => _AddInvDataState();
}
class _AddInvDataState extends State<AddInvData> {
List abc = [];
#override
Widget build(BuildContext context) {
final providerr = Provider.of<InventoryProvider>(context);
return ListView.builder(
padding: const EdgeInsets.all(15.0),
physics: const ScrollPhysics(),
shrinkWrap: true,
itemCount: widget.snapshot.data!.size,
itemBuilder: (context, index) {
Map<String, dynamic> sellerData =
widget.snapshot.data!.docs[index].data() as Map<String, dynamic>;
return InkWell(
onTap: () {
print('Date: ${sellerData['date']} | QTY: ${sellerData['qty']}');
showDialog(
context: context,
builder: (context) {
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(40),
),
elevation: 16,
child: Form(
key: _formkey,
child: Container(
padding: const EdgeInsets.all(20),
child: TextButton(
onPressed: () {
setState(() {
providerr.getInvSellerData(
sellerDate1: sellerData['date'],
sellerQty1: sellerData['qty'],
);
});
print(providerr.inventorySellerData);
abc.add(providerr.inventorySellerData);
print(abc);
},
child: const Text('ASSIGN'),
),
),
),
);
},
);
},
child: Padding(
padding: const EdgeInsets.fromLTRB(10, 0, 10, 30),
child: Container(
padding: const EdgeInsets.all(15),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
border: Border.all(color: Colors.black),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(sellerData['date']),
Text('${sellerData['qty']} kg'),
],
),
),
),
);
},
);
}
}
inv_provider.dart
class InventoryProvider with ChangeNotifier {
Map<String, dynamic> inventorySellerData = {};
getInvSellerData({
String? sellerDate1,
String? sellerQty1,
}) {
if (sellerDate1 != null) {
inventorySellerData['sellerDate1'] = sellerDate1;
}
if (sellerQty1 != null) {
inventorySellerData['sellerQty1'] = sellerQty1;
}
notifyListeners();
}
}
There are only two collections I have created and when I tapped on first container it's printingDate: 10/31/2022 | QTY: 50and a dialog is showing.
Then I clicked assign button it's printing
{sellerDate1: 10/31/2022, sellerQty1: 50}
[{sellerDate1: 10/31/2022, sellerQty1: 50}]
After that I clicked on second container it's printing
Date: 11/25/2022 | QTY: 54and a dialog is showing same as first container.
Then I clicked assign button it's printing
{sellerDate1: 11/25/2022, sellerQty1: 54}
[{sellerDate1: 11/25/2022, sellerQty1: 54}, {sellerDate1: 11/25/2022, sellerQty1: 54}]
I need to get printed when clicked assign button
[{sellerDate1: 10/31/2022, sellerQty1: 50}, {sellerDate1: 11/25/2022, sellerQty1: 54}]
How can I do that?
Why is this replacing all the elements in the list?
how about add map to list abc outside of dialog.
onTap: () {
print('Date: ${sellerData['date']} | QTY: ${sellerData['qty']}');
showDialog(
context: context,
builder: (context) {
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(40),
),
elevation: 16,
child: Form(
key: _formkey,
child: Container(
padding: const EdgeInsets.all(20),
child: TextButton(
onPressed: () {
// no need call setState. bacause you already update state by provider
providerr.getInvSellerData(
sellerDate1: sellerData['date'],
sellerQty1: sellerData['qty'],
);
print(providerr.inventorySellerData);
},
child: const Text('ASSIGN'),
),
),
),
);
},
);
// add here
abc.add(providerr.inventorySellerData);
print(abc);
},
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.
I'm trying to fetch data from Genshin API, code below is working, but only with delay (in GenshinCubit class), it looks weard, because I don't know how much time to set for delay. I think, there is a problem in code, cause it must not set the GenshinLoaded state before the loadedList is completed. Now, if I remove the delay, it just sets the GenshinLoaded when the list is still in work and not completed, await doesn't help. Because of that I get a white screen and need to hot reload for my list to display.
class Repository {
final String characters = 'https://api.genshin.dev/characters/';
Future<List<Character>> getCharactersList() async {
List<Character> charactersList = [];
List<String> links = [];
final response = await http.get(Uri.parse(characters));```
List<dynamic> json = jsonDecode(response.body);
json.forEach((element) {
links.add('$characters$element');
});
links.forEach((element) async {
final response2 = await http.get(Uri.parse(element));
dynamic json2 = jsonDecode(response2.body);
charactersList.add(Character.fromJson(json2));
});
return charactersList;
}
}
class GenshinCubit extends Cubit<GenshinState> {
final Repository repository;
GenshinCubit(this.repository) : super(GenshinInitial(),);
getCharacters() async {
try {
emit(GenshinLoading());
List<Character> list = await repository.getCharactersList();
await Future<void>.delayed(const Duration(milliseconds: 1000));
emit(GenshinLoaded(loadedList: list));
}catch (e) {
print(e);
emit(GenshinError());
}
}
}
class HomeScreen extends StatelessWidget {
final userRepository = Repository();
HomeScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return BlocProvider<GenshinCubit>(
create: (context) => GenshinCubit(userRepository)..getCharacters(),
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(body: Container(child: const CharactersScreen())),
),
);
}
}
class CharactersScreen extends StatefulWidget {
const CharactersScreen({
Key? key,
}) : super(key: key);
#override
State<CharactersScreen> createState() => _CharactersScreenState();
}
class _CharactersScreenState extends State<CharactersScreen> {
#override
Widget build(BuildContext context) {
return Column(
children: [
BlocBuilder<GenshinCubit, GenshinState>(
builder: (context, state) {
if (state is GenshinLoading) {
return Center(
child: CircularProgressIndicator(),
);
}
if (state is GenshinLoaded) {
return SafeArea(
top: false,
child: Column(
children: [
Container(
color: Colors.black,
height: MediaQuery.of(context).size.height,
child: ListView.builder(
scrollDirection: Axis.horizontal,
shrinkWrap: true,
itemCount: state.loadedList.length,
itemBuilder: ((context, index) {
return Padding(
padding: const EdgeInsets.symmetric(
vertical: 50.0, horizontal: 50),
child: GestureDetector(
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CharacterDetailsPage(
character: state.loadedList[index],
),
),
),
child: Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
color: Colors.blueAccent.withOpacity(0.3),
borderRadius: const BorderRadius.all(
Radius.circular(
30,
),
)),
child: Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: const EdgeInsets.only(
right: 30.0, bottom: 30),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(
state.loadedList[index].name
.toString(),
style: TextStyle(
color: Colors.black,
fontSize: 50),
),
RatingBarIndicator(
itemPadding: EdgeInsets.zero,
rating: double.parse(
state.loadedList[index].rarity
.toString(),
),
itemCount: int.parse(
state.loadedList[index].rarity
.toString(),
),
itemBuilder: (context, index) =>
Icon(
Icons.star_rate_rounded,
color: Colors.amber,
))
],
),
),
),
),
),
);
})),
),
],
),
);
}
if (state is GenshinInitial) {
return Text('Start');
}
if (state is GenshinError) {
return Text('Error');
}
return Text('Meow');
}),
],
);
}
}
I found a solution!
I've got that problem because of forEach. How to wait for forEach to complete with asynchronous callbacks? - there is a solution.
i have a json data which am displaying in the listview that contains item name, scores and target scores. i want to show a progress bar indicating scored points and the target points below is my code and images to help understand.
This is my json data
Below is My code:
`
import 'dart:convert';
import 'package:network/network.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
class FavouriteTab extends StatefulWidget {
#override
_FavouriteTabState createState() => _FavouriteTabState();
}
class _FavouriteTabState extends State<FavouriteTab> {
List products = List();
Future getAllProducts() async {
var response = await http.get(NetworkUrl.getProducts());
if (response.statusCode == 200) {
setState(() {
products = json.decode(response.body);
});
return products;
}
}
#override
void initState() {
super.initState();
getAllProducts();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Target Scores"),
),
body: FutureBuilder(
future: getAllProducts(),
builder: (context, snapshot) {
if (snapshot.hasData) {
if (products.contains("empty")) {
return Center(child: Text("NO DATA"));
} else {
return ListView.builder(
itemCount: products.length,
itemBuilder: (context, index) {
var notis = products;
var scoredAmt = notis[index]['scored_amt'];
var targetAmt = notis[index]['target_score'];
var _percentages = (scoredAmt / targetAmt);
return Card(
child: Container(
child: Column(
children: [
Text(notis[index]['item_name']),
Text(
notis[index]['target_score'],
style: TextStyle(color: Colors.indigo),
),
SizedBox(height: 5),
Row(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Text("Scored Amount :" +
notis[index]['scored_amt']),
),
Spacer(),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text("Target Amount :" +
notis[index]['target_score']),
),
],
),
Padding(
padding: const EdgeInsets.all(8.0),
child: SizedBox(
width: 300,
height: 30,
child: LinearProgressIndicator(
value: _percentages)),
),
],
),
),
);
});
}
} else {
return LinearProgressIndicator(
backgroundColor: Colors.purpleAccent,
);
}
}),
);
}
}`
Am getting this error
i want to have data display like this according to the scores:
This is how i want to display
I don't know what am doing wrong kindly help
As the error you are getting denotes it seems that you are not parsing the strings as num's and are trying to divide Strings.
var _percentages = (num.tryParse(scoredAmt) / num.tryParse(targetAmt))
Or even better do that parsing in your model see here.
Also you are calling the getAllProducts in initState but are not caching the result and you call it again in the futureBuilder,
import 'dart:convert';
import 'package:Habapay/network/network.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
class FavouriteTab extends StatefulWidget {
#override
_FavouriteTabState createState() => _FavouriteTabState();
}
class _FavouriteTabState extends State<FavouriteTab> {
List products = List();
Future allProducts;
Future getAllProducts() async {
var response = await http.get(NetworkUrl.getProducts());
if (response.statusCode == 200) {
setState(() {
products = json.decode(response.body);
});
return products;
}
}
#override
void initState() {
super.initState();
allProducts= getAllProducts();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Target Scores"),
),
body: FutureBuilder(
future: allProducts,/// Now the future gets called only once
builder: (context, snapshot) {
if (snapshot.hasData) {
if (products.contains("empty")) {
return Center(child: Text("NO DATA"));
} else {
return ListView.builder(
itemCount: products.length,
itemBuilder: (context, index) {
var notis = products;
var scoredAmt = notis[index]['scored_amt'];
var targetAmt = notis[index]['target_score'];
var _percentages = (scoredAmt / targetAmt);
return Card(
child: Container(
child: Column(
children: [
Text(notis[index]['item_name']),
Text(
notis[index]['target_score'],
style: TextStyle(color: Colors.indigo),
),
SizedBox(height: 5),
Row(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Text("Scored Amount :" +
notis[index]['scored_amt']),
),
Spacer(),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text("Target Amount :" +
notis[index]['target_score']),
),
],
),
Padding(
padding: const EdgeInsets.all(8.0),
child: SizedBox(
width: 300,
height: 30,
child: LinearProgressIndicator(
value: _percentages)),
),
],
),
),
);
});
}
} else {
return LinearProgressIndicator(
backgroundColor: Colors.purpleAccent,
);
}
}),
);
}
}`
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) {
.....
...
..
}