Flutter自适用高度PageView的实现方案
作者:一笑轮回~
需求
在 Flutter 中,PageView
是一个非常常用的组件,能够实现多个页面的滑动切换。然而,默认的 PageView
高度是固定的,这在展示不同高度的页面时,可能会导致不必要的空白或内容裁剪问题。为了使 PageView
能够根据每个页面的内容高度动态调整,我们需要一个自适应高度的 PageView
实现。
效果
本方案的 PageView
可以根据每个页面内容的高度自动调整,保证每个页面的内容在其实际高度内完整显示,并且在页面滑动时,使用平滑的过渡效果。具体来说:
- 每个页面的内容高度不同,
PageView
能够动态调整高度。 - 页面切换时,高度变化平滑,不会造成突兀的视觉效果。
实现思路
1. 测量每个页面的高度
首先,我们需要为每个页面添加一个高度测量的机制,测量页面内容的高度。在 Flutter 中,我们可以通过 GlobalKey
和 RenderBox
获取每个 Widget
的实际高度。
2. 动态调整 PageView 高度
在页面滑动时,我们需要根据滑动的进度动态计算当前 PageView
的高度。这就需要在 PageView
的滑动过程中实时更新高度,使得高度随滑动位置逐步过渡。
3. 动态高度过渡
我们可以使用 AnimatedContainer
来平滑过渡 PageView
的高度,避免切换时高度变化过于突兀。通过监听 PageView
的滑动状态,实时调整容器高度。
实现代码
1. 高度测量组件 HeightMeasureWidget
这个组件负责测量每个页面的高度,并将测量结果通过回调传递出去。
import 'package:flutter/material.dart'; class HeightMeasureWidget extends StatefulWidget { final Widget child; final Function(double height) onHeightChanged; const HeightMeasureWidget( {super.key, required this.child, required this.onHeightChanged}); @override HeightMeasureState createState() => HeightMeasureState(); } class HeightMeasureState extends State<HeightMeasureWidget> { final GlobalKey _key = GlobalKey(); @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { _measureHeight(); }); } void _measureHeight() { final RenderBox? renderBox = _key.currentContext!.findRenderObject() as RenderBox?; if (renderBox != null) { widget.onHeightChanged(renderBox.size.height); } } @override Widget build(BuildContext context) { return Container( key: _key, child: widget.child, ); } }
2. 自适应高度的 AutoHeightPageView
这个组件使用了前面创建的 HeightMeasureWidget
来测量每个页面的高度,然后根据滑动进度调整高度。
import 'package:flutter/material.dart'; import 'measure_height_widget.dart'; class AutoHeightPageView extends StatefulWidget { final List<Widget> children; final PageController pageController; const AutoHeightPageView({ Key? key, required this.children, required this.pageController, }) : super(key: key); @override AutoHeightPageViewState createState() => AutoHeightPageViewState(); } class AutoHeightPageViewState extends State<AutoHeightPageView> { final List<double> _heights = []; double _currentHeight = 0; @override void initState() { super.initState(); widget.pageController.addListener(_updateHeight); } void _updateHeight() { if (widget.pageController.position.haveDimensions && _heights.isNotEmpty) { double page = widget.pageController.page ?? 0.0; int index = page.floor(); int nextIndex = (index + 1) < _heights.length ? index + 1 : index; double percent = page - index; double height = _heights[index] + (_heights[nextIndex] - _heights[index]) * percent; setState(() { _currentHeight = height; }); } } @override Widget build(BuildContext context) { var isMeasureHeight = _heights.length == widget.children.length ? false : true; return Column( children: [ Stack( children: [ Visibility( visible: isMeasureHeight, child: Stack( children: widget.children .map((e) => HeightMeasureWidget( child: e, onHeightChanged: (height) { _heights.add(height); if (_heights.length == widget.children.length) { setState(() { _currentHeight = _heights[0]; }); } }, )) .toList(), ), ), if (!isMeasureHeight) AnimatedContainer( duration: const Duration(milliseconds: 200), height: _currentHeight, curve: Curves.easeOut, child: PageView( controller: widget.pageController, children: widget.children, ), ), ], ) ], ); } @override void dispose() { widget.pageController.dispose(); super.dispose(); } }
3. 使用示例 AutoHeightPageViewPage
该页面演示了如何使用自适应高度的 PageView
,通过内容高度的动态调整,确保 PageView
始终适应当前页面的高度。
import 'package:flutter/material.dart'; import 'package:flutter_xy/r.dart'; import 'package:flutter_xy/xydemo/vp/pageview/auto_height_page_view.dart'; class AutoHeightPageViewPage extends StatefulWidget { const AutoHeightPageViewPage({Key? key}) : super(key: key); @override State<StatefulWidget> createState() => AutoHeightPageViewState(); } class AutoHeightPageViewState extends State<AutoHeightPageViewPage> { final PageController _pageController = PageController(); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('自适用高度PageView'), ), body: Container( color: Colors.white, child: SingleChildScrollView( child: Column( children: [ AutoHeightPageView( pageController: _pageController, children: [ Container( color: Colors.white, child: ListView( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), children: [ Container( color: Colors.red, height: 50, alignment: Alignment.center, child: const Text("第一个界面"), ), Container( color: Colors.yellow, height: 50, alignment: Alignment.center, child: const Text("第一个界面"), ), Container( color: Colors.blue, height: 50, alignment: Alignment.center, child: const Text("第一个界面"), ), ], ), ), Container( color: Colors.green, height: 250, child: const Center(child: Text('第二个界面'))), ], ), Image.asset( R.vp_content_jpg, width: MediaQuery.of(context).size.width, fit: BoxFit.fill, ), ], ), ), ), ); } }
总结
通过本方案,我们实现了一个自适应高度的 PageView
,它能够根据每个页面的内容高度进行动态调整。该实现依赖于对每个页面内容的测量,并使用 AnimatedContainer
来平滑地过渡高度变化。这样可以确保页面切换时,用户体验更加自然流畅,避免了内容被裁剪或空白区域的出现。这种自适应高度的 PageView
非常适合用于不同页面内容高度不一致的场景。
详情:github.com/yixiaolunhui/flutter_xy
到此这篇关于Flutter自适用高度PageView的实现方案的文章就介绍到这了,更多相关Flutter PageView内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!