How to convert a Row to a GridView in Streambuilder - flutter

I currently have a StreamBuilder nested inside a SingleChildScrollView that returns a Row of widgets, which is scrollable along the horizontal axis. I want to change this to a GridView with crossAxisCount: 2, that is scrollable along the vertical axis instead. Any ideas about how to do this please?
Here's my current code:
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: StreamBuilder<QuerySnapshot> (
stream: _firestore
.collection('recipes')
.where('favouritedBy', arrayContains: widget.userEmail)
.snapshots(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return Center(
child: CircularProgressIndicator(
backgroundColor: Colors.lightBlueAccent,
),
);
}
if (snapshot.data == null) {
return Center(
child: CircularProgressIndicator(
backgroundColor: Colors.lightBlueAccent,
),
);
}
final recipes = snapshot.data.documents;
List<Widget> recipeWidgets = [];
for (var recipe in recipes) {
final recipeTitle = recipe.data['recipeTitle'];
final ingredients = recipe.data['ingredients'];
final videoID = recipe.data['videoID'];
final youtubeURL = recipe.data['youtubeURL'];
final method = recipe.data['method'];
final thumbnail = recipe.data['thumbnail'];
final recipeID = recipe.data['recipeID'];
final favouritedBy = recipe.data['favouritedBy'];
final recipeWidget = FavouritesRecipeCard(
recipeTitle: recipeTitle,
videoID: videoID,
youtubeURL: youtubeURL,
recipeID: recipeID,
favouritedBy: favouritedBy,
method: method,
ingredients: ingredients,
thumbnail: thumbnail,
);
recipeWidgets.add(recipeWidget);
}
return Row(
children: recipeWidgets,
); //This is the Row I would like to change to be a GridView instead
}),
),

Problem solved! Here's the solution:
I just changed the Row to be a GridView.count widget:
return GridView.count(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
crossAxisCount: 2,
crossAxisSpacing: 10.0,
mainAxisSpacing: 10.0,
children: recipeWidgets,
);
Hope this helps someone in the future!

return Row(
GridView.builder(
itemCount: snapshot.data.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: (orientation == Orientation.portrait) ? 2 : 3),
itemBuilder: (BuildContext context, int index) {
return new Card(
child: new GridTile(
footer: new Text(snapshot.data.documentsp[index].data()[key]),
child: new Text(snapshot.data.documentsp[index].data()[key]),
),
);
},
),
);

Related

Flutter FutureBuilder duplicates items inside a GridView

