I am using Flutter with the PagedMasonryGridView
widget to implement infinite scroll pagination
. However, the lazy loading functionality is not working as expected. Instead of loading data on user scroll, all data is fetched at once. I can see multiple API calls in the logger, but it appears to fetch all data in one go. I am using the Provider package for state management.
My Widget
<code>class AgencyProfile extends StatelessWidget {
const AgencyProfile({super.key});
@override
Widget build(BuildContext context) {
double screenWidth = MediaQuery.of(context).size.width;
double screenHeight = MediaQuery.of(context).size.height;
return Scaffold(
body: SizedBox(
child: Consumer<AgencyProfileProvider>(
builder: (context, provider, child) {
if (provider.agencyProfileModel != null &&
provider.agencyProfileModel!.success &&
!provider.isAgencyLoading) {
return NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverList(
delegate: SliverChildListDelegate(
[
// Header content here
],
),
),
];
},
body: PagedMasonryGridView<int, Datum>(
shrinkWrap: true,
primary: true,
pagingController: provider.pagingController,
padding: const EdgeInsets.only(top: 0),
crossAxisSpacing: 1,
cacheExtent: 9999,
mainAxisSpacing: 1,
addAutomaticKeepAlives: true,
builderDelegate: PagedChildBuilderDelegate<Datum>(
itemBuilder: (context, item, index) {
return Container(
width: screenWidth / 3.1,
height: screenHeight / 3.6,
decoration: const BoxDecoration(
color: Colors.black45,
),
// Item content here
);
},
),
gridDelegateBuilder: (int childCount) {
return SliverSimpleGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: screenWidth / 3,
);
},
),
);
} else if (provider.agencyProfileModel != null &&
!provider.agencyProfileModel!.success) {
return const Center(
child: Text("Something went wrong! Please try again later."),
);
}
return const Center(
child: CircularProgressIndicator(),
);
},
),
),
);
}
}
</code>
<code>class AgencyProfile extends StatelessWidget {
const AgencyProfile({super.key});
@override
Widget build(BuildContext context) {
double screenWidth = MediaQuery.of(context).size.width;
double screenHeight = MediaQuery.of(context).size.height;
return Scaffold(
body: SizedBox(
child: Consumer<AgencyProfileProvider>(
builder: (context, provider, child) {
if (provider.agencyProfileModel != null &&
provider.agencyProfileModel!.success &&
!provider.isAgencyLoading) {
return NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverList(
delegate: SliverChildListDelegate(
[
// Header content here
],
),
),
];
},
body: PagedMasonryGridView<int, Datum>(
shrinkWrap: true,
primary: true,
pagingController: provider.pagingController,
padding: const EdgeInsets.only(top: 0),
crossAxisSpacing: 1,
cacheExtent: 9999,
mainAxisSpacing: 1,
addAutomaticKeepAlives: true,
builderDelegate: PagedChildBuilderDelegate<Datum>(
itemBuilder: (context, item, index) {
return Container(
width: screenWidth / 3.1,
height: screenHeight / 3.6,
decoration: const BoxDecoration(
color: Colors.black45,
),
// Item content here
);
},
),
gridDelegateBuilder: (int childCount) {
return SliverSimpleGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: screenWidth / 3,
);
},
),
);
} else if (provider.agencyProfileModel != null &&
!provider.agencyProfileModel!.success) {
return const Center(
child: Text("Something went wrong! Please try again later."),
);
}
return const Center(
child: CircularProgressIndicator(),
);
},
),
),
);
}
}
</code>
class AgencyProfile extends StatelessWidget {
const AgencyProfile({super.key});
@override
Widget build(BuildContext context) {
double screenWidth = MediaQuery.of(context).size.width;
double screenHeight = MediaQuery.of(context).size.height;
return Scaffold(
body: SizedBox(
child: Consumer<AgencyProfileProvider>(
builder: (context, provider, child) {
if (provider.agencyProfileModel != null &&
provider.agencyProfileModel!.success &&
!provider.isAgencyLoading) {
return NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverList(
delegate: SliverChildListDelegate(
[
// Header content here
],
),
),
];
},
body: PagedMasonryGridView<int, Datum>(
shrinkWrap: true,
primary: true,
pagingController: provider.pagingController,
padding: const EdgeInsets.only(top: 0),
crossAxisSpacing: 1,
cacheExtent: 9999,
mainAxisSpacing: 1,
addAutomaticKeepAlives: true,
builderDelegate: PagedChildBuilderDelegate<Datum>(
itemBuilder: (context, item, index) {
return Container(
width: screenWidth / 3.1,
height: screenHeight / 3.6,
decoration: const BoxDecoration(
color: Colors.black45,
),
// Item content here
);
},
),
gridDelegateBuilder: (int childCount) {
return SliverSimpleGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: screenWidth / 3,
);
},
),
);
} else if (provider.agencyProfileModel != null &&
!provider.agencyProfileModel!.success) {
return const Center(
child: Text("Something went wrong! Please try again later."),
);
}
return const Center(
child: CircularProgressIndicator(),
);
},
),
),
);
}
}
My Provider
<code>class AgencyProfileProvider extends ChangeNotifier {
// other codes
static const _pageSize = 10;
final PagingController<int, Datum> pagingController =
PagingController(firstPageKey: 0);
final ScrollController _scrollController = ScrollController();
Future<void> initData(int id) async {
getAgencyDetails(id);
WidgetsBinding.instance.addPostFrameCallback((_) {
pagingController.addPageRequestListener((pageKey) {
_fetchPage(id, pageKey);
});
});
}
Future<void> _fetchPage(int agencyId, int pageKey) async {
try {
await Future.delayed(const Duration(seconds: 1));
final newItems = await AgencyDetailRepository().getAgencyTours(
agencyId.toString(), _pageSize, (pageKey * _pageSize));
final isLastPage = newItems.data!.length < _pageSize;
if (isLastPage) {
pagingController.appendLastPage(newItems.data!);
} else {
final nextPageKey = pageKey + 1;
pagingController.appendPage(newItems.data!, nextPageKey);
}
} catch (error) {
pagingController.error = error;
}
}
// other codes
}
</code>
<code>class AgencyProfileProvider extends ChangeNotifier {
// other codes
static const _pageSize = 10;
final PagingController<int, Datum> pagingController =
PagingController(firstPageKey: 0);
final ScrollController _scrollController = ScrollController();
Future<void> initData(int id) async {
getAgencyDetails(id);
WidgetsBinding.instance.addPostFrameCallback((_) {
pagingController.addPageRequestListener((pageKey) {
_fetchPage(id, pageKey);
});
});
}
Future<void> _fetchPage(int agencyId, int pageKey) async {
try {
await Future.delayed(const Duration(seconds: 1));
final newItems = await AgencyDetailRepository().getAgencyTours(
agencyId.toString(), _pageSize, (pageKey * _pageSize));
final isLastPage = newItems.data!.length < _pageSize;
if (isLastPage) {
pagingController.appendLastPage(newItems.data!);
} else {
final nextPageKey = pageKey + 1;
pagingController.appendPage(newItems.data!, nextPageKey);
}
} catch (error) {
pagingController.error = error;
}
}
// other codes
}
</code>
class AgencyProfileProvider extends ChangeNotifier {
// other codes
static const _pageSize = 10;
final PagingController<int, Datum> pagingController =
PagingController(firstPageKey: 0);
final ScrollController _scrollController = ScrollController();
Future<void> initData(int id) async {
getAgencyDetails(id);
WidgetsBinding.instance.addPostFrameCallback((_) {
pagingController.addPageRequestListener((pageKey) {
_fetchPage(id, pageKey);
});
});
}
Future<void> _fetchPage(int agencyId, int pageKey) async {
try {
await Future.delayed(const Duration(seconds: 1));
final newItems = await AgencyDetailRepository().getAgencyTours(
agencyId.toString(), _pageSize, (pageKey * _pageSize));
final isLastPage = newItems.data!.length < _pageSize;
if (isLastPage) {
pagingController.appendLastPage(newItems.data!);
} else {
final nextPageKey = pageKey + 1;
pagingController.appendPage(newItems.data!, nextPageKey);
}
} catch (error) {
pagingController.error = error;
}
}
// other codes
}
main.dart
<code>ChangeNotifierProvider(
create: (context) => AgencyProfileProvider()..initData(2),
child: const AgencyProfile(),
)
</code>
<code>ChangeNotifierProvider(
create: (context) => AgencyProfileProvider()..initData(2),
child: const AgencyProfile(),
)
</code>
ChangeNotifierProvider(
create: (context) => AgencyProfileProvider()..initData(2),
child: const AgencyProfile(),
)