I have a gridview,i have implemented search,search is working fine,but the viewing of search item is not shows properly,
Before search
This how my gridview show after searching a item
Code
SliverGrid(
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 200,
childAspectRatio: 6 / 5,
// crossAxisCount: 2,
crossAxisSpacing: 0,
mainAxisSpacing: 0),
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
SubCategoryModel data = sub_category_model[index];
if (searchController.text.isEmpty) {
return GestureDetector(
child: Card(
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(6),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
flex: 0,
child: Container(
width: 100,
height: 100.0,
child: ClipRRect(
borderRadius: BorderRadius.circular(10.0),
child: CachedNetworkImage(
fit: BoxFit.fill,
imageUrl: Urls.BASE_IMAGE_URL +
data.image.toString(),
placeholder: (context, url) => Center(
child: CircularProgressIndicator()),
errorWidget: (context, url, error) =>
Icon(Icons.error),
),
),
)),
Expanded(
flex: 0,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10, vertical: 10),
child: Text(
data.name,
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 13),
),
),
)
],
),
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => OnlinecartSubitems(data.id.toString(),Category_Name),
),
);
},
);
} else if (data.name.contains(searchController.text) ||
data.name.toLowerCase().contains(searchController.text) ||
data.name.toUpperCase().contains(searchController.text)) {
return GestureDetector(
child: Card(
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(6),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
flex: 0,
child: Container(
width: 100,
height: 100.0,
child: ClipRRect(
borderRadius: BorderRadius.circular(10.0),
child: CachedNetworkImage(
fit: BoxFit.fill,
imageUrl: Urls.BASE_IMAGE_URL +
data.image.toString(),
placeholder: (context, url) => Center(
child: CircularProgressIndicator()),
errorWidget: (context, url, error) =>
Icon(Icons.error),
),
),
)),
Expanded(
flex: 0,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10, vertical: 10),
child: Text(
data.name,
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 13),
),
),
)
],
),
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => OnlinecartSubitems(data.id.toString(),Category_Name),
),
);
},
);
} else {
return Container();
}
},
childCount: sub_category_model.length,
),
)
Why not make a new list each time the search matches an item. Then, build a New GridView object based on the newly created list. Whenever your TextView is empty, you return the original list.
For Instance
//Create Search List
List<Object> searchList = [];
//Check if SearchTextView is empty or not with a ternary Operator
controller.text.isEmpty?
//Build your GridView based on the Original List
GridView.count(
...
itemCount: mainList.length,
...
) //Replace with you SliverGrid
//Build your GridView based on the Search List
: GridView.count(
...
itemCount: searchList.length
...
),//Replace with you SliverGrid
You can Replace my GridView with your SilverGrid widget.
The full Code (Spoiler alert: Very long):
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Demo'),
);
}
}
class MyHomePage extends StatefulWidget {
final String title;
MyHomePage({Key key, #required this.title}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
//Search TextField Controller
final _searchController = TextEditingController();
List<Fruit> mainList = [
Fruit(name: 'Apple', imageUrl: 'https://images.pexels.com/photos/102104/pexels-photo-102104.jpeg'),
Fruit(name: 'Banana', imageUrl: 'https://images.pexels.com/photos/5945848/pexels-photo-5945848.jpeg'),
Fruit(name: 'Pineapple', imageUrl: 'https://images.pexels.com/photos/1071878/pexels-photo-1071878.jpeg'),
Fruit(name: 'Mango', imageUrl: 'https://images.pexels.com/photos/918643/pexels-photo-918643.jpeg'),
];
List<Fruit> searchList = [];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Column(
children: [
Container(
width: MediaQuery.of(context).size.width,
height: 60.0,
child: TextFormField(
controller: _searchController,
onChanged: (text){
final String queryString = _searchController.text;
setState((){
if(queryString.isNotEmpty){
for(final fruit in mainList){
if(fruit.name.contains(queryString)){
searchList.add(fruit);
} else{
searchList.remove(fruit);
}
}
}else{
searchList.clear();
}
});
}
),
),
Expanded(
child: _searchController.text.isEmpty
? GridView.count(
crossAxisCount: 2,
children: mainList.map((fruit)=> CardWidget(fruit: fruit)).toList(),
)
:GridView.count(
crossAxisCount: 2,
children: searchList.map((searchedFruit)=>CardWidget(fruit: searchedFruit)).toList()
),
),
],
),
);
}
}
Create a Class to hold Fruit
class Fruit{
final String imageUrl;
final String name;
Fruit({this.imageUrl, this.name});
}
Create widget to be built for each fruit object found in the mainList
//Card Widget
class CardWidget extends StatefulWidget{
final Fruit fruit;
CardWidget({this.fruit});
#override
_CardWidgetState createState()=> _CardWidgetState();
}
class _CardWidgetState extends State<CardWidget>{
#override
Widget build(BuildContext context){
return Container(
width: 100.0,
height: 140.0,
child: Column(
children:[
Image(image: NetworkImage(widget.fruit.imageUrl)),
SizedBox(height: 10.0),
Text(widget.fruit.name),
]
)
);
}
}
Try it and let see
I have fixed the issue with help of Benedict and Farhan Syah thanks for the idea and some codes
Initialize variables
//Search controller for textfield
TextEditingController searchController = TextEditingController();
//For show list data first
List<SubCategoryModel> sub_category_model = [];
//for searchresult list
List<SubCategoryModel> _searchResult = [];
View
_searchResult.length != 0 ||searchController.text.isNotEmpty?SliverGrid(
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 200,
childAspectRatio: 6 / 5,
// crossAxisCount: 2,
crossAxisSpacing: 0,
mainAxisSpacing: 0),
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
SubCategoryModel data = _searchResult[index];
return GestureDetector(
child: Card(
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(6),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
flex: 0,
child: Container(
width: 100,
height: 100.0,
child: ClipRRect(
borderRadius:
BorderRadius.circular(10.0),
child: CachedNetworkImage(
fit: BoxFit.fill,
imageUrl: Urls.BASE_IMAGE_URL +
data.image.toString(),
placeholder: (context, url) => Center(
child:
CircularProgressIndicator()),
errorWidget: (context, url, error) =>
Icon(Icons.error),
),
),
)),
Expanded(
flex: 0,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10, vertical: 10),
child: Text(
data.name,
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 13),
),
),
)
],
),
),
onTap: () {
},
);
},
childCount: _searchResult.length,
),
):SliverGrid(//use same code above with **sub_category_model.length**)
Search widget
Widget _SearchText() {
return Container(
width: 360,
height: 65,
color: Colors.transparent,
child: new Padding(
padding: const EdgeInsets.only(top: 10, left: 10, right: 10),
child: new Card(
elevation: 8,
child: TextField(
decoration: new InputDecoration(
filled: true,
hintStyle: TextStyle(fontSize: 11.5),
hintText: 'Search by Name',
suffixIcon: GestureDetector(
onTap: () {
FocusScope.of(context).unfocus();
searchController.clear();
_searchResult.clear();
},
child: Icon(Icons.cancel_rounded,
color: Color.fromRGBO(34, 83, 148, 1))),
prefixIcon:
Icon(Icons.search, color: Color.fromRGBO(34, 83, 148, 1)),
border: InputBorder.none),
onChanged: onSearchTextChanged,
controller: searchController,
),
),
),
);
}
Onchanged function for searching through list
onSearchTextChanged(String text) async {
//clear search data before typing
_searchResult.clear();
if (text.isEmpty) {
setState(() {});
return;
}
//use searchcontroller text and loop through list of api data and add the result to _searchResult
sub_category_model.forEach((searchValue) {
if (searchValue.name.toLowerCase().contains(text))
_searchResult.add(searchValue);
});
setState(() {});
}
You are returning Container() when the search condition doesn't match. Container itself will take a place of a widget, hence, it will be an item of the grids.
To test this, you can try putting something inside the container, such as:
Container(
child: Text('This grid is not empty)
)
The problem with this, is if you have 12 items in the data, no matter your search result, it will still build 12 items (including the empty container)
To solve this, you need a better way to filter the data, such as using a list to filter, and then passing the list data to the grid builder.
The concept is like this:
Original list : 10 items
Newlist based on the search: 5 items
The grid builder will build the items based on the list provided.
Related
I'm creating a flutter project on which it consist of news feature sadly after building an apk and installing it to try in become an empty screen with grey shade, i also try to test it on my phone and this is what happen i dont know where the bugs came from.. please help me
here's the code
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
import 'Article_View.dart';
import 'News_Categories.dart';
import 'helper/Data.dart';
import 'helper/News.dart';
import 'model/Article_Model.dart';
import 'model/CategoryModel.dart';
class NewsHomePage extends StatefulWidget {
#override
_NewsHomePageState createState() => _NewsHomePageState();
}
class _NewsHomePageState extends State<NewsHomePage> {
List<CategoryModel> categories = <CategoryModel>[];
List<ArticleModel> articles = <ArticleModel>[];
bool _loading = true;
//bannerads
late BannerAd _bannerads;
bool _isAdsLoaded = false ;
#override
void initState() {
// TODO: implement initState
super.initState();
categories = getCategories();
getNews();
_initBannerAds();
}
getNews() async {
News newsClass = News();
await newsClass.getNews();
articles = newsClass.news;
setState(() {
_loading = false;
});
}
_initBannerAds(){
_bannerads = BannerAd(
size: AdSize.banner,
// ignore: deprecated_member_use
adUnitId: "ca-app-pub-8634651641429291/4830511818",
listener: BannerAdListener(
onAdLoaded: (ad){
setState(() {
_isAdsLoaded =true;
});
},
onAdFailedToLoad: (ad,error){}
),
request: const AdRequest()
);
_bannerads.load();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.green,
elevation: 0.0,
title: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Text('Stock '),
Text(
'News',
style: TextStyle(
color: Colors.black54,
),
),
],
),
centerTitle:true,
bottom: const PreferredSize(
preferredSize: Size.zero,
child: Text("Powered by news.org")),
),
body: _loading
? Container(
child: const Center(
child: CircularProgressIndicator(),
),
)
: SingleChildScrollView(
child: Container(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
///Categories
Container(
padding: const EdgeInsets.symmetric(horizontal: 22.0),
height: 90.0,
child: Expanded(
child: ListView.builder(
itemCount: categories.length,
scrollDirection: Axis.horizontal,
shrinkWrap: true,
itemBuilder: (context, index) {
return CategoryTile(
imageUrl: categories[index].imageAssetUrl,
categoryName: categories[index].categoryName,
);
}),
),
),
///Blogs
Container(
padding: const EdgeInsets.only(top: 16.0),
child: Expanded(
child: ListView.builder(
physics: const ClampingScrollPhysics(),
shrinkWrap: true,
itemCount: articles.length,
itemBuilder: (context, index) {
return BlogTile(
imageURL: articles[index].urlToImage,
title: articles[index].title,
desc: articles[index].description,
url: articles[index].url,
);
},
),
),
),
],
),
),
),
bottomNavigationBar: _isAdsLoaded?SizedBox(
height: _bannerads.size.height.toDouble(),
width: _bannerads.size.width.toDouble(),
child: AdWidget(ad: _bannerads),
)
:const SizedBox(),
);
}
}
class CategoryTile extends StatelessWidget {
final String imageUrl, categoryName;
const CategoryTile({required this.imageUrl, required this.categoryName});
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CategoryNews(
category: categoryName.toLowerCase(),
),
),
);
},
child: Container(
margin: EdgeInsets.only(right: 10.0),
child: Stack(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(6),
child: imageUrl != null
? CachedNetworkImage(
imageUrl: imageUrl,
width: 120,
height: 60.0,
fit: BoxFit.cover,
)
: Image.network(
imageUrl,
width: 120.0,
height: 60.0,
fit: BoxFit.cover,
),
),
Container(
alignment: Alignment.center,
decoration: BoxDecoration(
color: Colors.black26,
borderRadius: BorderRadius.circular(6),
),
width: 120,
height: 60.0,
child: Text(
categoryName,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w500,
fontSize: 18.0,
),
),
),
],
),
),
);
}
}
class BlogTile extends StatelessWidget {
final String imageURL, title, desc, url;
BlogTile(
{required this.imageURL,
required this.title,
required this.desc,
required this.url});
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ArticleView(
blogUrl: url,
)),
);
},
child: Container(
margin: const EdgeInsets.only(bottom: 16.0, left: 10.0, right: 10.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
ClipRRect(
borderRadius: BorderRadius.circular(6.0),
child: imageURL != null
? CachedNetworkImage(
imageUrl: imageURL,
)
: Image.network(imageURL),
),
const SizedBox(
height: 8.0,
),
Text(
title,
style: const TextStyle(
//color: Colors.black87,
fontSize: 17.0,
fontWeight: FontWeight.w500,
),
),
const SizedBox(
height: 8.0,
),
Text(
desc,
style: const TextStyle(
//color: Colors.black54,
),
),
],
),
),
);
}
}
and here's the screen shots of the app
[![debugging app][1]][1]
[![build app][2]][2]
[1]: https://i.stack.imgur.com/tvBsG.jpg
[2]: https://i.stack.imgur.com/wOuxS.jpg
Check if this line exists in android/app/src/main/AndroidManifest.xml file. If it doesn't add it right below the package name line. This will grant the app INTERNET permission so it can access it & display the data from the internet.
<uses-permission android:name="android.permission.INTERNET"/>
Flutter by default only adds the INTERNET permission in debug mode. When you build an APK in release mode, you have to explicitly add the permission by including the above line.
More info here.
I want to display a group of books on the main page (this is working fine!), but right now I want to navigate to a dynamic detail page if I click on the book, but I don't know how to do it properly. Can anyone explain how to navigate to a detail page with firestore data ?
models
class Buecher {
String autor;
String buchtitel;
String bild;
int id;
Buecher({
required this.autor,
required this.id,
required this.buchtitel,
required this.bild,
});
}
getting data from firestore
Buecher _bouksListFromSnapshot(DocumentSnapshot snapshot) {
return Buecher(
autor: snapshot.get('author') ?? '',
buchtitel: snapshot.get('buchtitel') ?? '',
bild: snapshot.get('bild') ?? '',
id: snapshot.get('id') ?? '0',
);
}
Stream<Buecher> get bouks {
return brewCollection.doc().snapshots()
.map(_bouksListFromSnapshot);
}
}
main page
class BrewList extends StatefulWidget {
#override
_BrewListState createState() => _BrewListState();
}
class _BrewListState extends State<BrewList> {
#override
Widget build(BuildContext context) {
final buecher = Provider.of<List<Buecher>>(context);
//print(brews.documents);
return SizedBox(
height: 50,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: buecher.length,
itemBuilder: (context, index) {
return BrewTile(
buecher: buecher[index],
);
},
),
);
}
}
class BrewTile extends StatelessWidget {
final Buecher buecher;
BrewTile({required this.buecher});
#override
Widget build(BuildContext context) {
return GestureDetector(
child: Row(children: [
InkWell(
child: ElevatedButton(
style: ElevatedButton.styleFrom(
primary: AppColors.backColor,
elevation: 0.0,
shadowColor: Colors.transparent,
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailForm(buech:buecher),
));
},
child: Container(
margin: EdgeInsets.fromLTRB(10, 0, 0, 5),
height: 320,
width: 170,
child: Column(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(15.0),
child: Image.asset(
"${buecher.bild}",
width: 170.0,
height: 230.0,
fit: BoxFit.cover,
),
),
Expanded(
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
BigText(
height: 1.5,
text: buecher.buchtitel,
size: 17,
color: AppColors.mainColor,
),
SmallText(
text: buecher.autor,
size: 16,
color: AppColors.FontColor,
),
],
)),
),
],
),
),
),
),
]));
}
}
detail page
class DetailForm extends StatelessWidget {
Buecher buech;
DetailForm({
Key? key, required this.buech
}) : super(key: key);
#override
Widget build(BuildContext context) {
return StreamBuilder<Buecher>(
stream: DatabaseService().bouks,
builder: (context, snapshot) {
if (snapshot.hasData) {
Buecher? buecherData = snapshot.data;
return MaterialApp(
home: Scaffold(
backgroundColor: AppColors.backColor,
appBar: AppBar(
backgroundColor: AppColors.backColor,
elevation: 0,
centerTitle: true,
leading: IconButton(
icon: Icon(FeatherIcons.chevronLeft),
color: AppColors.mainColor,
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Profil()),
);
},
),
leadingWidth: 50,
title: BigText(
text: '${buecherData!.autor}',
),
),
body: SingleChildScrollView(
child: Column(
children: [
SizedBox(
height: 340,
child: Center(
child: Column(
mainAxisAlignment:
MainAxisAlignment.spaceEvenly,
children: [
BuchContainer(
bild: "assets/image/GG.jpeg",
height: 260,
width: 200),
BigText(
text: '${buech.autor}',
color: AppColors.FontColor),
],
),
),
),
Container(
height: 70,
width: 360,
decoration: BoxDecoration(
color: AppColors.backColor2,
borderRadius: BorderRadius.circular(25.0)),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceEvenly,
children: [
Icon(Icons.star),
BigText(text: '4.8'),
SmallText(
text: '/5', color: AppColors.FontColor),
SmallText(
text: '|',
color: AppColors.FontColor,
size: 20),
BigText(text: '4.2k'),
SmallText(
text: 'Read', color: AppColors.FontColor),
SmallText(
text: '|',
color: AppColors.FontColor,
size: 20),
BigText(text: '120'),
SmallText(
text: 'pages',
color: AppColors.FontColor),
],
)),
Container(
margin: EdgeInsets.symmetric(horizontal: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 10),
BigText(text: 'Synopsis'),
SizedBox(height: 20),
Text(
'The story of the mysteriously wealthy Jay Gatsby and his love for the beautiful Daisy Buchanan, of lavish parties on Long Island at a time when The New York Times noted “gin was the national drink and sex the national obsession,” it is an exquisitely crafted tale of America in the 1920s.',
style: TextStyle(
height: 1.5,
fontSize: 16,
),
)
],
)),
],
),
)));
} else {
return Text('error');
}
});
}
}
I have asked this question previously but I am not receiving much help. I've created a GridView using GridView.count; however, I cannot get an indiviual container to change colors. The entire row changes if I click on any container within the row. I want to be able to change an individual container's color when it is tapped on, as well as have check mark appear on the top right corner of the container when selected.
(1) My Layout
(2) An Example of what I would like to happen
I'm very new to Flutter, so my code is not very optimal. I've tried making a list model as well but I have not had any luck with that. I'm attaching a portion of my code to show what I've done so far. Any help would be great :)
Widget build(BuildContext) {
double _height = MediaQuery.of(context).size.height;
final data = ModalRoute.of(context)!.settings;
String retrieveString;
if (data.arguments == null) {
retrieveString = "empty";
} else {
retrieveString = data.arguments as String;
}
return Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: const Color(0xff31708c),
body: Padding(
padding: EdgeInsets.only(
left: 30,
right: 30,
top: _height * 0.2),
child: Column(
children: <Widget>[
Text('Hi $retrieveString! What all would you like to focus on?',
style: GoogleFonts.montserrat(
color: Colors.white70,
fontSize: 19,
fontWeight: FontWeight.w600
),
textAlign: TextAlign.center,),
const SizedBox(height: 9),
Text("You can pick all that apply:",
style: GoogleFonts.montserrat(
color: Colors.white70,
fontSize: 14.5,
fontWeight: FontWeight.w600
),),
const SizedBox(height: 9,),
Column(children: [
GridView.count(
primary: true,
shrinkWrap: true,
padding: const EdgeInsets.all(10),
childAspectRatio: 1.15,
crossAxisCount: 2,
crossAxisSpacing: 25,
mainAxisSpacing: 25,
children: <Widget>[
GestureDetector(
onTap: () {
setState(() {
_ContainerColor = _ContainerColor == Colors.white
? Color(0xffa1d0e6)
: Colors.white;
});
},
child: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
border: Border.all(
color: const Color.fromARGB(255, 20, 83, 106),
width: 2.5),
color: _ContainerColor
),
child: Column(
children: [
const Align(alignment: Alignment.topCenter,
child: Icon(MyFlutterApp.relationships,
color: Color(0xff31708c),
size: 45,),
),
const SizedBox(height: 4,),
Text('Maintaining healthy relationships',
style: GoogleFonts.montserrat(
fontSize: 14,
fontWeight: FontWeight.w500,
color: const Color(0xff31708c)
),
textAlign: TextAlign.center,)
],
),
),
),
From my understanding, you have to do allow users to have multi-select, because of the line
You can pick all that apply:
So here is a custom stateful widget that helps you to do multi-select, you can have your own widget child inside the gridview.
class CustomPage extends StatefulWidget {
const CustomPage({Key? key}) : super(key: key);
#override
State<CustomPage> createState() => _CustomPageState();
}
class _CustomPageState extends State<CustomPage> {
String retrieveString = "";
List selectedIndex = [];
List dataItems = ['India', 'USA', 'Germany'];
#override
Widget build(BuildContext context) {
double height = MediaQuery.of(context).size.height;
final data = ModalRoute.of(context)!.settings;
if (data.arguments == null) {
retrieveString = "empty";
} else {
retrieveString = data.arguments as String;
}
return Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: const Color(0xff31708c),
body: Padding(
padding: EdgeInsets.only(left: 30, right: 30, top: height * 0.2),
child: Column(children: <Widget>[
Text('Hi $retrieveString! What all would you like to focus on?'),
const SizedBox(height: 10),
const Text("You can pick all that apply:"),
const SizedBox(height: 10,),
Expanded(
child: GridView.builder(
scrollDirection: Axis.vertical,
primary: true,
shrinkWrap: true,
padding: const EdgeInsets.all(10),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
childAspectRatio: 1.15,
crossAxisCount: 2,
crossAxisSpacing: 25,
mainAxisSpacing: 25),
itemCount: dataItems.length,
itemBuilder: (context, index) {
return GestureDetector(
onTap: () {
setState(() {
if (selectedIndex.contains(index)) {
selectedIndex.remove(index);
} else {
selectedIndex.add(index);
}
});
},
child: Stack(
alignment: Alignment.topRight,
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
border: Border.all(
color:
const Color.fromARGB(255, 20, 83, 106),
width: 2.5),
color: selectedIndex.contains(index)
? const Color(0xffa1d0e6)
: Colors.white),
child: Center(
child: Text(dataItems[index]),
),
),
selectedIndex.contains(index)
? Container(
padding: const EdgeInsets.all(10),
child: const CircleAvatar(
child: Icon(Icons.check_outlined),
),
)
: Container()
],
),
);
},
),
)
])));
}
}
Hope it resolves your issue.
I've created your app as a test version. All you need to do is inject your widget in the //put your widget here!!!!! section. Also for testing this right now as a demo, you can paste the code on dartpad and select "New" -> "Flutter" -> Paste Code: https://dartpad.dev
import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowtappedModeBanner: false,
home: Scaffold(
body: Center(
child: CustomCheckboxGrid(),
),
),
);
}
}
class CustomCheckboxGrid extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _CustomCheckboxGridState();
}
}
class _CustomCheckboxGridState extends State<CustomCheckboxGrid> {
int tapped_index = 0;
List card_names = [
'Maintaining healthy relationships',
'Being happier and more content in life',
'Work life balance',
'Personal Growth',
'Stress',
'Mental health',
];
#override
Widget build(BuildContext context) {
return Scaffold(
body: GridView.builder(
padding: const EdgeInsets.all(16),
itemCount: card_names.length,
itemBuilder: (BuildContext context, int index) {
return buildCard(index);
},
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
),
),
);
}
Widget buildCard(int index) {
bool tapped = index == tapped_index;
String current_name = card_names[index];
return GestureDetector(
onTap: () {
setState(() {
print("Tapped index: ${index}");
tapped_index = index;
});
},
child: Stack(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(14),
child:
//put your widget here!!!!!
//-----------------------------------
Card(
color: tapped ? Colors.orange : Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
child: Container(
child: Center(child: Text(current_name)),
),
),
//-----------------------------------
),
Positioned(
top: 14,
right: 14,
child: Offstage(
offstage: !tapped,
child: Container(
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(width: 2),
shape: BoxShape.circle),
child: Icon(
Icons.check,
color: Colors.green,
),
),
),
),
],
),
);
}
}
I am calling API data inside Listview. builder in flutter but the error I am facing is the items are changing their positions automatically.
For Example, When I load this class for the First Time the arrangement of List items is the same as required but after 30-40 seconds the List items arrangement automatically starts changing, and data showing itself randomly.
I am looking for someone who can help me to fix this issue?
For this purpose here is myClass Code.
import 'dart:convert';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:http/http.dart'as HTTP;
import 'package:worldcup/utils/colors.dart';
class SeriesSchedulePage extends StatefulWidget {
#override
_SeriesSchedulePageState createState() => _SeriesSchedulePageState();
}
class _SeriesSchedulePageState extends State<SeriesSchedulePage> {
List<dynamic> matches = [];
var match;
getSchedule() async {
http.Response response = await http
.get(Uri.parse("https://api.cricket.com.au/matches/2780"));
final Map parseData = await json.decode(response.body.toString());
matches = parseData['matchList']["matches"];
setState(() {
match = matches;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.primaryWhite,
appBar: AppBar(
backgroundColor: AppColors.yellow,
elevation: 0,
centerTitle: true,
leading: IconButton(
onPressed: () {
Navigator.pop(context,true);
},
icon: Icon(
Icons.arrow_back,
color: Colors.white,
),
),
title: Text(
'Schedule',
textScaleFactor: 0.9,
style: GoogleFonts.openSans(
color: Colors.white,
fontWeight: FontWeight.w600,
fontSize: 17),
),
),
body: Container(
child: FutureBuilder(
future: getSchedule(),
builder: (context, snapshot) {
if (match == null) {
return Center(
child: CupertinoActivityIndicator(
animating: true, radius: 15));
} else
return ListView.builder(
itemCount: matches.length,
shrinkWrap: true,
reverse: false,
itemBuilder: (context, index) {
if (matches[index]["status"] =="UPCOMING") {
return Card(
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(7),
),
child: Container(
width: double.infinity,
child: Padding(
padding: EdgeInsets.only(left: 15, top: 7, bottom: 7, right: 15),
child: Row(
children: [
SizedBox(width: 20,),
Expanded(
flex: 2,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
matches[index]['name'].toString(),
textScaleFactor: 0.9,
style: GoogleFonts.openSans(
fontWeight: FontWeight.w700, fontSize: 15),
),
SizedBox(height: 10,),
Text(
matches[index]["homeTeam"]['name'].toString(),
textScaleFactor: 0.9,
style: GoogleFonts.openSans(
fontWeight: FontWeight.w700, fontSize: 15),
),
SizedBox(height: 10,),
Text(
matches[index]["awayTeam"]['name'].toString(),
textScaleFactor: 0.9,
style: GoogleFonts.openSans(
fontWeight: FontWeight.w500, fontSize: 13),
),
],
),
],
),
),
],
),
),
)
);
} else {
return Center(
child: Text("No Upcoming Match in this series"),
);
}
}
);
},
),
)
);
}
}
The issue is because getSchedule() has a setState inside it. When the build method is called, getSchedule() will trigger, and since it is calling setState , the build method is being called again, causing your widgets to continuously rebuild in an infinite loop.
What you need to do is prevent such a loop from happening. I see that you are using FutureBuilder too, that is a solution but your implementation is incorrect.
What you should do is this:
Future<List<dynamic>> getSchedule() async {
http.Response response =
await http.get(Uri.parse("https://api.cricket.com.au/matches/2780"));
final Map parseData = await json.decode(response.body.toString());
var matches = parseData['matchList']["matches"];
return matches;
}
This function returns a Future<List<dynamic>> which your future builder can use to handle the builds. For info on future builder here https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html.
Since you FutureBuilder will react to what is provided by getSchedule() when the future is complete, you do not need to use setState to rebuild.
I have modified your SeriesShedualPage here is the full code:
class SeriesSchedulePage extends StatefulWidget {
#override
_SeriesSchedulePageState createState() => _SeriesSchedulePageState();
}
class _SeriesSchedulePageState extends State<SeriesSchedulePage> {
Future<List<dynamic>> getSchedule() async {
http.Response response =
await http.get(Uri.parse("https://api.cricket.com.au/matches/2780"));
final Map parseData = await json.decode(response.body.toString());
var matches = parseData['matchList']["matches"];
return matches;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: FutureBuilder<List<dynamic>>(
future: getSchedule(),
builder: (context, snapshot) {
if (snapshot.hasData) {
List<dynamic> matches = snapshot.data!;
return ListView.builder(
itemCount: matches.length,
shrinkWrap: true,
reverse: false,
itemBuilder: (context, index) {
if (matches[index]["status"] == "UPCOMING") {
return Card(
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(7),
),
child: Container(
width: double.infinity,
child: Padding(
padding: EdgeInsets.only(
left: 15, top: 7, bottom: 7, right: 15),
child: Row(
children: [
SizedBox(
width: 20,
),
Expanded(
flex: 2,
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
matches[index]['name'].toString(),
textScaleFactor: 0.9,
),
SizedBox(
height: 10,
),
Text(
matches[index]["homeTeam"]['name']
.toString(),
textScaleFactor: 0.9,
),
SizedBox(
height: 10,
),
Text(
matches[index]["awayTeam"]['name']
.toString(),
textScaleFactor: 0.9,
),
],
),
],
),
),
],
),
),
));
} else {
return Center(
child: Text("No Upcoming Match in this series"),
);
}
});
}
return Center(
child: CupertinoActivityIndicator(animating: true, radius: 15));
},
),
));
}
}
I want to apply categorise selection for my flutter app. I'm a beginner in flutter. I've tried to my very best to get here but I don't know how to remove these empty spaces.
In the body of the homescreen.dart I've written the following code.
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: KDefaultPadding),
child: GridView.builder(
itemCount: products.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 0.75,
mainAxisSpacing: KDefaultPadding,
crossAxisSpacing: KDefaultPadding,
),
itemBuilder: (context, index) => ItemCard(
product:
products[index].tag == "chicken" ? products[index] : null,
press: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailScreen(
product: products[index],
),
),
),
),
),
),
),
and in the item card.dart I've written the following.
class ItemCard extends StatelessWidget {
final Product product;
final Function press;
const ItemCard({
Key key,
this.product,
this.press,
}) : super(key: key);
#override
Widget build(BuildContext context) {
if (product != null) {
return GestureDetector(
onTap: press,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
padding: EdgeInsets.all(KDefaultPadding),
// height: 180,
// width: 160,
decoration: BoxDecoration(
color: product.color,
borderRadius: BorderRadius.circular(16),
),
child: Hero(
tag: "${product.id}",
child: Image.asset(
product.image,
)),
),
Padding(
padding:
const EdgeInsets.symmetric(vertical: KDefaultPadding / 4),
child: Text(
product.title,
style:
TextStyle(color: kTextColor, fontWeight: FontWeight.bold),
),
),
Text(
"$kCurrency${product.price}",
style: TextStyle(
color: kTextLightColor, fontWeight: FontWeight.bold),
)
],
),
);
} else {
return Container();
}
}
}
and the output I got is this.
initial is this...
Can someone help me out please?
Change this products.length to products.where((product) => product.tag == 'chicken').length to do not put unexpected product into GridView.