It seems that the GridView.builder inside FutureBuilder duplicates each element a number of times equal to the list length.
Here is the code:
InformationScreen:
List<Reference> documentReference = [];
Widget showSavedDocument() => FutureBuilder(
future: _futureListResult,
builder: (context, AsyncSnapshot<ListResult> snapshot) {
if (snapshot.connectionState == ConnectionState.done && snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data!.items.length,
itemBuilder: (context, index) {
final photo = snapshot.data!.items[index].getDownloadURL();
final photoName = snapshot.data!.items[index].name;
final metaData = snapshot.data!.items[index].getMetadata();
documentReference = snapshot.data!.items;
return Column(
children: [
FutureBuilder(
future: metaData,
builder: (context, AsyncSnapshot<FullMetadata> snapshot) {
if(snapshot.hasData) {
photoType = snapshot.data!.contentType!;
}
return Container();
},
),
FutureBuilder(
future: photo,
builder: (context, AsyncSnapshot<String?> snapshot) {
if (snapshot.hasData) {
final image = snapshot.data;
List<Document> documents = [];
for (int i = 0; i < documentReference.length; i++) {
Document document = Document(user!.uid, image!, photoName, photoType);
documents.add(document);
}
return DocumentGrid(documents: documents,); // <------------------------------
}
return Container();
},
),
],
);
},
shrinkWrap: true,
physics: const BouncingScrollPhysics(),
);
}
if (snapshot.connectionState == ConnectionState.waiting || !snapshot.hasData) {
return const Loader();
}
if (snapshot.hasError) {
return Utils.showErrorMessage(snapshot.hasError.toString());
}
return Container();
},
);
DocumentGrid
import 'package:flutter/material.dart';
import 'package:app_test/constant/color.dart';
import '../constant/text.dart';
import '../model/document.dart';
class DocumentGrid extends StatelessWidget {
final List<Document> documents;
const DocumentGrid({Key? key, required this.documents}) : super(key: key);
#override
Widget build(BuildContext context) {
return buildGridView();
}
//****************************************************************************
// Create GridView
//****************************************************************************
Widget buildGridView() => GridView.builder(
itemCount: documents.length,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 2,
mainAxisSpacing: 2,
),
itemBuilder: (context, index) {
final photo = documents[index].photo;
final title = documents[index].title;
final type = documents[index].type;
return buildGridViewItem(photo, title, type);
},
shrinkWrap: true,
physics: const BouncingScrollPhysics(),
);
//****************************************************************************
// Create GridView item
//****************************************************************************
Widget buildGridViewItem(String photo, String? title, String type) => Container(
width: 50,
height: 50,
color: phoneButtonColor,
child: Stack(
fit: StackFit.expand,
alignment: Alignment.center,
children: [
buildNetworkImage(photo, type),
buildBlackOpacity(title),
],
),
);
//****************************************************************************
// Create Network image
//****************************************************************************
Widget buildNetworkImage(String photo, String type) => Image.network(
fit: BoxFit.cover,
width: 100,
height: 100,
photo,
errorBuilder: (context, exception, stackTrace) {
return type == "pdf" || type != "jpg"
|| type != "jpeg" || type != "png"
? Image.asset(
fit: BoxFit.cover,
width: 100,
height: 100,
"assets/images/pdf.png",
)
: Container(
color: grey,
width: 100,
height: 100,
child: const Center(
child: Text(
errorLoadImage,
textAlign: TextAlign.center,
),
),
);
},
);
//****************************************************************************
// Create Black opacity
//****************************************************************************
Widget buildBlackOpacity(String? title) => Container(
color: Colors.black54,
padding: const EdgeInsets.symmetric(
vertical: 30,
horizontal: 20,
),
child: Column(
children: [
Expanded(
child: Center(
child: Text(
title!,
style: const TextStyle(
fontSize: 20,
color: Colors.white,
),
),
),
),
],
),
);
}
How can I solve that, thanks in advance
Problem solved
Replacing ListView by GridView
Widget showSavedDocument() => FutureBuilder(
future: _futureListResult,
builder: (context, AsyncSnapshot<ListResult> snapshot) {
if(snapshot.connectionState == ConnectionState.done && snapshot.hasData) {
return GridView.builder(
itemCount: snapshot.data!.items.length,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 2,
mainAxisSpacing: 2,
),
itemBuilder: (context, index) {
final instructorDocument = snapshot.data!.items;
final photo = instructorDocument[index].getDownloadURL();
final photoName = instructorDocument[index].name;
final metaData = instructorDocument[index].getMetadata();
return Column(
children: [
FutureBuilder(
future: metaData,
builder: (context, AsyncSnapshot<FullMetadata> snapshot) {
if (snapshot.hasData) {
photoType = snapshot.data!.contentType!;
}
return Container();
},
),
FutureBuilder(
future: photo,
builder: (context, AsyncSnapshot<String?> snapshot) {
if (snapshot.hasData) {
final image = snapshot.data!;
return Expanded(
child: buildGridViewItem(image, photoName, photoType),
);
}
return Container();
},
),
],
);
},
shrinkWrap: true,
physics: const BouncingScrollPhysics(),
);
}
if(snapshot.connectionState == ConnectionState.waiting || !snapshot.hasData) {
return const Loader();
}
if(snapshot.hasError) {
return Utils.showErrorMessage(snapshot.hasError.toString());
}
return Container();
},
);

How do I remove all the spaces between the card in gridview.builder

