Card stuck at it's previous postion after performing search. Trying to search constitutions using the titles from an API - flutter

Constitution Card stuck at its position after performing search instead of moving to the top first position in the gridview. The searched card is supposed to appear at the the index[0] of the gridview where "Test Constitution" when filtered after performing search .
Could post images because of low reputution from Stackoverflow
Before Search
After Search
This's the code.
class _HomePageState extends State<HomePage> {
final ConstitutionController constitutionController = Get.find();
String search = "";
#override
Widget build(BuildContext context) {
final double height = MediaQuery.of(context).size.height;
return Scaffold(
appBar: AppBar(
backgroundColor: const Color(0xFF0B3C5D),
elevation: 0.0,
),
drawer: DrawerWidget(),
body: Stack(children: [
Container(
),
),
SafeArea(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Column(
children: [
Text(),
),
// The Search bar
Container(
margin: const EdgeInsets.symmetric(vertical: 30),
padding:
const EdgeInsets.symmetric(horizontal: 20, vertical: 8),
child: CupertinoSearchTextField(
onChanged: ((value) {
setState(() {
search = value;
}
);
}),
autofocus: true,
itemColor: Colors.black,
itemSize: 20,
backgroundColor: const Color.fromARGB(255, 185, 204, 218),
placeholderStyle: const TextStyle(
color: Colors.black,
fontSize: 18,
),
),
),
const SizedBox(
height: 70,
),
Flexible(
child: Obx(() {
if (constitutionController.isLoading.value) {
return GridView.builder(
itemBuilder: (_, __) {
return Shimmer.fromColors(
baseColor: Colors.grey[300]!,
highlightColor: Colors.grey[100]!,
period: Duration(seconds: 2),
enabled: constitutionController.isLoading.value,
child: ShimmerGridCard());
},
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2),
);
} else {
return GridView.builder(
itemCount:
constitutionController.constitutionList.length,
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2),
itemBuilder: (context, index) {
Constitution constitution =
constitutionController.constitutionList[index];
if (search.isEmpty) {
return InkWell(
onTap: (() {
Get.to(BranchPage(
constitutionId: constitution));
}),
child: ConstitutionCard(constitutionController
.constitutionList[index]));
}
if (constitution.title
.toLowerCase()
.contains(search)) {
return InkWell(
onTap: (() {
Get.to(BranchPage(
constitutionId: constitution));
}),
child: ConstitutionCard(constitutionController
.constitutionList[index]));
}
return null;
});
}
}),
),
],
),
),
),
]),
);
}
}

Related

setState inside FutureBuilder

