In this code i am encountering an issue within a PageView.builder widget in a Flutter app. The PageView.builder is meant to display multiple product images in a horizontal scrollable manner. However, the images beyond the initial view (images indexed after the first few) are not displaying as expected when swiping through the images. Instead, the screen flickers or doesn’t update to show the subsequent images beyond the first few.
Despite confirming the presence of the images through debugging (the correct imageUrl values are retrieved), the PageView.builder seems to have trouble rendering these images beyond the initial ones visible on load. We’ve ensured that the state is updated correctly by using productNotifier.activePage in onPageChanged and triggering a rebuild with setState(). Despite this, the subsequent images fail to display properly within the PageView.builder.
The goal is to have a smooth horizontal scroll functionality within the PageView.builder, allowing users to view and navigate through all product images seamlessly. We’re seeking insights into why the subsequent images are not rendering or updating properly when swiping through the images and how to resolve this issue effectively.
here is the ProductPage code: // i have reduce the complexity in the code because it was too long
class ProductPage extends StatefulWidget {
const ProductPage({super.key, required this.id, required this.category});
final String id;
final String category;
@override
State<ProductPage> createState() => _ProductPageState();
}
class _ProductPageState extends State<ProductPage> {
final PageController pageController = PageController();
@override
Widget build(BuildContext context) {
var favouritesNotifier = Provider.of<FavoritesNotifier>(context);
favouritesNotifier.getFavorites();
var productNotifier = Provider.of<ProductNotifier>(context);
productNotifier.getProducts(widget.category, widget.id);
return Scaffold(
body: FutureBuilder<Products>(
future: productNotifier.products,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
} else if (snapshot.hasError) {
return Text("Error ${snapshot.error}");
} else {
final products = snapshot.data;
return Consumer<ProductNotifier>(
builder: (context, productNotifier, child) {
return CustomScrollView(
slivers: [
SliverAppBar(
automaticallyImplyLeading: false,
leadingWidth: 0,
title: Padding(
padding: const EdgeInsets.only(bottom: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
GestureDetector(
onTap: () {
Navigator.pop(context);
// productNotifier.productSizes.clear();
},
child: const Icon(Ionicons.close),
),
GestureDetector(
onTap: null,
child: const Icon(Ionicons.ellipsis_horizontal),
),
],
),
),
pinned: true,
snap: false,
floating: true,
backgroundColor: Colors.transparent,
expandedHeight: MediaQuery.of(context).size.height,
flexibleSpace: FlexibleSpaceBar(
background: Stack(
children: [
SizedBox(
height: MediaQuery.of(context).size.height * 0.5,
width: MediaQuery.of(context).size.width,
child: PageView.builder(
itemCount: products!.imageUrl.length,
scrollDirection: Axis.horizontal,
controller: pageController,
onPageChanged: (page) {
productNotifier.activePage = page;
setState(() {});
},
itemBuilder: (context, int index) {
return Stack(
children: [
Container(
height:
MediaQuery.of(context).size.height *
0.39,
width:
MediaQuery.of(context).size.width,
color: Colors.grey.shade300,
child: CachedNetworkImage(
imageUrl: products.imageUrl[index],
fit: BoxFit.contain,
),
),
Positioned(
top: MediaQuery.of(context)
.size
.height *
0.06,
right: 15,
child: Consumer<FavoritesNotifier>(
builder: (context,
favoritesNotifier, child) {
return GestureDetector(
onTap: () {
if (favoritesNotifier.ids
.contains(widget.id)) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
const Favorites(),
),
);
} else {
favouritesNotifier
.createFav({
"id": products.id,
"name": products.name,
"category":
products.category,
"price": products.price,
"imageUrl":
products.imageUrl[0],
});
}
setState(() {});
},
child: favoritesNotifier.ids
.contains(products.id)
? const Icon(Ionicons.heart)
: const Icon(
Ionicons.heart_outline),
);
},
)),
Positioned(
bottom: 0,
right: 0,
left: 0,
height:
MediaQuery.of(context).size.height *
0.3,
child: Row(
mainAxisAlignment:
MainAxisAlignment.center,
children: List<Widget>.generate(
products.imageUrl.length,
(index) => Padding(
padding:
const EdgeInsets.symmetric(
horizontal: 4),
child: CircleAvatar(
radius: 5,
backgroundColor: productNotifier
.activePage !=
index
? Colors.grey
: Colors.black,
),
),
),
),
),
],
);
},
),
),
],
),
),
),
],
);
},
);
}
},
),
);
}
}
here is the ProductNotifier class code:
class ProductNotifier extends ChangeNotifier {
int _activePage = 0;
List<dynamic> _productSizes = [];
List<String> _sizes = [];
final _cartBox = Hive.box('cart_box');
int get activePage => _activePage;
set activePage(int newIndex) {
_activePage = newIndex;
notifyListeners();
}
late Future<List<Products>> male;
late Future<List<Products>> female;
late Future<List<Products>> vintageAndRetroStyle;
late Future<Products> products;
void getMale() {
male = Helper().getMaleProducts();
}
void getFemale() {
female = Helper().getFemaleProducts();
}
void getVintageAndRetroStyle() {
vintageAndRetroStyle = Helper().getVintageAndRetroProducts();
}
List<dynamic> get productSizes => _productSizes;
set productSizes(List<dynamic> newSizes) {
_productSizes = newSizes;
notifyListeners();
}
void toggleCheck(int index) {
for (int i = 0; i < _productSizes.length; i++) {
if (i == index) {
_productSizes[i]['isSelected'] = !_productSizes[i]['isSelected'];
}
}
notifyListeners();
}
Future<void> createCart(Map<String, dynamic> newCart) async {
await _cartBox.add(newCart);
}
List<String> get sizes => _sizes;
set sizes(List<String> newSizes) {
_sizes = newSizes;
notifyListeners();
}
void getProducts(String category, String id) {
if (category == "Men's Fashion") {
products = Helper().getMaleProductsById(id);
} else if (category == "Women's Fashion") {
products = Helper().getFemaleProductsById(id);
} else {
products = Helper().getVintageAndRetroProductsById(id);
}
}
}
What I tried:
Implemented a PageView.builder widget to display product images horizontally.
Confirmed correct retrieval of image URLs and data through debugging.
Used productNotifier.activePage in onPageChanged to trigger a rebuild with setState().
Ensured that the state updates properly upon swiping through images.
Expected Outcome:
I expected the PageView.builder to smoothly render and display all product images, allowing seamless horizontal navigation through each image.
Actual Result:
The initial set of images displays correctly. However, when swiping through to view subsequent images beyond the initially visible ones, the screen either flickers or fails to update/render these images properly within the PageView.builder. Despite confirming the presence of the images (correct image URLs retrieved), the subsequent images are not displaying as expected during the horizontal scroll.
2
Answers
Image BoxFit Property:
Adjust the fit property of the Image widget to make sure the image is properly fitted within its container. The fit property controls how the image should be inscribed into the box.
Container Size:
PageView Controller:
CachedNetworkImage:
Debugging:
Rebuild Issue:
The issue you are encountering might be related to the asynchronous nature of loading images and the way the CachedNetworkImage widget works. To address this, you can try a few optimizations in your code:
Preload images before displaying them in the PageView.builder. This can be done by using the precacheImage method from the Image class. You can call this method in the initState or when the product data is loaded.