I need help regarding my gridview.builder, my gridview become like this, this happened because I want to filter to show only item that has the same id. I already try to put padding: EdgetInsets.zeroand SizedBox.shrink but it still does not work. How can I remove the blank card and only show the visible card? if I remove return Card()
Stack(
children: [
StreamBuilder(
stream: FirebaseFirestore.instance.collection('products').snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot){
if(snapshot.hasError){
return Text("Error: ${snapshot.error}");
}
if(snapshot.hasData){
return Column(
children: [
Flexible(
child: GridView.builder(
padding: EdgeInsets.zero,
itemCount: snapshot.data!.docs.length,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
mainAxisSpacing: 15,
crossAxisSpacing: 15,
childAspectRatio: 2/3,
),
itemBuilder: (context, index){
final DocumentSnapshot documentSnapshot =
snapshot.data!.docs[index];
DateTime dt = (documentSnapshot['endDateTime'] as Timestamp).toDate();
if(documentSnapshot['userid'] == widget.sellerId){
print(documentSnapshot['userid']);
return buildImageCard(
documentSnapshot['imageURL'][0], documentSnapshot['nameP'],
documentSnapshot['startPrice'], dt, documentSnapshot['id'],
documentSnapshot['categoryP']);
}
return Card();
}
)
),
],
);
}
return const Center(
child: CircularProgressIndicator(),
);
}
),
],
);
The solution would be to filter the list of docs above the widget, and use the filtered list for itemCount and the builder.
The cause of the issue is that each widget takes the aspect ratio that your specified (2/3) + the spacing regardless of the size of the child widget.
So it should be a little something like this
if(snapshot.hasData){
final listToUse = snapshot.data?.docs.where((documentSnapshot) => documentSnapshot['userid'] == widget.sellerId).toList() ?? [];
return Column(...

Flutter - resizeToAvoidBottomInset property makes my ListView covered by keyboard

I will describe my problem from the very first time. I have a page with BottomNavigationBar, ListView, and a Custom Search Widget (using TextField inside it). Whenever I use the Search Widget the keyboard appears and bringing unnecessary white box on it (I have browsed this problem a lot and found this fix by using resizeToAvoidBottomInset: false as my Scaffold property. Using that property fixes the white box problem, but it gives a new problem: bottom-half of my ListView is now blocked by the keyboard because the ListView height is not getting resized when the keyboard appears.
Here is my view code:
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
FocusManager.instance.primaryFocus?.unfocus();
},
child: SafeArea(
child: Scaffold(
resizeToAvoidBottomInset: false,
body: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(
width: double.infinity,
margin: EdgeInsets.all(16),
child: const Text("Inventory",
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: "Quicksand",
fontSize: 20,
fontWeight: FontWeight.w700)),
),
Container(
child: _buildSearch(),
),
Flexible(
child: Container(
child: FutureBuilder(
future: _fetchData(),
builder: (context, AsyncSnapshot snapshot) {
if (isFiltered) {
isFiltered = false;
return ListView.builder(
// itemCount: snapshot.data.length,
physics: BouncingScrollPhysics(),
itemCount: arrFilteredStock.length,
itemBuilder: (context, index) {
var id = arrFilteredStock[index].id;
var code = arrFilteredStock[index].itemCode;
var comname = arrFilteredStock[index].itemComname;
var unit = arrFilteredStock[index].itemUnit;
var qty =
arrFilteredStock[index].itemStockBalanceQty;
return StockCard(
stock: Stock(id, code, comname, unit, qty));
},
);
} else {
if (snapshot.data == null) {
return Container(
child: const Center(
child: Text("Loading..."),
));
} else {
return ListView.builder(
physics: BouncingScrollPhysics(),
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
var id = snapshot.data[index].id;
var code = snapshot.data[index].itemCode;
var comname = snapshot.data[index].itemComname;
var unit = snapshot.data[index].itemUnit;
var qty =
snapshot.data[index].itemStockBalanceQty;
return StockCard(
stock: Stock(id, code, comname, unit, qty));
},
);
}
}
}),
)
)
]))),
);
}
I have found a temporary solution:
White necessary white box is gone, ListView can be scrolled until the last data in it. Only problem is after scrolled until the final data of ListView, the white box appears again. I think this is better comparing to other solutions.
Here's the revisioned code:
#override
Widget build(BuildContext context) {
final bottom = MediaQuery.of(context).viewInsets.bottom;
return GestureDetector(
onTap: () {
FocusManager.instance.primaryFocus?.unfocus();
},
child: SafeArea(
child: Scaffold(
resizeToAvoidBottomInset: false,
body: Column(mainAxisSize: MainAxisSize.max, children: <Widget>[
Container(
width: double.infinity,
margin: EdgeInsets.all(16),
child: const Text("Inventory",
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: "Quicksand",
fontSize: 20,
fontWeight: FontWeight.w700)),
),
Container(
child: _buildSearch(),
),
Flexible(
child: Container(
child: FutureBuilder(
future: _fetchData(),
builder: (context, AsyncSnapshot snapshot) {
if (isFiltered) {
isFiltered = false;
return ListView.builder(
// itemCount: snapshot.data.length,
padding: EdgeInsets.only(bottom: bottom),
physics: BouncingScrollPhysics(),
itemCount: arrFilteredStock.length,
itemBuilder: (context, index) {
var id = arrFilteredStock[index].id;
var code = arrFilteredStock[index].itemCode;
var comname = arrFilteredStock[index].itemComname;
var unit = arrFilteredStock[index].itemUnit;
var qty =
arrFilteredStock[index].itemStockBalanceQty;
return StockCard(
stock: Stock(id, code, comname, unit, qty));
},
);
} else {
if (snapshot.data == null) {
return Container(
child: const Center(
child: Text("Loading..."),
));
} else {
return ListView.builder(
padding: EdgeInsets.only(bottom: bottom),
physics: BouncingScrollPhysics(),
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
var id = snapshot.data[index].id;
var code = snapshot.data[index].itemCode;
var comname = snapshot.data[index].itemComname;
var unit = snapshot.data[index].itemUnit;
var qty =
snapshot.data[index].itemStockBalanceQty;
return StockCard(
stock: Stock(id, code, comname, unit, qty));
},
);
}
}
}),
)),
]))),
);
}
This is just a temporary solution. Any solution will be appreciated.
Add this padding in your code at the top of your fields when you add data keyboard appears first wrape in container then check other area I resolved same issue adding this
Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom));

