在Flutter中使用BottomNavigationBar和IndexedStack可以实现一个功能完整的底部导航栏

发布于:2025-04-10 ⋅ 阅读:(32) ⋅ 点赞:(0)

在Flutter中,使用BottomNavigationBarIndexedStack可以实现一个功能完整的底部导航栏。BottomNavigationBar用于显示底部的导航按钮,而IndexedStack则用于管理页面的切换,确保每个页面的状态得以保留(即页面不会因为切换而重新构建)。

以下是实现的完整代码示例及详细解释:


代码实现

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'BottomNavigationBar + IndexedStack',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const BottomNavigationExample(),
    );
  }
}

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

  
  _BottomNavigationExampleState createState() =>
      _BottomNavigationExampleState();
}

class _BottomNavigationExampleState extends State<BottomNavigationExample> {
  // 当前选中的索引
  int _currentIndex = 0;

  // 页面列表
  final List<Widget> _pages = [
    const HomePage(),
    const SearchPage(),
    const ProfilePage(),
  ];

  
  Widget build(BuildContext context) {
    return Scaffold(
      body: IndexedStack(
        index: _currentIndex, // 根据当前索引显示对应的页面
        children: _pages,     // 所有页面
      ),
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: _currentIndex, // 当前选中的索引
        onTap: (int index) {
          setState(() {
            _currentIndex = index; // 更新索引
          });
        },
        items: const [
          BottomNavigationBarItem(
            icon: Icon(Icons.home),
            label: 'Home',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.search),
            label: 'Search',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.person),
            label: 'Profile',
          ),
        ],
      ),
    );
  }
}

// 首页
class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: const [
          Icon(Icons.home, size: 50, color: Colors.blue),
          Text('Home Page', style: TextStyle(fontSize: 24)),
        ],
      ),
    );
  }
}

// 搜索页
class SearchPage extends StatelessWidget {
  const SearchPage({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: const [
          Icon(Icons.search, size: 50, color: Colors.green),
          Text('Search Page', style: TextStyle(fontSize: 24)),
        ],
      ),
    );
  }
}

// 个人中心页
class ProfilePage extends StatelessWidget {
  const ProfilePage({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: const [
          Icon(Icons.person, size: 50, color: Colors.purple),
          Text('Profile Page', style: TextStyle(fontSize: 24)),
        ],
      ),
    );
  }
}

代码解析

  1. IndexedStack 的作用

    • IndexedStack 是一个特殊的堆栈组件,它会根据 index 属性显示其子组件列表中的某一个子组件。
    • 与普通的 Stack 不同,IndexedStack 只渲染当前索引对应的子组件,但所有子组件的状态都会被保留。因此,当用户切换回某个页面时,该页面的状态不会丢失。
  2. BottomNavigationBar 的配置

    • currentIndex: 表示当前选中的导航项索引。
    • onTap: 当用户点击导航项时触发的回调函数,用于更新 _currentIndex
    • items: 定义了导航栏的每一项,包括图标和标签。
  3. 页面状态保留

    • 使用 IndexedStack 管理页面,而不是直接使用 PageViewNavigator,可以确保每个页面的状态在切换时不会被销毁。这对于需要保存滚动位置、输入框内容等场景非常有用。
  4. 页面切换逻辑

    • 用户点击底部导航栏时,onTap 回调会触发 setState,更新 _currentIndex,从而让 IndexedStack 显示对应索引的页面。

运行效果

  • 应用启动后,默认显示首页(HomePage)。
  • 点击底部导航栏的不同选项,页面会切换到对应的页面(SearchPageProfilePage)。
  • 切换回之前的页面时,页面状态保持不变。

注意事项

  1. 性能优化
    如果页面较多或页面内容较复杂,可以考虑懒加载页面(例如通过 AutomaticKeepAliveClientMixin 实现),以减少内存占用。

  2. 动态数据更新
    如果页面需要动态更新数据,可以在每个页面中使用 StatefulWidget,并通过 setState 或其他状态管理工具(如 ProviderRiverpod)来更新页面内容。

  3. 更多自定义
    BottomNavigationBar 支持更多自定义样式,例如背景颜色、字体大小、图标大小等,可以根据需求调整。