Flutter实现Instagram风格个人页GridView无限加载及整页滚动
问题解决方法
问题根源
你遇到的加载异常+滚动冲突问题来自两个核心原因:
- 外层嵌套了
SingleChildScrollView,内部GridView开启shrinkWrap: true时,GridView自身不会产生滚动区域,高度会自动适配所有子项总高度,所有滚动事件都被外层SingleChildScrollView消费,你绑定在GridView上的滚动监听器只能在首次初始化时触发一次,所以最多只能加载2页共30条数据。 - 关闭
shrinkWrap后GridView会独占滚动权限,就会出现只有GridView区域滚动、头部个人信息固定不动的问题,不符合Instagram个人页整体滚动的需求。
最优解决方案
推荐用CustomScrollView+Sliver系列组件实现,这是Flutter中实现整页滚动+网格列表无限加载的官方标准方案,完全符合你要的交互效果,具体修改步骤和代码如下:
1. 改造父页面AccountPage
将原有的SingleChildScrollView+Column结构替换为CustomScrollView,持有全局滚动控制器监听整页滚动事件:
import 'package:dresskip/model/user_model.dart'; import 'package:flutter/material.dart'; import 'itemSection_widget.dart'; import 'profileSection_widget.dart'; import '/assets/constants.dart' as constants; class AccountPage extends StatefulWidget { const AccountPage({Key? key}) : super(key: key); @override State<AccountPage> createState() => _AccountPageState(); } class _AccountPageState extends State<AccountPage> { final ScrollController _scrollController = ScrollController(); final GlobalKey<_ItemSectionState> _itemSectionKey = GlobalKey(); @override void initState() { super.initState(); _scrollController.addListener(_scrollListener); } _scrollListener() { if (_scrollController.offset >= _scrollController.position.maxScrollExtent && !_scrollController.position.outOfRange) { // 滚动到页面底部,触发加载更多 _itemSectionKey.currentState?.loadMore(); } } @override void dispose() { _scrollController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( body: CustomScrollView( controller: _scrollController, slivers: [ // 头部个人信息区域 SliverToBoxAdapter( child: ProfileSection( description: getUserInformation().properties, onClicked: () async {}, ), ), // 分割线 const SliverToBoxAdapter( child: Divider( color: Color(constants.COLOR_BLUE_DRESSKIP), thickness: 2, indent: 50, endIndent: 50, ), ), // 网格列表区域 ItemSection(key: _itemSectionKey), ], ), ); } User getUserInformation() { User myUser = User(username: "test", email: "test@test.test", properties: [ "22 yo\nFlexeur/Epicurien/Philanthrope\nJ'adore la vie\nEFREI Paris", "instagram_test", "facebook_test", "twitter" ]); return myUser; } }
2. 改造ItemSection组件
移除自身的滚动控制器,将普通GridView替换为SliverGrid,关闭自身滚动能力:
import 'dart:developer'; import 'package:dresskip/model/item_model.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; class ItemSection extends StatefulWidget { const ItemSection({Key? key}) : super(key: key); @override _ItemSectionState createState() => _ItemSectionState(); } class _ItemSectionState extends State<ItemSection> { List<Item> items = []; bool isLoading = false; int pageCount = 1; @override void initState() { super.initState(); // 加载第一页数据 addItemsToList(1); } @override Widget build(BuildContext context) { return SliverGrid.count( crossAxisCount: 3, mainAxisSpacing: 0, children: items.map((value) { return Image.network(value.picture); }).toList(), ); } // 对外暴露的加载更多方法 void loadMore() { if (isLoading) return; setState(() { isLoading = true; }); pageCount += 1; addItemsToList(pageCount); } addItemsToList(int page) { Item myItem = Item( name: "test", brand: "test", color: ["0xFF39BDC8", "0xFFdb8abc", ""], picture: "https://images.pexels.com/photos/9676177/pexels-photo-9676177.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260", solo: false, clean: true, type: "test"); for (int i = (page * 15) - 15; i < page * 15; i++) { items.add(myItem); } setState(() { isLoading = false; }); } }
修正后的逻辑说明
- 整个页面的滚动由
CustomScrollView统一控制,头部个人信息和网格列表会一起滚动,完全匹配Instagram个人页的交互效果 - 滚动监听绑定在全局滚动控制器上,每次滚动到页面底部都会触发下一页加载,不会再出现加载中断的问题
- 修复了原有代码中
addItemsToList方法未正确更新isLoading状态、传入的page参数未使用的小bug
内容的提问来源于stack exchange,提问作者FloconDavoine
相关产品推荐
相关产品推荐

