How to fix "Could not find the correct Provider<Product> above this ProductItem Widget "? - flutter

I am very much new to flutter. This is my first time using providers. I have been through every solution out there but nothing seems to help.
In a shopping app I am developing, I tried to use the provider package to pass data to my widget dynamically.
I am sharing the code and the screenshot of the very big error message. Hope somebody could help
Gridview builder
final productData = Provider.of<ProductsProvider>(context);
final products = productData.items;
return GridView.builder(
padding: EdgeInsets.all(10),
itemCount: products.length,
itemBuilder: (context, index) => ChangeNotifierProvider.value(
value: products[index], child: ProductItem()),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 0.75,
crossAxisSpacing: 10,
mainAxisSpacing: 10),
);
Individual product item in the grid
final product = Provider.of<Product>(context);
return Container(
child: GestureDetector(
onTap: () {
Navigator.of(context)
.pushNamed(ProductDetails.routeName, arguments: product.id);
},
child: Card(
color: Colors.white,
elevation: 6,
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
//product image
Image(image: AssetImage(product.imageUrl)),
Padding(
padding: EdgeInsets.symmetric(vertical: 0, horizontal: 10),
//item name and fav button
child: Flex(
direction: Axis.horizontal,
children: <Widget>[
Expanded(
child: Text(
product.name,
style: Theme.of(context)
.textTheme
.headline1
.copyWith(fontSize: 16, color: Colors.black),
)),
IconButton(
icon: Icon(
product.isFav
? Icons.favorite
: Icons.favorite_border,
color: Colors.red,
),
onPressed: () {
product.toggleFav();
})
],
),
),
// price and buy button
Padding(
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 0),
child: Flex(
direction: Axis.horizontal,
children: <Widget>[
Expanded(
child: Text(
"\$ " + product.price.toString(),
style: Theme.of(context).textTheme.headline1.copyWith(
color: Theme.of(context).primaryColor, fontSize: 15),
)),
SizedBox(
width: 60,
child: RaisedButton(
onPressed: () {},
color: Colors.orange,
child: Text(
"Buy",
style: TextStyle(fontFamily: 'Prompt', fontSize: 14),
),
),
)
],
),
),
],
),
),
),
);
screenshot of error

You need to declare the provider in you main material widget. Atm you are using it but you haven't told flutter that you will use it.
https://flutter.dev/docs/development/data-and-backend/state-mgmt/simple#changenotifierprovider

Related

Flutter: Make all screen scrollable with GridView.builder inside

