Display featured books data
Presentation (Integration)
Summary
This lesson focuses on displaying actual remote image data within the UI using the cached_network_image package. It explains why relying on default network image widgets is inefficient and demonstrates how to implement proper image caching to save bandwidth and improve user experience. The lesson also covers passing domain entities (BookEntity) down to the presentation layer widgets and handling common Flutter compilation issues like MissingPluginException after adding native dependencies, as well as restoring UI styling using ClipRRect.
Key Ideas
- Passing data from the state (state.books) down to specific UI widgets requires updating their constructors to accept List<BookEntity>.
- Default NetworkImage caching relies on URLs rather than persistent files, leading to redundant data fetching and poor UX.
- The cached_network_image package fetches and caches images persistently, improving performance for repeated loads.
- Adding new packages with native dependencies requires a full application stop and restart to avoid a MissingPluginException.
- Handling potential null values in entity properties (e.g., books[index].image ?? '') prevents runtime null assertion errors.
- ClipRRect applies rounded corners (borderRadius) to widgets like CachedNetworkImage that do not possess built-in Decoration properties.
Code Snippets
class CustomBookImage extends StatelessWidget {
const CustomBookImage({Key? key, required this.image}) : super(key: key);
final String image;
@override
Widget build(BuildContext context) {
return AspectRatio(
aspectRatio: 2.6 / 4,
child: ClipRRect(
borderRadius: BorderRadius.circular(16),
child: CachedNetworkImage(
imageUrl: image,
fit: BoxFit.fill,
),
),
);
}
}class FeaturedBooksListView extends StatelessWidget {
const FeaturedBooksListView({Key? key, required this.books}) : super(key: key);
final List<BookEntity> books;
@override
Widget build(BuildContext context) {
return SizedBox(
height: MediaQuery.of(context).size.height * .3,
child: ListView.builder(
itemCount: books.length, // [Editor's note: Instructor doesn't explicitly type itemCount in this video but it is implied for ListView.builder]
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: CustomBookImage(
image: books[index].image ?? '',
),
);
}),
);
}
}if (state is FeaturedBooksSuccess) {
return FeaturedBooksListView(
books: state.books,
);
}My Notes
The instructor encounters a classic Flutter gotcha: the MissingPluginException. This happens when you add a package that relies on native code (Java/Kotlin/Swift/Obj-C) while the app is hot-reloading. The cached_network_image package depends on native directory access packages like path_provider. Always fully stop and restart the app after adding such packages. [Editor's note] The instructor used BoxFit.fill for the CachedNetworkImage. This forces the image to fill the given dimensions but distorts its natural aspect ratio. In production applications, especially for varying book cover sizes, BoxFit.cover is highly recommended as it fills the space while preserving the aspect ratio (cropping the overflow). Additionally, it is best practice to provide a placeholder or errorWidget to CachedNetworkImage to handle slow networks or broken URLs gracefully.