I am designing a screen with a list of documents and I decided to use ToggleButtons on the header to filter the documents depending on the year they were published.
The information about the documents is loaded from an URL and I use a FutureBuilder to build the screen. And when I click on a ToggleButton, the setState doesn't work, it doesn't updates the screen. And I don't know why.
That way is impossible to filter the documents.
I share the code:
class ListOfDocuments extends StatefulWidget {
final SingleItem singleItem;
final String sectionRowName;
const ListOfDocuments(
{Key? key, required this.singleItem, required this.sectionRowName})
: super(key: key);
#override
State<ListOfDocuments> createState() => _ListOfDocumentsState();
}
class _ListOfDocumentsState extends State<ListOfDocuments> {
// Iniciamos el servicio para pedir a la web la información.
final HttpService httpService = HttpService();
// Iniciamos la variable donde almacenaremos los documentos.
Future<List<Document>>? listadoDocumentos;
// Obtenemos el código del idioma utilizado.
String myLocale = Intl.getCurrentLocale();
// Cargamos documentos.
void cargarDocumentos() async {
// Pedimos el listado de documentos.
listadoDocumentos = httpService.getDocList(
myLocale.toString(),
widget.sectionRowName,
widget.singleItem,
);
}
#override
void initState() {
super.initState();
// Cargamos los documentos al iniciar.
cargarDocumentos();
}
#override
Widget build(BuildContext context) {
// Screen size.
double screenWidth = MediaQuery.of(context).size.width;
double screenHeight = MediaQuery.of(context).size.height;
// For ToggleButtons.
List<Widget> years = <Widget>[
Padding(
padding: const EdgeInsets.all(4.0),
child: Text(S.of(context).all),
),
const Text("2005"),
const Text("2006"),
const Text("2007"),
const Text("2008"),
const Text("2009"),
const Text("2010"),
const Text("2011"),
const Text("2012"),
const Text("2013"),
];
List<bool> selectedYear = <bool>[
true,
false,
false,
false,
false,
false,
false,
false,
false,
false
];
return Scaffold(
backgroundColor: kPrimaryColor,
appBar: AppBar(
backgroundColor: Colors.transparent,
elevation: 0.0,
centerTitle: true,
title: getTitle(widget.sectionRowName, widget.singleItem.name, context),
),
body: FutureBuilder(
future: listadoDocumentos,
builder: (BuildContext context, AsyncSnapshot snapshotFromList) {
if (snapshotFromList.hasData) {
// Hacemos una lista de titulos con fechas.
List<Document> docList = List.generate(
snapshotFromList.data.length,
(index) => Document(
id: snapshotFromList.data[index].id,
fecha: snapshotFromList.data[index].fecha,
titulo: snapshotFromList.data[index].titulo,
),
);
return Column(
children: [
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Padding(
padding: const EdgeInsets.fromLTRB(10.0, 0.0, 0.0, 0.0),
child: ToggleButtons(
direction: Axis.horizontal,
textStyle: kAppBarStyle.copyWith(fontSize: 18.0),
borderColor: Colors.yellow,
borderWidth: 1.0,
selectedBorderColor: Colors.yellowAccent,
fillColor: Colors.yellowAccent.withOpacity(0.2),
color: Colors.white,
selectedColor: Colors.white,
isSelected: selectedYear,
borderRadius:
const BorderRadius.all(Radius.circular(10.0)),
onPressed: (int index) {
setState(() {
// The button that is tapped is set to true, and the others to false.
for (int i = 0; i < selectedYear.length; i++) {
selectedYear[i] = i == index;
}
});
},
children: years,
),
),
),
Expanded(
child: Padding(
padding:
EdgeInsets.fromLTRB(0.0, 0.0, screenWidth * 0.05, 0.0),
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: ListView.separated(
itemCount: docList.length,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
separatorBuilder: (BuildContext context, int index) {
return const Divider(
thickness: 2.0,
color: Colors.white10,
indent: 15.0,
);
},
itemBuilder: (context, index) {
DateTime dateFormated = DateFormat('dd/MM/yyyy')
.parse(docList[index].fecha);
return ListTile(
leading: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: [
Text(
DateFormat("dd").format(dateFormated),
style: kDocTitleInList.copyWith(
color: Colors.white70,
),
),
Text(
DateFormat("MMM")
.format(dateFormated)
.toUpperCase(),
style: kDocTitleInList.copyWith(
fontSize: 10.0,
color: Colors.white70,
),
),
Text(
DateFormat("yyyy").format(dateFormated),
style: kDocTitleInList.copyWith(
fontSize: 14.0,
color: Colors.white70,
),
),
],
),
title: Text(
docList[index].titulo,
style: kDocTitleInList,
),
trailing: const Icon(
Icons.arrow_forward_ios_rounded,
color: Colors.white,
),
onTap: () {
showDialog(
context: context,
builder: (BuildContext context) {
return FutureBuilder(
// Pedimos a la web el documento por el "id".
future: httpService.getDocument(
snapshotFromList.data[index].id
.toString()),
builder: (BuildContext context,
AsyncSnapshot snapshotFromDocument) {
if (snapshotFromDocument.hasData) {
return Dialog(
backgroundColor: Colors.transparent,
elevation: 0.0,
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius:
BorderRadius.circular(30.0),
),
child: Column(
mainAxisAlignment:
MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Padding(
padding:
const EdgeInsets.all(
15.0),
child: Text(
snapshotFromList
.data[index].titulo,
style: kSubTitleStyle
.copyWith(
color:
Colors.black),
),
),
Padding(
padding:
const EdgeInsets.all(
15.0),
child: FlagChips(
documento:
snapshotFromDocument
.data),
),
GestureDetector(
child: Text(
'- ${S.of(context).cerrarVentana} -',
style: kSubTitleStyle
.copyWith(
color:
Colors.black),
),
onTap: () {
Navigator.of(context)
.pop();
},
),
const SizedBox(
height: 20.0,
),
],
),
),
);
} else {
return const Center(
child: CircularProgressIndicator(
color: Colors.white54,
),
);
}
});
},
);
},
);
},
),
),
),
),
],
);
} else {
return const Center(
child: CircularProgressIndicator(
color: Colors.white54,
),
);
}
},
),
);
}
}
The issue is that your selectedYear variable is declared within the scope of the build function. Since this is your "state" of your stateful widget, you should be declaring this within the body of your _ListOfDocumentsState. Just move this variable outside of build to be with your httpService, listadoDocumentos, and myLocale variables.
class _ListOfDocumentsState extends State<ListOfDocuments> {
...(other function and variable declarations)
List<bool> selectedYear = <bool>[
true,
false,
false,
false,
false,
false,
false,
false,
false,
false
];
#override
Widget build(BuildContext context) {
// Screen size.
double screenWidth = MediaQuery.of(context).size.width;
double screenHeight = MediaQuery.of(context).size.height;
// For ToggleButtons.
List<Widget> years = <Widget>[
Padding(
padding: const EdgeInsets.all(4.0),
child: Text(S.of(context).all),
),
const Text("2005"),
const Text("2006"),
const Text("2007"),
const Text("2008"),
const Text("2009"),
const Text("2010"),
const Text("2011"),
const Text("2012"),
const Text("2013"),
];
...(and the rest of your build function)
With your current method, your loop updates the selectedYear list as you intend, but calling setState calls build again, which resets the selectedYear to the original state with the first element being true.
You can define it on a Future method and call your Future method on you button.
Future<String> _reloadState() {
setState(() {});
}
And also you can fetch your data from an API if you have then call setState function to reload your state.

How to make Flutter GridView change colors when tapped

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,
),
),
),
),
],
),
);
}
}