In my home screen my app shows carousel first then a vertical list of challenges cards retrieved from Cloud Firestore using GridView.builder as follows:
GridView.builder(
scrollDirection: Axis.vertical,
itemCount: _challenges.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 1,
childAspectRatio: MediaQuery.of(context).size.width /
(MediaQuery.of(context).size.height / 4),
),
itemBuilder: (context, index) {
return InkWell(
onTap: () {
if (_challenges[index]["isLocked"] == "true") {
showLockedDialog();
} else {
checkParticipation(index);
if (checkPart == true) {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) =>
ChallengeDetails(_challenges[index])));
}
checkPart = true;
}
},
child: Stack(
children: [
Center(
child: ClipRRect(
borderRadius: BorderRadius.circular(15),
child: Image(
image: NetworkImage(_challenges[index]["image-path"]),
fit: BoxFit.cover,
height: 150,
width: 350,
opacity: _challenges[index]["isLocked"] == "true"
? AlwaysStoppedAnimation(.4)
: null,
),
),
),
Center(
child: Text(
"${_challenges[index]["name"]}\n",
style: TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.bold),
),
),
Center(
child: Text(
"\n${_challenges[index]["date"]}",
style: TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.bold),
textDirection: TextDirection.ltr,
)),
Center(
child: SizedBox(
height: 130,
width: 130,
child: _challenges[index]["isLocked"] == "true"
? Image.asset("assets/lock-icon.jpg")
: null,
),
)
],
),
);
});
Everything retrieving fine and it is rendered in my home_screen as follows:
body: Column(
children: [
AdsBanner(),
SizedBox(
height: 30,
),
Padding(
padding: const EdgeInsets.only(right: 8, left: 8, bottom: 5),
child: Row(
children: [
Text(
AppLocalizations.of(context)!.challenges + " ",
style: TextStyle(fontSize: 20),
),
Text(
AppLocalizations.of(context)!.clickToParticipate,
style: TextStyle(fontSize: 15),
)
],
),
),
Expanded(child: ChallengeCard()),
],
),
The problem is that only the GridView area is scrolling and what am seeking for is to scroll the whole screen with the GridView area, I was trying to use the CustomScrollView() but its not working properly.
I'll be thankful for any help.
First in your GridView.builder add these:
GridView.builder(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
...
)
then in your home_screen wrap your column with SingleChildScrollView:
SingleChildScrollView(
child: Column(
children: [
AdsBanner(),
SizedBox(
height: 30,
),
Padding(
...
),
),
You can provide physics: NeverScrollableScrollPhysics() on GridView to disable scroll effect. If you want scrollable as secondary widget use primary: false, to have Full Page scrollable, you can use body:SingleChildScrollView(..) or better using body:CustomScrollView(..)

GridView.count() doesn't scroll - Flutter

I want the Card widgets to scroll horizontally, but it's not working. I've tried and changed GridView to ListView.
Adding physics is not helping either.
Here's the code:
#override
Widget build(BuildContext context) {
SizeConfig().init(context);
return Expanded(
child: Column(
children: [
Padding(
padding: EdgeInsets.symmetric(
vertical: SizeConfig.blockSizeVertical * 2),
child: (Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Names',
style: Theme.of(context).textTheme.headline6,
),
InkWell(
onTap: () {},
child: Row(children: [
Text('show more',
style: Theme.of(context).textTheme.caption),
const Padding(
padding: EdgeInsets.only(top: 2),
child: Icon(
Icons.keyboard_arrow_left_sharp,
size: 20,
),
),
]),
),
],
)),
),
FutureBuilder<DataModel>(
future: InfoAPI('assets/itemsData.json').getDataLocally(context),
builder: (context, snapshot) {
final data = snapshot.data;
final List<String> list = getnames(data);
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Theme.of(context).primaryColor,
),
height: SizeConfig.blockSizeVertical * 20,
// width: double.infinity,
child: GridView.count(
crossAxisCount: 1,
padding: const EdgeInsets.symmetric(
vertical: 8.0, horizontal: 10.0),
scrollDirection: Axis.horizontal,
childAspectRatio: 1,
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
children: List.generate(list.length, (index) {
return Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16)),
//clipBehavior: Clip.antiAlias,
color: Theme.of(context).cardColor,
child: Center(
child: Text(
list[index],
style: Theme.of(context).textTheme.bodyText1,
)));
},
)),
);
})
],
),
);
}```
So, after long research I discovered that the 'horizontal scrolling' is working just fine on mobile, the problem was because I used chrome.
You can refer to [Horizontal listview not scrolling on web but scrolling on mobile] for more information.
Try wrapping your ListView/GridView with a Flexible Widget. Also Youve set physics to NeverScrollableScrollPhysics() so change it to some other scroll physics(like AlwaysScrollableScrollPhysics()) if you want it to be scrollable

set left widget as focused one in flutter

I am using card_swiper widget in my project. It is a good library, but there is a problem: the focused widget always stays on centre but I want the focused item to be the one on the left. How can I accomplish this with this library?
In the below picture, focused item is on centre, but yellow item, in my case, should be on the left.
I found the answer which could be useful for others. The example is for 4 items on view but you can customise it to as many as you want:
Scaffold(
backgroundColor: AppColors.purpleDark,
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: [
Flexible(
child: Swiper(
index: chosenIndex,
layout: SwiperLayout.CUSTOM,
physics: BouncingScrollPhysics(),
customLayoutOption: CustomLayoutOption(
startIndex: 0,
stateCount: math.min(4, listSize),
).addScale(
[3, 1, 1, 1], // there should be 4 widgets in view with the first 3 times larger
Alignment.topRight,
).addTranslate( // place the 4 items as I want
[
Offset(-35.toResizableWidth(), 0),
Offset(30.toResizableWidth(), 0.0),
Offset(118.toResizableWidth(), 0.0),
Offset(200.toResizableWidth(), 0.0),
],
),
itemWidth: 51.toResizableWidth(),
itemHeight: 51.toResizableHeight(),
controller: controller,
itemCount: listSize,
onIndexChanged: (newIndex) => setState(() => chosenIndex = newIndex),
itemBuilder: (context, index) {
return GestureDetector(
onTap: () => controller.next(),
child: Container(
width: 51.toResizableWidth(),
height: 51.toResizableHeight(),
color: Colors.grey,
child: Center(
child: Text(
'$index',
style: AppTextStyles.styleS16W600.copyWith(color: Colors.white),
),
),
),
);
},
),
),
Padding(
padding: EdgeInsets.symmetric(
vertical: 10.toResizableHeight(),
horizontal: 20.toResizableWidth(),
),
child: Text(
LocaleKeys.rewards_day.tr(args: <String>[chosenIndex.toString()]),
style: AppTextStyles.styleS20W600
.copyWith(color: true ? AppColors.greenPositive : AppColors.redNegative),
),
),
Padding(
padding: EdgeInsets.symmetric(
horizontal: 20.toResizableWidth(),
),
child: Text(
longText,
style: AppTextStyles.styleS16W400.copyWith(color: AppColors.white100),
),
),
Expanded(child: Container()),
],
),
);

Flutter how to remove gridview top space

I am getting a space on top of my gridview. I have tried to remove it without success.
It looks like this:
What I want:
Here is my code:
Container(
margin: EdgeInsets.all(10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
"Check",
style: TextStyle(color: Colors.black, fontSize: 18, fontWeight: FontWeight.bold),
textAlign: TextAlign.left,
),
GridView.count(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
crossAxisCount: 1,
crossAxisSpacing: 2,
mainAxisSpacing: 10,
childAspectRatio: 5.1,
children: <Widget>[
GestureDetector(
child: _buildWidget("Car", 0),
onTap: () => setState(() => _languageIndex = 0),
),
GestureDetector(
child: _buildWidget("Boat", 1),
onTap: () => setState(() => _languageIndex = 1),
),
],
),
],
));
Widget _buildWidget(String language, int index) {
bool isSelected = _languageIndex == index;
return Container(
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4),
border: Border.all(color: isSelected ? Colors.blue.withOpacity(1.0) : Colors.black26),
color: isSelected ? Colors.white.withOpacity(0.0) : Colors.white
),
child:
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(width:10),
Text(
language,
style: TextStyle(fontSize: 16,fontWeight: FontWeight.bold, color: isSelected ? Colors.black : Colors.black26),
),
SizedBox(width:20),
],)
);
}
What can I do to remove the space between the text and the gridview? Or can I do it in another way to get the output I am looking for?
The GridView widget has a default padding, you can remove the padding by giving it a padding of EgdeInsets.zero.
GridView.count(
padding: EdgeInsets.zero // set padding to zero
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
crossAxisCount: 1,
crossAxisSpacing: 2,
mainAxisSpacing: 10,
childAspectRatio: 5.1,
children: <Widget>[
GestureDetector(
child: _buildWidget("Car", 0),
onTap: () => setState(() => _languageIndex = 0),
),
GestureDetector(
child: _buildWidget("Boat", 1),
onTap: () => setState(() => _languageIndex = 1),
),
],
),

Flutter/Dart: Create a GridView where the first item is a unique grid item

I am new to Flutter and Dart but I have a grid view on one of my screens and I want the first grid item to be a unique button that allows the user to add a student. You can see in the image below an example of what I am trying to accomplish. How do I create a unique view with the first item allowing a user to add a student?
Grid Class:
body: Padding(
padding: const EdgeInsets.only(left: 15.0, right: 15.0),
child: Column(
children: <Widget>[
ProfileHeader(),
ProfileViewSwitch(),
ProfileSearch(),
Flexible(
//Below is the GridView used for each student in the class
child: GridView(
//TODO convert to a builder for performance efficiency
padding: const EdgeInsets.only(top: 10.0),
children: DUMMY_CATEGORIES
.map(
(catData) => ImageItem(
catData.firstName,
catData.lastName,
catData.color,
catData.initials,
),
)
.toList(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
//maxCrossAxisExtent: 150,
crossAxisCount: 3,
childAspectRatio: 1,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
),
),
),
],
),
),
Image Item Class:
Widget build(BuildContext context) {
return InkWell(
onTap: () => selectCategory(context),
borderRadius: BorderRadius.circular(15),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
child: new Column(
children: <Widget>[
ClipOval(
child: Material(
color: color, // button color
child: InkWell(
//splashColor: Colors.red, // inkwell color
child: SizedBox(
width: 80,
height: 80,
child: Align(
alignment: Alignment.center,
child: Text(initials,
textAlign: TextAlign.center,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 35)),
),
),
),
),
)
],
),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
//crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
child: new Column(
children: <Widget>[
new Text(
firstName,
textAlign: TextAlign.center,
style:
TextStyle(fontWeight: FontWeight.bold, fontSize: 15),
),
new Text(
lastName,
textAlign: TextAlign.center,
style:
TextStyle(fontWeight: FontWeight.bold, fontSize: 15),
),
],
),
),
],
),
],
),
);
}
}
You can achieve the desired result in two ways,
Method 1: Using GridView,builder, return your Button at index 0 - I'd recommend to using this method.
GridView.builder(
//TODO convert to a builder for performance efficiency
padding: const EdgeInsets.only(top: 10.0),
itemCount: DUMMY_CATEGORIES.length + 1,
itemBuilder: (context, i) {
if (i == 0) {
return IconButton(
icon: Icon(Icons.add),
onPressed: () {},
);
}
final catData = DUMMY_CATEGORIES[i - 1];
return ImageItem(
catData.firstName,
catData.lastName,
catData.color,
catData.initials,
);
},
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
//maxCrossAxisExtent: 150,
crossAxisCount: 3,
childAspectRatio: 1,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
),
)
Method 2: Concat a null or unique value in from and handle during map
GridView(
//TODO convert to a builder for performance efficiency
padding: const EdgeInsets.only(top: 10.0),
children: [null, ...DUMMY_CATEGORIES].map(
(catData) {
if (catData == null) {
return IconButton(
icon: Icon(Icons.add),
onPressed: () {},
);
}
return ImageItem(
catData.firstName,
catData.lastName,
catData.color,
catData.initials,
);
},
).toList(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
//maxCrossAxisExtent: 150,
crossAxisCount: 3,
childAspectRatio: 1,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
),
)
Hope that helps!