Flutter 02:BottomNavigationBar底部导航栏

1,531 阅读2分钟

基本概念

BottomNavigationBar({
    Key key, 
    @required List<BottomNavigationBarItem> items, //BNI数组,
    ValueChanged<int> onTap, //其中一项被点击后的回调
    int currentIndex: 0, //激活项的索引
    double elevation: 8.0, //
    BottomNavigationBarType type, //有fixed、shifting两种
    Color fixedColor, //选择的颜色,'Either selectedItemColor or fixedColor can be specified, but not both'
    Color backgroundColor, //背景颜色
    double iconSize: 24.0, 
    Color selectedItemColor, 
    Color unselectedItemColor, 
    IconThemeData selectedIconTheme: const IconThemeData(),
    IconThemeData unselectedIconTheme: const IconThemeData(),
    double selectedFontSize: 14.0, 
    double unselectedFontSize: 12.0, 
    TextStyle selectedLabelStyle, 
    TextStyle unselectedLabelStyle, 
    bool showSelectedLabels: true, 是否显示选中的Item的文字
    bool showUnselectedLabels //是否显示未选中的Item的文字
});

BottomNavigationBar通常在Scaffold中使用,如下

Scaffold(
    appBar: AppBar(...),
    body: Container(...),
    bottomNavigationBar: BottomNavigationBar(....)
)
  • appBar,即标题
  • body,内容显示区域
  • bottomNavigationBar,底部导航栏

items少于等于3个,type默认值为fixed;items四个或者以上,type默认值为shifting,默认的选中和未选中的颜色是根据type变化的

fixed模式是平分,而shifting模式是可以摇摆的

滑动切换

body: PageView.builder( //实现滑动切换
    onPageChanged: (int index){
      Provider.of<SelectedIndexProvider>(context, listen: false).changeIndex(index);
    },
    controller: _pageController,
    itemCount: _bodyPages.length,
    itemBuilder: (BuildContext context, int index){
      return _bodyPages.elementAt(index);
    }
),

用_pageController去控制页面的显示

void _onChangeIndex(BuildContext context, int index){
    Provider.of<SelectedIndexProvider>(context, listen: false).changeIndex(index);
    //bottomNavigationBar 和 PageView 关联
    _pageController.animateToPage(index, duration: Duration(milliseconds: 300), curve: Curves.ease);
  }

代码

import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:provider/provider.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';

import 'package:flutter_bee/pages/page_home.dart';
import 'package:flutter_bee/pages/page_navi.dart';
import 'package:flutter_bee/pages/page_project.dart';
import 'package:flutter_bee/pages/page_tree.dart';
import 'package:flutter_bee/pages/page_article_detail.dart';
import 'package:flutter_bee/common/strings.dart';
import 'package:flutter_bee/common/colors.dart';
import 'package:flutter_bee/provider/provider_selected_index.dart';
import 'package:flutter_bee/provider/provider_theme.dart';


class IndexPage extends StatelessWidget {
  final PageController _pageController = PageController(initialPage: 0);
  final List<Widget> _bodyPages = [
    HomePage(),
    TreePage(),
    NaviPage(),
    ProjectPage()
  ];
  final List<BottomNavigationBarItem> _bottomItems= [
    BottomNavigationBarItem(
      icon: Icon(Icons.home),
      title: Text(YStrings.home)
    ),
    BottomNavigationBarItem(
      icon: Icon(Icons.filter_list),
      title: Text(YStrings.tree)
    ),
    BottomNavigationBarItem(
      icon: Icon(Icons.low_priority),
      title: Text(YStrings.navi)
    ),
    BottomNavigationBarItem(
      icon: Icon(Icons.apps),
      title: Text(YStrings.project)
    ),
  ];


