如何解决Flutter PageView中卡片高度不统一的问题

有没有小伙伴遇到这种场景的,在一个页面A中放置一个PageView(B), 但是Pageview中卡片的布局是动态布局高度的,有时候是h1的高度,有时候是h2的高度

正常情况下,滑动时,Pageview的高度默认是由最大高度的那个卡片决定的,即为h1,当卡片实际高度为h2时,即占据图中B1面积时,B2面积也是实际上被Pageview占据的,此时针对B2面积的各种手势都会被Pageview吸收掉

这时会出现一个问题,B2部分看起来是背景页A的内容,但是点击等手势却不生效

那么,怎么解决这个问题呢?

首先我们来看下,解决后的效果 ,图中点击了相同位置,见上图中的B2

解决方案

思路

可以监听卡片滑动,在滑动停止时,根据当前卡片的高度调整整个PageView的高度,设置clipBehavior为none

less 复制代码
Widget _buildPageView() {
    return Container(
      width: MediaQuery.of(context).size.width,
      height: cardHeight,
      color: Colors.transparent,
      child: NotificationListener<ScrollNotification>(
        onNotification: (ScrollNotification notification) {
          if (notification is ScrollEndNotification) {
            // 滚动停止时的操作
            onPageViewScrollEndListener();
          }
          return true; // 返回 true 表示事件已处理
        },
        child: PageView.builder(
          clipBehavior: Clip.none,
          scrollDirection: Axis.horizontal,
          itemBuilder: (_, index) {
            return _pageItemView(index);
          },
          itemCount: listData.length,
          controller: pageController,
        ),
      ),
    );
  }

那么,该如何获取当前动态布局卡片的高度呢?

可以创建一个继承自SingleChildRenderObjectWidget的类,布局完成后获取高度

设置一个Map记录卡片的高度,并在刷新时获取高度给PageView和卡片

scala 复制代码
class CustomLayoutSizedBox extends SingleChildRenderObjectWidget {
  const CustomLayoutSizedBox({
    super.key,
    super.child,
    required this.onPerformLayout,
  });

  final void Function(double height)? onPerformLayout;

  @override
  RenderObject createRenderObject(BuildContext context) {
    return CustomRenderConstrainedBox(onPerformLayout: onPerformLayout);
  }
}

class CustomRenderConstrainedBox extends RenderProxyBox {
  CustomRenderConstrainedBox({required this.onPerformLayout});

  final void Function(double height)? onPerformLayout;

  @override
  void performLayout() {
    super.performLayout();
    onPerformLayout?.call(child?.size.height ?? 0);
  }
}

设置一个Map记录卡片的高度,并在刷新时获取高度给PageView和卡片

less 复制代码
/// 卡片渲染完成时,记录当前卡片高度
Map<int, double> cardHeightMap = {};
......
......
Widget _buildPageView() {
    double cardHeight = 200.0; // 默认卡片最大高度
    if (listData.length > currentPageIndex) {
      cardHeight = cardHeightMap[currentPageIndex] ?? 200;
    }
    return Container(
      ......
      );
  }
......
......
Widget _pageItemView(int index) {
    double width = MediaQuery.of(context).size.width - 100;
    Widget child = Container(
        width: width,
        decoration: BoxDecoration(
          color: Colors.blueAccent,
          borderRadius: BorderRadius.circular(8.0),
        ),
        alignment: Alignment.center,
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: List.generate(
            listData[index],
            (i) => SizedBox(
              width: double.infinity,
              height: 40,
            ),
          ),
        ),
      );
    child = CustomLayoutSizedBox(
      child: child,
      onPerformLayout: (height) {
        cardHeightMap[index] = height;
      },
    );
    double height = 0.1; // 设置一个默认最低高度
    if (listData.length > currentPageIndex) {
      height = cardHeightMap[currentPageIndex] ?? 0.1;
    }
    child = Container(
      alignment: Alignment.topLeft,
      child: Stack(
        clipBehavior: Clip.none,
        children: [
          SizedBox(
            width: width,
            height: height,
          ),
          Positioned(
            left: 0,
            right: 0,
            top: 0,
            child: child,
          ),
        ],
      ),
    );

    return child;
  }

在首屏渲染完成后以及列表停止滑动时,记得刷新视图

scss 复制代码
@override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      if (mounted) {
        return;
      }
      setState(() {});
    });
  }

// 列表滑动停止
  void onPageViewScrollEndListener() {
    currentPageIndex = pageController.page?.toInt() ?? 0;
    setState(() {});
  }

Demo地址:github.com/percival888...

若有收获,就点个赞吧

相关推荐
SoaringHeart6 小时前
Flutter进阶:高内存任务的动态并发执行完美实现
前端·flutter
吴Wu涛涛涛涛涛Tao7 小时前
Flutter 实现类似抖音/TikTok 的竖向滑动短视频播放器
android·flutter·ios
猪哥帅过吴彦祖7 小时前
Flutter 插件工作原理深度解析:从 Dart 到 Native 的完整调用链路
android·flutter·ios
叽哥1 天前
flutter学习第 18 节:设备功能调用
android·flutter·ios
来来走走1 天前
Flutter 顶部导航标签组件Tab + TabBar + TabController
android·flutter
程序员老刘1 天前
2025 Google 开发者大会 客户端要点速览
flutter·ai编程·客户端
Wakeup1 天前
当Flutter下载依赖慢,运行在Android上卡Running Gradle task ‘assembleDebug...解决方法
flutter
你听得到111 天前
告别重复造轮子!我从 0 到 1 封装一个搞定全场景的弹窗库!
前端·flutter·性能优化
恋猫de小郭2 天前
Flutter 3.35 发布,快来看看有什么更新吧
android·前端·flutter
w_y_fan2 天前
flutter_riverpod: ^2.6.1 应用笔记 (一)
前端·flutter