如何解决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...

若有收获,就点个赞吧

相关推荐
SoaringHeart1 小时前
Flutter组件封装:验证码倒计时按钮 TimerButton
前端·flutter
lqj_本人1 小时前
Flutter 适配鸿蒙桌面快捷入口完整指南
flutter·华为·harmonyos
kirk_wang2 小时前
Flutter 三方库鸿蒙适配实践:以 Firebase Messaging 为例实现跨平台推送集成
flutter·移动开发·跨平台·arkts·鸿蒙
赵财猫._.4 小时前
【Flutter x 鸿蒙】第一篇:环境搭建与第一个鸿蒙Flutter应用运行
flutter·华为·harmonyos
恋猫de小郭4 小时前
Android Studio Otter 2 Feature 发布,最值得更新的 Android Studio
android·前端·flutter
走在路上的菜鸟4 小时前
Android学Dart学习笔记第十二节 函数
android·笔记·学习·flutter
sunly_5 小时前
Flutter:高德定位,获取经纬度,详细地址信息
flutter
解局易否结局6 小时前
Flutter 跨平台开发进阶:从 Widget 思想到全栈集成
flutter
Bryce李小白6 小时前
理解InheritedWidget概念
flutter
西西学代码6 小时前
flutter---进度条(3)
flutter