  @override
  Widget build(BuildContext context) {
    ScreenUtil.init(context, width: 750, height: 1334);//初始化屏幕数据
    int themeIndex = Provider.of<ThemeProvider>(context).value;
    Map theme = YColors.themeColor[themeIndex == null ? 0 : themeIndex];
    int _selectedIndex = Provider.of<SelectedIndexProvider>(context).selectedIndex;

    return Scaffold(
      appBar: AppBar(
        leading: Builder(
          builder: (BuildContext context){
            return _accountAvatar(context);
          }
        ),
        title: Text(_getTitleByIndex(_selectedIndex)),
        centerTitle: true,
        actions: <Widget>[
          IconButton(
            icon: Icon(Icons.search), 
            onPressed: _onSearch
          )
        ],
      ),
      // body: IndexedStack(
      //   index: _selectedIndex,
      //   children: _bodyPages,
      // ),
      body: PageView.builder( //实现滑动切换
        onPageChanged: (int index){
          Provider.of<SelectedIndexProvider>(context, listen: false).changeIndex(index);
        },
        controller: _pageController,
        itemCount: _bodyPages.length,
        itemBuilder: (BuildContext context, int index){
          return _bodyPages.elementAt(index);
        }
      ),
      bottomNavigationBar: BottomNavigationBar(
        type: BottomNavigationBarType.fixed,
        fixedColor: theme['primaryColor'],
        items: _bottomItems,
        currentIndex: _selectedIndex,
        onTap: (index)=> _onChangeIndex(context, index),
      ),
      floatingActionButton: FloatingActionButton(
        backgroundColor: theme['primaryColor'],
        onPressed: _onShowToast,
        tooltip: '点击选中最后一个',
        child: Icon(Icons.add, color: Colors.white),
      ), 
      drawer: _showDrawer(context),
    );
  }

  void _onSearch(){
    
  }
  String _getTitleByIndex(int index){
    switch (index) {
      case 0:
        return YStrings.home;
      case 1: 
        return YStrings.tree;
      case 2:
        return YStrings.navi;
      case 3: 
        return YStrings.project;
      default:
        return YStrings.home;
    }
  }

  void _onChangeIndex(BuildContext context, int index){
    Provider.of<SelectedIndexProvider>(context, listen: false).changeIndex(index);
    //bottomNavigationBar 和 PageView 关联
    _pageController.animateToPage(index, duration: Duration(milliseconds: 300), curve: Curves.ease);
  }

  void _onShowToast(){
    Fluttertoast.showToast(
      msg: 'test',
      textColor: Colors.white,
      fontSize: 12,
      backgroundColor: YColors.color_999,
      gravity: ToastGravity.BOTTOM
    );
  }

  Widget _showDrawer(BuildContext context){
    return Drawer(
      child: ListView(
        padding: EdgeInsets.zero, //注意设置0
        children: <Widget>[
          _accountInfo(context),
          ListTile(
            leading: Icon(Icons.favorite_border),
            title: Text(YStrings.favorite),
            trailing: Icon(Icons.chevron_right),
            onTap: (){},
          ),
          ListTile(
            leading: Icon(Icons.color_lens),
            title: Text(YStrings.changeTheme),
            trailing: Icon(Icons.chevron_right),
            onTap: (){},
          ),
          ListTile(
            leading: Icon(Icons.share),
            title: Text(YStrings.share),
            trailing: Icon(Icons.chevron_right),
            onTap: (){},
          ),
          ListTile(
            leading: Icon(Icons.info_outline),
            title: Text(YStrings.aboute),
            trailing: Icon(Icons.chevron_right),
            onTap: (){},
          ),
          Divider(),
          ListTile(
            leading: Icon(Icons.power_settings_new),
            title: Text(YStrings.logout),
            trailing: Icon(Icons.chevron_right),
            onTap: (){},
          ),
        ],
      ),
    );
  }

  Widget _accountInfo(BuildContext context){
    return UserAccountsDrawerHeader(
      currentAccountPicture: GestureDetector(
        child: ClipOval(
          child: Image.asset('lib/assets/images/avatar.png'),
        ),
      ),
      otherAccountsPictures: <Widget>[
        IconButton(
          icon: Icon(Icons.stars, color: Colors.white),
          onPressed: (){
            Navigator.of(context).pop();
            Navigator.push(
              context,  
              MaterialPageRoute(builder: (context) => ArticleDetailPage(
                title: YStrings.addStar,
                url: YStrings.github
              ))
            );
          }
        )
      ],
      accountName: Text(
        YStrings.proName, 
        style: TextStyle(fontWeight: FontWeight.bold, fontSize: ScreenUtil().setSp(40)),
      ), 
      accountEmail: Text(YStrings.github),
      onDetailsPressed: (){},
    );
  }
  /**
   * 自定义leading
   */
  Widget _accountAvatar(BuildContext context){
    return InkWell(
      child: Container(
        padding: EdgeInsets.all(ScreenUtil().setWidth(15)),
        child: ClipOval(
          child: Image.asset(
            'lib/assets/images/avatar.png',
          ),
        )
      ),
      onTap: (){
        Scaffold.of(context).openDrawer();
      },
    );
  }
}

参考链接

官方地址:api.flutter.dev/flutter/mat…

Flutter 底部导航栏BottomNavigationBar:blog.csdn.net/yechaoa/art…

Flutter笔记(一):BottomNavigationBar常见问题:www.jianshu.com/p/7274bad9f…