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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 14:40:23