Created
January 12, 2023 10:42
-
-
Save bizz84/94612fce6daf6e689d8caced1ba30685 to your computer and use it in GitHub Desktop.
SliverAlignedGrid bottom overflow bug when loading a CachedNetworkImage
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Note: this example needs the flutter_staggered_grid_view and cached_network_image packages to run correctly. | |
import 'dart:math'; | |
import 'package:cached_network_image/cached_network_image.dart'; | |
import 'package:flutter/material.dart'; | |
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; | |
void main() { | |
runApp(const MyApp()); | |
} | |
class MyApp extends StatelessWidget { | |
const MyApp({super.key}); | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
debugShowCheckedModeBanner: false, | |
theme: ThemeData( | |
primarySwatch: Colors.indigo, | |
), | |
home: const ProductsGridScreen(), | |
); | |
} | |
} | |
class ProductsGridScreen extends StatefulWidget { | |
const ProductsGridScreen({super.key, this.onPressed}); | |
final void Function(BuildContext, String)? onPressed; | |
@override | |
State<ProductsGridScreen> createState() => _ProductsGridScreenState(); | |
} | |
class _ProductsGridScreenState extends State<ProductsGridScreen> { | |
int _productsToShow = 1; | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar( | |
title: const Text('SliverAlignedGrid bug'), | |
actions: [ | |
TextButton( | |
onPressed: () => setState(() => _productsToShow = 1), | |
child: const Text( | |
'Reset', | |
style: TextStyle(color: Colors.white), | |
), | |
//label: const Text('Reset')), | |
), | |
], | |
), | |
body: CustomScrollView( | |
slivers: [ | |
ProductsSliverAlignedGrid( | |
itemCount: min(_productsToShow, kTestProducts.length), | |
itemBuilder: (_, index) { | |
final product = kTestProducts[index]; | |
return ProductCard(product: product); | |
}, | |
), | |
], | |
), | |
floatingActionButton: FloatingActionButton( | |
child: const Icon(Icons.add), | |
onPressed: () => setState(() => ++_productsToShow), | |
), | |
); | |
} | |
} | |
/// Used to show a single product inside a card. | |
class ProductCard extends StatelessWidget { | |
const ProductCard({super.key, required this.product, this.onPressed}); | |
final Product product; | |
final VoidCallback? onPressed; | |
// * Keys for testing using find.byKey() | |
static const productCardKey = Key('product-card'); | |
@override | |
Widget build(BuildContext context) { | |
return Card( | |
child: InkWell( | |
key: productCardKey, | |
onTap: onPressed, | |
child: Padding( | |
padding: const EdgeInsets.all(16), | |
child: Column( | |
crossAxisAlignment: CrossAxisAlignment.stretch, | |
children: [ | |
CachedNetworkImage(imageUrl: product.imageUrl), | |
// Note: wrapping this with `AspectRatio` prevents the layout jump | |
// and always renders correctly | |
// AspectRatio( | |
// aspectRatio: 1, | |
// child: CachedNetworkImage(imageUrl: product.imageUrl), | |
// ), | |
const SizedBox(height: 8), | |
const Divider(), | |
const SizedBox(height: 8), | |
Text(product.title, style: Theme.of(context).textTheme.headline6), | |
const SizedBox(height: 24), | |
Text('\$${product.price}', | |
style: Theme.of(context).textTheme.headline5), | |
const SizedBox(height: 4), | |
Text( | |
product.availableQuantity <= 0 | |
? 'Out of Stock' | |
: 'Quantity: ${product.availableQuantity}', | |
style: Theme.of(context).textTheme.caption, | |
) | |
], | |
), | |
), | |
), | |
); | |
} | |
} | |
class ProductsSliverAlignedGrid extends StatelessWidget { | |
const ProductsSliverAlignedGrid({ | |
super.key, | |
required this.itemCount, | |
required this.itemBuilder, | |
}); | |
/// Total number of items to display. | |
final int itemCount; | |
/// Function used to build a widget for a given index in the grid. | |
final Widget Function(BuildContext, int) itemBuilder; | |
@override | |
Widget build(BuildContext context) { | |
return SliverPadding( | |
padding: const EdgeInsets.all(16), | |
sliver: SliverAlignedGrid.count( | |
crossAxisCount: 3, | |
mainAxisSpacing: 24, | |
crossAxisSpacing: 24, | |
itemBuilder: itemBuilder, | |
itemCount: itemCount, | |
), | |
); | |
} | |
} | |
/// Class representing a product. | |
class Product { | |
const Product({ | |
required this.id, | |
required this.imageUrl, | |
required this.title, | |
required this.description, | |
required this.price, | |
required this.availableQuantity, | |
this.avgRating = 0, | |
this.numRatings = 0, | |
}); | |
/// Unique product id | |
final String id; | |
final String imageUrl; | |
final String title; | |
final String description; | |
final double price; | |
final int availableQuantity; | |
final double avgRating; | |
final int numRatings; | |
} | |
final kTestProducts = [ | |
const Product( | |
id: '1', | |
imageUrl: | |
'https://firebasestorage.googleapis.com/v0/b/ecommerce-app-scratch.appspot.com/o/products%2Fbruschetta-plate.jpg?alt=media&token=79a1ee8a-c626-42fb-9c5d-842b1db1ac53', | |
title: 'Bruschetta plate', | |
description: 'Lorem ipsum', | |
price: 15, | |
availableQuantity: 5, | |
), | |
Product( | |
id: '2', | |
imageUrl: | |
'https://firebasestorage.googleapis.com/v0/b/ecommerce-app-scratch.appspot.com/o/products%2Fmozzarella-plate.jpg?alt=media&token=f3609f0e-a44b-4d2d-99df-9ba08e87f990', | |
title: 'Mozzarella plate' * 2, | |
description: 'Lorem ipsum', | |
price: 13, | |
availableQuantity: 5, | |
), | |
Product( | |
id: '3', | |
imageUrl: | |
'https://firebasestorage.googleapis.com/v0/b/ecommerce-app-scratch.appspot.com/o/products%2Fpasta-plate.jpg?alt=media&token=0885722c-501b-4e49-a1cc-3fba2c86e9ce', | |
title: 'Pasta plate' * 3, | |
description: 'Lorem ipsum', | |
price: 17, | |
availableQuantity: 5, | |
), | |
const Product( | |
id: '4', | |
imageUrl: | |
'https://firebasestorage.googleapis.com/v0/b/ecommerce-app-scratch.appspot.com/o/products%2Fpiggy-blue.jpg?alt=media&token=ec9603ed-5db3-48b0-9bfe-42d7b473d809', | |
title: 'Piggy Bank Blue', | |
description: 'Lorem ipsum', | |
price: 12, | |
availableQuantity: 5, | |
), | |
Product( | |
id: '5', | |
imageUrl: | |
'https://firebasestorage.googleapis.com/v0/b/ecommerce-app-scratch.appspot.com/o/products%2Fpiggy-green.jpg?alt=media&token=9db42cd5-8d0a-4bab-9b67-5ff7244107e3', | |
title: 'Piggy Bank Green' * 2, | |
description: 'Lorem ipsum', | |
price: 12, | |
availableQuantity: 10, | |
), | |
Product( | |
id: '6', | |
imageUrl: | |
'https://firebasestorage.googleapis.com/v0/b/ecommerce-app-scratch.appspot.com/o/products%2Fpiggy-pink.jpg?alt=media&token=eaddea19-243c-4d45-9075-1b104241f862', | |
title: 'Piggy Bank Pink' * 3, | |
description: 'Lorem ipsum', | |
price: 12, | |
availableQuantity: 10, | |
), | |
const Product( | |
id: '7', | |
imageUrl: | |
'https://firebasestorage.googleapis.com/v0/b/ecommerce-app-scratch.appspot.com/o/products%2Fpizza-plate.jpg?alt=media&token=0099ff6d-b9e0-4fb7-8d82-118dfdd655a5', | |
title: 'Pizza plate', | |
description: 'Lorem ipsum', | |
price: 18, | |
availableQuantity: 10, | |
), | |
Product( | |
id: '8', | |
imageUrl: | |
'https://firebasestorage.googleapis.com/v0/b/ecommerce-app-scratch.appspot.com/o/products%2Fplate-and-bowl.jpg?alt=media&token=b371bda7-63a3-4e64-b490-94f55569cc05', | |
title: 'Plate and Bowl' * 2, | |
description: 'Lorem ipsum', | |
price: 21, | |
availableQuantity: 10, | |
), | |
Product( | |
id: '9', | |
imageUrl: | |
'https://firebasestorage.googleapis.com/v0/b/ecommerce-app-scratch.appspot.com/o/products%2Fsalt-pepper-lemon.jpg?alt=media&token=1806483a-b3f7-40d1-85fa-54b4332510f4', | |
title: 'Salt and pepper lemon' * 3, | |
description: 'Lorem ipsum', | |
price: 11, | |
availableQuantity: 10, | |
), | |
const Product( | |
id: '10', | |
imageUrl: | |
'https://firebasestorage.googleapis.com/v0/b/ecommerce-app-scratch.appspot.com/o/products%2Fsalt-pepper-olives.jpg?alt=media&token=fd28eba5-ac88-4ca3-bed1-4cca4447a131', | |
title: 'Salt and pepper olives', | |
description: 'Lorem ipsum', | |
price: 11, | |
availableQuantity: 10, | |
), | |
Product( | |
id: '11', | |
imageUrl: | |
'https://firebasestorage.googleapis.com/v0/b/ecommerce-app-scratch.appspot.com/o/products%2Fsnacks-plate.jpg?alt=media&token=64509406-adc1-45f4-af28-eb01f4cd24b3', | |
title: 'Snacks plate' * 2, | |
description: 'Lorem ipsum', | |
price: 24, | |
availableQuantity: 10, | |
), | |
Product( | |
id: '12', | |
imageUrl: | |
'https://firebasestorage.googleapis.com/v0/b/ecommerce-app-scratch.appspot.com/o/products%2Fflowers-plate.jpg?alt=media&token=1181a205-9cb9-41e1-a80c-f89886cd5e76', | |
title: 'Flowers plate' * 3, | |
description: 'Lorem ipsum', | |
price: 22, | |
availableQuantity: 10, | |
), | |
const Product( | |
id: '13', | |
imageUrl: | |
'https://firebasestorage.googleapis.com/v0/b/ecommerce-app-scratch.appspot.com/o/products%2Fjuicer-citrus-fruits.jpg?alt=media&token=00edf667-e957-42b3-9b15-a0e1aacc683d', | |
title: 'Juicer for citrus fruits', | |
description: 'Lorem ipsum', | |
price: 14, | |
availableQuantity: 10, | |
), | |
Product( | |
id: '14', | |
imageUrl: | |
'https://firebasestorage.googleapis.com/v0/b/ecommerce-app-scratch.appspot.com/o/products%2Fhoney-pot.jpg?alt=media&token=0de0f72f-c1e2-4422-a680-5e623dafec01', | |
title: 'Honey pot' * 2, | |
description: 'Lorem ipsum', | |
price: 16, | |
availableQuantity: 10, | |
), | |
]; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment