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

如何在Flutter中结合PageController与Bloc实现页面切换?

解决Flutter中PageController与Bloc配合实现页面切换的问题

你的代码目前存在一个核心问题:父组件和_Menu组件各自创建了独立的PageController实例,这导致_Menu里的控制器无法关联到父组件中的PageView,所以点击菜单时页面不会切换。

修改步骤

1. 给_Menu组件添加PageController参数

将_Menu改为接收父组件传递的控制器,复用同一个实例:

class _Menu extends StatelessWidget {
  final PageController pageController;
  const _Menu({
    Key? key,
    required this.pageController,
  }) : super(key: key);

  // 后续代码移除内部创建的PageController,直接使用传入的实例
}

2. 父组件传递PageController给_Menu

在父组件的build方法中,把已创建的控制器传给_Menu,注意去掉const修饰符:

return Scaffold(
  body: SafeArea(
    child: PageView(
      controller: pageController,
      children: [/* 页面内容 */],
    ),
  ),
  bottomNavigationBar: _Menu(pageController: pageController),
);

3. 调整_Menu中的控制器使用逻辑

删掉_Menu内部创建的PageController,改用传入的实例处理页面切换,推荐用animateToPage实现平滑过渡:

@override
Widget build(BuildContext context) {
  return BlocListener<MenuBloc, MenuState>(
    listener: (context, state) {
      if (state is MenuSelected) {
        pageController.animateToPage(
          state.index,
          duration: const Duration(milliseconds: 300),
          curve: Curves.easeInOut,
        );
        // 不需要动画的话保留jumpToPage:
        // pageController.jumpToPage(state.index);
      }
    },
    // 后续builder代码保持不变
  );
}

4. (可选)管理控制器生命周期

将父组件的pageController改为类成员变量,添加dispose方法避免内存泄漏:

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

  @override
  State<YourParentWidget> createState() => _YourParentWidgetState();
}

class _YourParentWidgetState extends State<YourParentWidget> {
  late final PageController pageController;

  @override
  void initState() {
    super.initState();
    pageController = PageController(
      initialPage: 0,
      keepPage: true,
    );
  }

  @override
  void dispose() {
    pageController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    // 原build逻辑不变,直接使用pageController
  }
}

修改后完整代码示例

父组件部分

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

  @override
  State<YourParentWidget> createState() => _YourParentWidgetState();
}

class _YourParentWidgetState extends State<YourParentWidget> {
  late final PageController pageController;

  @override
  void initState() {
    super.initState();
    pageController = PageController(
      initialPage: 0,
      keepPage: true,
    );
  }

  @override
  void dispose() {
    pageController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: PageView(
          controller: pageController,
          children: const [
            Center(child: Text("Shop")),
            Center(child: Text("Shop1")),
            Center(child: Text("Shop2")),
            Center(child: Text("Shop3")),
            Center(child: Text("Shop4")),
          ],
        ),
      ),
      bottomNavigationBar: _Menu(pageController: pageController),
    );
  }
}
class _Menu extends StatelessWidget {
  final PageController pageController;
  const _Menu({
    Key? key,
    required this.pageController,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return BlocListener<MenuBloc, MenuState>(
      listener: (context, state) {
        if (state is MenuSelected) {
          pageController.animateToPage(
            state.index,
            duration: const Duration(milliseconds: 300),
            curve: Curves.easeInOut,
          );
        }
      },
      child: BlocBuilder<MenuBloc, MenuState>(
        builder: (context, state) {
          return Column(
            mainAxisAlignment: MainAxisAlignment.end,
            children: [
              Container(
                decoration: BoxDecoration(
                  boxShadow: [
                    BoxShadow(
                      color: const Color(0XFFBFC4C7).withOpacity(0.5),
                      offset: const Offset(5, 8),
                      blurRadius: 15,
                      spreadRadius: 5,
                    ),
                  ],
                  color: Colors.white,
                ),
                padding: const EdgeInsets.only(
                  top: 20,
                  bottom: 40,
                  right: 10,
                  left: 10,
                ),
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                  children: [
                    MenuItems(
                      onTap: () {
                        context.read<MenuBloc>().add(MenuSwitch(0));
                      },
                      label: "Shop",
                      icon: Icon(
                        Icons.explore,
                        size: 24,
                        color: state.index == 0
                            ? const Color(0XFF851252)
                            : const Color(0XFFD4D6DD),
                      ),
                    ),
                    MenuItems(
                      onTap: () {
                        context.read<MenuBloc>().add(MenuSwitch(1));
                      },
                      label: "Shop1",
                      icon: Icon(
                        Icons.apps,
                        size: 24,
                        color: state.index == 1
                            ? const Color(0XFF851252)
                            : const Color(0XFFD4D6DD),
                      ),
                    ),
                    MenuItems(
                      onTap: () {
                        context.read<MenuBloc>().add(MenuSwitch(2));
                      },
                      label: "Shop2",
                      icon: Icon(
                        Icons.signal_cellular_alt,
                        size: 24,
                        color: state.index == 2
                            ? const Color(0XFF851252)
                            : const Color(0XFFD4D6DD),
                      ),
                    ),
                    MenuItems(
                      onTap: () {
                        context.read<MenuBloc>().add(MenuSwitch(3));
                      },
                      label: "Shop3",
                      icon: Icon(
                        Icons.store,
                        size: 24,
                        color: state.index == 3
                            ? const Color(0XFF851252)
                            : const Color(0XFFD4D6DD),
                      ),
                    ),
                    MenuItems(
                      onTap: () {
                        context.read<MenuBloc>().add(MenuSwitch(4));
                      },
                      label: "Shop4",
                      icon: Icon(
                        Icons.account_balance_wallet,
                        size: 24,
                        color: state.index == 4
                            ? const Color(0XFF851252)
                            : const Color(0XFFD4D6DD),
                      ),
                    ),
                  ],
                ),
              )
            ],
          );
        },
      ),
    );
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 12:15:27