Grivdview search not working properly flutter

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.

The argument type 'Future<>' can't be assigned to the parameter type ''

I am trying to build a GridView list. This all works when I create a simple list of 'players', but now I am trying to persist data using sqflite and I am getting an error:
Error: The argument type 'Future' can't be assigned to the parameter type 'int'
This is my list builder widget:
class PlayerList extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Consumer<PlayerData>(
builder: (context, playerData, child) {
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
mainAxisSpacing: 20,
crossAxisSpacing: 20,
),
itemCount: playerData.playerCount,
itemBuilder: (BuildContext context, int index) {
final player = playerData.players[index];
return RawMaterialButton(
fillColor: Color(0x991c5597),
elevation: 20,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
CircleAvatar(
backgroundColor: Colors.tealAccent,
radius: 30,
child: Icon(
Icons.star,
color: Color(0xFF16377a),
size: 30,
),
),
Text(player.name, style: smallTitleText),
],
),
),
onPressed: () {
playerData.makeActive(player.name);
playerData.printList();
Navigator.pushNamed(context, PlayScreen.id);
});
});
},
);
}
}
This is how I get the list count:
Future<int> get playerCount async {
final Database db = await getDBConnector();
final List<Map<String, dynamic>> maps = await db.query('players');
if (maps.length != null) {
return maps.length;
} else { return 0;}
}
Wrap your GridView with FutureBuilder
Something like this
return FutureBuilder(
future: playerData.playerCount,
builder : (context,snapshot){
if(!(snapshot.hasData)){
return Container();
}
else{
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
mainAxisSpacing: 20,
crossAxisSpacing: 20,
),
itemCount: snapshot.data,
itemBuilder: (BuildContext context, int index) {
final player = playerData.players[index];
return RawMaterialButton(
fillColor: Color(0x991c5597),
elevation: 20,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
CircleAvatar(
backgroundColor: Colors.tealAccent,
radius: 30,
child: Icon(
Icons.star,
color: Color(0xFF16377a),
size: 30,
),
),
Text(player.name, style: smallTitleText),
],
),
),
onPressed: () {
playerData.makeActive(player.name);
playerData.printList();
Navigator.pushNamed(context, PlayScreen.id);
});
});
},
);
}
}
);
The error is appearing because playerCount isn't returning int its returning a Future. First, you have to change your class to a stateful widget and load your data as shown below
class PlayerList extends StatefulWidget {
#override
_PlayerListState createState() => _PlayerListState();
}
class _PlayerListState extends State<PlayerList> {
int count;
#override
void initState() {
super.initState();
loadPlayerCount();
}
#override
Widget build(BuildContext context) {
return Consumer<PlayerData>(
builder: (context, playerData, child) {
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
mainAxisSpacing: 20,
crossAxisSpacing: 20,
),
itemCount: count??0,
itemBuilder: (BuildContext context, int index) {
final player = playerData.players[index];
return RawMaterialButton(
fillColor: Color(0x991c5597),
elevation: 20,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
CircleAvatar(
backgroundColor: Colors.tealAccent,
radius: 30,
child: Icon(
Icons.star,
color: Color(0xFF16377a),
size: 30,
),
),
Text(player.name, style: smallTitleText),
],
),
),
onPressed: () {
playerData.makeActive(player.name);
playerData.printList();
Navigator.pushNamed(context, PlayScreen.id);
});
});
},
);
}
void loadPlayerCount() async{
final Database db = await getDBConnector();
final List<Map<String, dynamic>> maps = await db.query('players');
if (maps.length != null) {
setState(() {
this.count = maps.length;
});
} else {
setState(() {
this.count = 0;
});
}
}
}

