Flutter:如何从PageView子页面组件切换页面索引并实现Hero动画?
解决方案:PageView子组件切换+Hero跨页动画
一、先解决子组件点击切换PageView和BottomNavigationBar的问题
核心逻辑是父组件维护页面状态与控制器,通过回调函数让子组件触发状态更新,确保BottomNavigationBar和PageView索引同步。
1. 父组件(lib/screens/home.dart)代码示例
import 'package:flutter/material.dart'; import '../widgets/home_widget.dart'; import '../widgets/search_widget.dart'; class HomeScreen extends StatefulWidget { const HomeScreen({super.key}); @override State<HomeScreen> createState() => _HomeScreenState(); } class _HomeScreenState extends State<HomeScreen> { final PageController _pageController = PageController(initialPage: 0); int _currentIndex = 0; // 子组件调用此方法切换页面 void _switchToSearchPage() { setState(() { _currentIndex = 1; // 平滑切换页面,也可用jumpToPage直接跳转 _pageController.animateToPage( 1, duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, ); }); } @override void dispose() { _pageController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( body: PageView( controller: _pageController, onPageChanged: (index) => setState(() => _currentIndex = index), children: [ HomeWidget(onTapSearch: _switchToSearchPage), const SearchWidget(), ], ), bottomNavigationBar: BottomNavigationBar( currentIndex: _currentIndex, onTap: (index) { setState(() { _currentIndex = index; _pageController.jumpToPage(index); }); }, items: const [ BottomNavigationBarItem(icon: Icon(Icons.home), label: '首页'), BottomNavigationBarItem(icon: Icon(Icons.search), label: '搜索'), ], ), ); } }
2. 子组件(lib/widgets/home_widget.dart)代码示例
import 'package:flutter/material.dart'; class HomeWidget extends StatelessWidget { final VoidCallback onTapSearch; // 接收父组件的切换回调 const HomeWidget({super.key, required this.onTapSearch}); @override Widget build(BuildContext context) { return Scaffold( body: Center( child: GestureDetector( onTap: onTapSearch, // 点击搜索栏触发切换 child: Container( width: 300, height: 50, decoration: BoxDecoration( color: Colors.grey[200], borderRadius: BorderRadius.circular(25), ), child: const Row( children: [ SizedBox(width: 15), Icon(Icons.search), SizedBox(width: 10), Text('点击搜索栏切换到搜索页'), ], ), ), ), ), ); } }
二、Hero Widget实现跨PageView组件动画(支持任意Widget)
Hero并非仅支持图片,任何Widget都可以通过Hero实现共享元素动画,只要两个页面的Hero标签(tag)一致即可。以下是基于上述代码的改造:
1. 改造home_widget.dart,为搜索栏添加Hero
import 'package:flutter/material.dart'; class HomeWidget extends StatelessWidget { final VoidCallback onTapSearch; const HomeWidget({super.key, required this.onTapSearch}); @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Hero( tag: 'search_bar', // 唯一标识,需与搜索页Hero标签一致 child: GestureDetector( onTap: onTapSearch, child: Container( width: 300, height: 50, decoration: BoxDecoration( color: Colors.grey[200], borderRadius: BorderRadius.circular(25), ), child: const Row( children: [ SizedBox(width: 15), Icon(Icons.search), SizedBox(width: 10), Text('点击搜索栏切换到搜索页'), ], ), ), ), ), ), ); } }
2. 改造search_widget.dart,添加对应Hero的搜索栏
import 'package:flutter/material.dart'; class SearchWidget extends StatelessWidget { const SearchWidget({super.key}); @override Widget build(BuildContext context) { return Scaffold( body: Padding( padding: const EdgeInsets.only(top: 60, left: 20, right: 20), child: Hero( tag: 'search_bar', // 与首页Hero标签完全一致 child: Container( width: double.infinity, height: 50, decoration: BoxDecoration( color: Colors.grey[200], borderRadius: BorderRadius.circular(25), ), child: const TextField( decoration: InputDecoration( border: InputBorder.none, prefixIcon: Icon(Icons.search), hintText: '输入内容搜索', ), ), ), ), ), ); } }
注意事项
- Hero的tag必须全局唯一,不可重复使用
- PageView切换时使用
animateToPage,能让Hero动画与页面滑动动画同步,提升视觉连贯性 - 若需自定义Hero动画逻辑,可通过
HeroController扩展实现
内容的提问来源于stack exchange,提问作者Logic Kahan Hai
相关产品推荐
相关产品推荐

