You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter:PageView切换时组件无法透明(显示白色)如何修复?

解决PageView纸牌叠顶滚动时视图变白问题

问题出在PageView的默认布局机制:垂直滚动时,页面是线性排列的,下一页位于当前页面的下方(屏幕外),所以当顶部页面透明后,显示的是Scaffold的白色背景,而非下一页。要实现纸牌叠顶效果,需要让所有页面堆叠在屏幕同一位置,通过滚动偏移控制顶部页面的透明度和位移,露出下方已存在的页面。

以下是修改后的完整代码:

import 'package:flutter/material.dart';

void main() => runApp(const MaterialApp(home: MyHomePage()));

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  MyHomePageState createState() => MyHomePageState();
}

class MyHomePageState extends State<MyHomePage> {
  final PageController controller = PageController();

  List<Widget> pages = <Widget>[
    Container(
      color: Colors.red,
      child: const Center(child: Text("0", style: TextStyle(fontSize: 40))),
    ),
    Container(
      color: Colors.green,
      child: const Center(child: Text("1", style: TextStyle(fontSize: 40))),
    ),
    Container(
      color: Colors.blue,
      child: const Center(child: Text("2", style: TextStyle(fontSize: 40))),
    )
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: [
          // 所有页面堆叠在同一位置
          ...List.generate(pages.length, (index) {
            return DeckPageView(
              controller: controller,
              index: index,
              child: pages[index],
            );
          }),
          // 透明PageView仅处理滚动手势,不显示内容
          PageView.builder(
            itemCount: pages.length,
            scrollDirection: Axis.vertical,
            controller: controller,
            itemBuilder: (context, index) => const SizedBox.shrink(),
          ),
        ],
      ),
    );
  }
}

class DeckPageView extends StatefulWidget {
  const DeckPageView({
    Key? key,
    required this.index,
    required this.controller,
    required this.child,
  }) : super(key: key);
  final int index;
  final PageController controller;
  final Widget child;

  @override
  DeckPageViewState createState() => DeckPageViewState();
}

class DeckPageViewState extends State<DeckPageView> {
  double _pagePosition = 0.0;

  @override
  void initState() {
    super.initState();
    // 监听PageController的页面变化
    widget.controller.addListener(_updatePagePosition);
    // 初始化页面位置
    WidgetsBinding.instance.addPostFrameCallback((_) {
      if (widget.controller.position.haveDimensions) {
        setState(() => _pagePosition = widget.controller.page ?? 0.0);
      }
    });
  }

  void _updatePagePosition() {
    if (mounted) {
      setState(() => _pagePosition = widget.controller.page ?? 0.0);
    }
  }

  @override
  void dispose() {
    widget.controller.removeListener(_updatePagePosition);
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    double delta = _pagePosition - widget.index;
    final screenHeight = MediaQuery.of(context).size.height;

    // 页面不在当前滚动范围内时隐藏
    if (delta < -1 || delta > 1) {
      return const SizedBox.shrink();
    }

    double opacity = 1.0;
    Offset offset = Offset.zero;

    if (delta > 0) {
      // 向上滚动:当前页面是顶部,逐渐上移并透明
      if (widget.index == _pagePosition.floor()) {
        opacity = 1 - delta;
        offset = Offset(0, -screenHeight * delta);
      }
    } else if (delta < 0) {
      // 向下滚动:上一页从上方返回,当前页面保持不动
      opacity = 1.0;
      offset = Offset.zero;
    }

    return Transform.translate(
      offset: offset,
      child: Opacity(
        opacity: opacity,
        child: widget.child,
      ),
    );
  }
}

关键修改点:

  • 页面堆叠:用Stack将所有页面放在同一位置,替代PageView的线性排列,确保下一页始终在当前页面下方(屏幕内)。
  • 滚动手势分离:保留PageView仅处理垂直滚动的手势逻辑,但其内容设为透明空组件,避免干扰堆叠页面的显示。
  • 动画逻辑优化:仅对当前正在滑走的顶部页面应用位移和透明度变化,其他页面保持正常显示,确保透明时露出的是下一页而非背景。

内容的提问来源于stack exchange,提问作者Eddy Todd

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 17:12:58