I have a problem with gridview display in flutter

You can see under my work, it is a lotto, a mini game in dart, user select 5 numbers and can validate his choice, the big 5 circles are the number selected but it is too too big and not render correctly in the blue rectangle :
I have a problem with the 5 Numbers in red just Under "Vos numéros", it is too big and i can't reduce the size in the code, it seems gridview adapt the size automatically, do you have any idea ?
import 'package:flutter/material.dart';
import 'dart:math';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'dart:async';
import 'package:flutter_app/menu_member.dart';
import 'package:flutter_app/globals.dart' as globals;
class Lotto extends StatefulWidget {
#override
_LottoState createState() => new _LottoState();
}
class _LottoState extends State<Lotto> {
#override
void initState() {
super.initState();
}
var i=1;
var nb_num=49;
var no_select=[];
var no_a_select=5;
List<Color> colorList = List<Color>.generate(49, (int index) => Colors.lightBlue);
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: new AppBar(
title: new Text('GRILLE DE LOTTO'),
),
body:
Center(
child: Column(
children: <Widget>[
Container(
width:400,
height:30,
margin: const EdgeInsets.only(top: 10.0),
child : new Text("Selectionnez 5 numéros",textAlign: TextAlign.center,style: TextStyle(fontSize: 30.0),),
),
Container(
width:400,
height:300,
child: new GridView.count(
crossAxisCount: 9,
padding: const EdgeInsets.all(30.0),
mainAxisSpacing: 4.0,
crossAxisSpacing: 4.0,
children: new List<Widget>.generate(49, (index) {
return new GestureDetector(
onTap: () {
setState(() {
if (colorList[index] == Colors.lightBlue) {
if (no_select.length<no_a_select) {
colorList[index] = Colors.redAccent;
no_select.add(index+1);
}
else {
showDialog(
context: context,
builder: (BuildContext context){
return AlertDialog(
title: Text("INFORMATION"),
content: Text("Vous ne pouvez pas sélectionner plus de 5 numéros !!!"),
);
}
);
}
print(no_select);
}
else {
colorList[index] = Colors.lightBlue;
no_select.remove(index+1);
print(no_select);
}
});
},
child: Container(
child: ClipOval(
child: Container(
color: colorList[index],
height: 20.0,
width: 20.0,
child: Center(
child: new Text((index+1).toString(),
style: TextStyle(color: Colors.white, fontSize: 24),
textAlign: TextAlign.center),
),
),
),
),
);
}
),
),
),
Container(
width:400,
height:30,
margin: const EdgeInsets.only(top: 10),
child : new Text("Vos Numéros",textAlign: TextAlign.center,style: TextStyle(fontSize: 30.0),),
),
Container(
width:400,
height:80,
margin: const EdgeInsets.only(top: 10.0),
decoration: BoxDecoration(
border: Border.all(
color: Colors.lightBlueAccent,
width: 2,
),
borderRadius: BorderRadius.circular(12),
),
child:
getWidget()
),
Container(
width:300,
height:45,
margin: const EdgeInsets.only(top: 10.0),
child:
RaisedButton(
color: Colors.green,
textColor: Colors.white,
padding: EdgeInsets.fromLTRB(9, 9, 9, 9),
child: Text('TIRAGE ALEATOIRE'),
onPressed: () {
Select_numbers();
},
),
),
Container(
width:300,
height:45,
margin: const EdgeInsets.only(top: 10.0),
child:
RaisedButton(
color: Colors.green,
textColor: Colors.white,
padding: EdgeInsets.fromLTRB(9, 9, 9, 9),
child: Text('VALIDER VOTRE GRILLE'),
onPressed: () {
Valide_grille();
},
),
),
]
)
),
),
);
}
getWidget() {
if (no_select.length==0) {
return Text("Pas de numéros");
}
else {
return GridView.count(
crossAxisCount: 5,
padding: const EdgeInsets.all(10.0),
mainAxisSpacing: 4.0,
crossAxisSpacing: 4.0,
children: new List<Widget>.generate(no_select.length, (index) {
return ClipOval(
child: Container(
color: Colors.red,
height: 20.0,
width: 20.0,
child: Center(
child: new Text((no_select[index].toString()),
style: TextStyle(color: Colors.white, fontSize: 24),
textAlign: TextAlign.center),
),
),
);
}
)
);
}
}
Select_numbers() {
setState(() {
var j = 1;
var num_sel;
var pos_sel;
no_select=[];
colorList=[];
colorList=List<Color>.generate(49, (int index) => Colors.lightBlue);
var rng = new Random();
List tab=[];
tab = List.generate(49, (int index) => index + 1);
print (tab);
while (j <= no_a_select) {
pos_sel = rng.nextInt(tab.length-1);
num_sel=tab[pos_sel];
no_select.add(num_sel);
colorList[num_sel-1] = Colors.redAccent;
tab.remove(num_sel);
print(tab);
j++;
}
print(no_select);
});
}
Future Valide_grille() async{
// For CircularProgressIndicator.
bool visible = false ;
// Showing CircularProgressIndicator.
setState(() {
visible = true ;
});
// SERVER LOGIN API URL
var url = 'https://www.easytrafic.fr/game_app/valide_lotto.php';
// Store all data with Param Name.
var data = {'id_membre':globals.id_membre, 'result':no_select};
print (data);
var grille_encode=jsonEncode(data);
print(grille_encode);
// Starting Web API Call.
var response = await http.post(url, body: grille_encode,headers: {'content-type': 'application/json','accept': 'application/json','authorization': globals.token});
print(response.body);
// Getting Server response into variable.
var message = json.decode(response.body);
// If the Response Message is Matched.
if(message == 'OK')
{
print('VALIDATION DE LA GRILLE OK');
// Hiding the CircularProgressIndicator.
setState(() {
visible = false;
});
}else{
// Hiding the CircularProgressIndicator.
setState(() {
visible = false;
});
// Showing Alert Dialog with Response JSON Message.
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: new Text(message),
actions: <Widget>[
FlatButton(
child: new Text("OK"),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
}
}
It's because you put a Padding on your Gridview which make it scrollable
You can put the Padding to your ClipOval elements instead
return GridView.count(
crossAxisCount: 5,
mainAxisSpacing: 4.0,
crossAxisSpacing: 4.0,
children: new List<Widget>.generate(
no_select.length,
(index) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: ClipOval(
child: Container(
color: Colors.red,
height: 20.0,
width: 20.0,
child: Center(
child: new Text((no_select[index].toString()),
style: TextStyle(color: Colors.white, fontSize: 24), textAlign: TextAlign.center),
),
),
),
);
},
),
);