carousel_slider showing only one image

I have to explain a bit the current structure:
news_view.dart has got FutureBuilder and fetching all necessary items from API and sending to another page Widget: customListTile3(articles, index, context)
hallo.dart has got above Widget:
Widget customListTile3(List<Datum> articles, int index, BuildContext context) {
final urlImages = [
articles[index].imageUrl!,
];
final urlImage = urlImages[index];
and
child: Column(
children: <Widget>[
Center(
child: CarouselSlider.builder(
options: CarouselOptions(height: 200),
itemCount: 10,
itemBuilder: (context, index, realIndex) {
return buildImage(urlImage, index);
},
),
bottom there is buildImage class:
child: Column(
children: <Widget>[
Center(
child: CarouselSlider.builder(
options: CarouselOptions(height: 200),
itemCount: 10,
itemBuilder: (context, index, realIndex) {
return buildImage(urlImage, index);
},
),
Slider works but it show only single image with multiple times. What is problem here?
You are only returning a single image in your function. Passing an index into a list would give you a single element at that particular index.
final urlImages = [
articles[index].imageUrl!,
];
final urlImage = urlImages; //remove [index];
I solved:
final urlImages = [
articles[0].imageUrl!,
articles[1].imageUrl!,
articles[2].imageUrl!,
articles[3].imageUrl!,
articles[4].imageUrl!,
articles[5].imageUrl!,
articles[6].imageUrl!,
articles[7].imageUrl!,
articles[8].imageUrl!,
articles[9].imageUrl!,
];
Center(
child: CarouselSlider.builder(
options: CarouselOptions(
),
itemCount: 10,
itemBuilder: (context, idx, realIndex) {
return buildImage(articles, urlImages[idx], idx, context);
},
),
),

How to implement checkbox over GridView correctly

I have checkbox for selecting and deselecting photos.
There is a visible loading screen for each tap.
_mediaList has the photo asset. mediaModel has the necessary methods to add and remove the path of selected and deselected photos respectively.
Widget build(BuildContext context) {
super.build(context);
return GridView.builder(
itemCount: _mediaList.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3, mainAxisSpacing: 4.0, crossAxisSpacing: 4.0),
itemBuilder: (BuildContext context, int index) {
final saved = mediaModel.getMedia().contains(
_mediaList[index].relativePath + '/' + _mediaList[index].title);
return FutureBuilder(
future: _mediaList[index].thumbDataWithSize(200, 200),
builder: (BuildContext context, snapshot) => snapshot.hasData
? GridTile(
header: saved
? Icon(Icons.check_circle, color: Colors.white,)
: Icon(Icons.check_circle_outline, color: Colors.white,),
child: GestureDetector(
child: Image.memory(
snapshot.data,
fit: BoxFit.cover,
),
onTap: () => setState(() => saved
? mediaModel.removeMedia(
_mediaList[index].relativePath +
'/' +
_mediaList[index].title)
: mediaModel.addMedia(
_mediaList[index].relativePath +
'/' +
_mediaList[index].title))),
)
: Container());
},
);
}
EDIT: After some analysis, I found out using Provider to load images might be the right way.
Can you help me in converting this code to Provider?
Thanks in advance!!!
Screenshot:
Full code:
class FooPage extends State<SoPage> {
static const int _count = 10;
final List<bool> _checks = List.generate(_count, (_) => false);
#override
Widget build(BuildContext context) {
return Scaffold(
body: GridView.builder(
itemCount: _count,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
itemBuilder: (_, i) {
return Stack(
children: [
Container(color: Colors.red[(i * 100) % 900]),
Align(
alignment: Alignment.topCenter,
child: Checkbox(
value: _checks[i],
onChanged: (newValue) => setState(() => _checks[i] = newValue),
),
),
],
);
},
),
);
}
}