Flutter 导航

该案例展示了如何在Flutter中创建带有头部菜单和底部导航栏的应用。头部使用了Drawer和Action,而菜单导航使用了AppBar和嵌套的TabBar,通过TabController进行控制。底部导航栏使用BottomNavigationBar实现,可切换不同的页面。同时,为了保持页面状态,使用了KeepAliveWrapper工具类。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

这个案例中用了哪些东西?

工具类KeepAliveWrapper

下面来说说底部导航栏


 

这个案例中用了哪些东西?

先来分析一下,头部有三个图标,使用Drawer 和action,Drawer在AppBar外面不受appbar控制,action[] 是一个集合 里面放了ButtonIcon, Appbar的下面的菜单导航呢? 使用了嵌套的方式,主页面使用了AppBar,然后body 使用子部件(子页面),子页面也有appBar,然后子部件的appbar的属性title可以是任意Widget,首先创建四个子页面,homo作为首页他有菜单导航,home 中的AppBar的title作为菜单栏的容器,title 使用PreferredSize 菜单栏设置高度使用preferreSize : Size.fromHeight(40),控制它的高度,这个页面是一个有状态的,创建一个TabController 给他加一个late意思是待会赋值, 使用生命周期函数 initState当widget被创建时,实例化TabController() 他有两个属性 一个是tab的长度 一个是vsync 为当前widget,写了长度为多少就要写入多少个Tab对应不然报错

 然后为TabBar设置属性 为他美化

class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin{
  late TabController _tabController;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    _tabController = TabController(length: 8, vsync: this);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.white,
        elevation: 0.5,
        title: PreferredSize(    //为菜单导航栏设置高度
          preferredSize: Size.fromHeight(40),
          child: TabBar(
            controller: _tabController,    //这是用来获取Controller
            isScrollable: true,              //设置菜单栏是可以滑动自适应的,如果不写这个那么Tab多了文字就会只有一个字,因为都挤在一起了
            labelColor: Colors.red,    //设置选中的菜单栏的文字颜色
            unselectedLabelColor: Colors.black,    //设置未选中的文字颜色
            indicatorColor: Colors.red,            //设置选中的指示器颜色
            indicatorSize: TabBarIndicatorSize.tab,    //设置导航栏的类型 label还有,
            padding: EdgeInsets.all(5),
            // indicator: BoxDecoration(  //设置导航背景颜色 和圆角矩形
            //   color: Colors.red,
            //   borderRadius: BorderRadius.circular(50)
            // ),
            labelStyle: const TextStyle(    //设置选中的文字字号大小
                fontSize: 16
            ),
            unselectedLabelStyle: const TextStyle(//设置未选中的文字字号大小
                fontSize: 14
            ),
            tabs: const [    //tabs的子部件数量要喝_tabController一样 并且需要一个TabBarView用来显示菜单栏点击后,显示的界面
              Tab(
                child: Text("关注"),
              ),
              Tab(
                child: Text("热门"),
              ),
              Tab(
                child: Text("资讯"),
              ),
              Tab(
                child: Text("篮球"),
              ),
              Tab(
                child: Text("动漫"),
              ),
              Tab(
                child: Text("动画片"),
              ),
              Tab(
                child: Text("电影"),
              ),
              Tab(
                child: Text("电视剧"),
              ),
            ],
          ),
        )
      ),
      body: TabBarView(    //菜单栏点击显示的界面 它的数量也要和_tabController长度一样
        controller: _tabController,
        children: [
//KeyAliveWrapper 是自定义的一个类,传入一个部件保存它的状态
          KeepAliveWrapper(child: ListView(
            children: const [
              ListTile(
                title: Text("我是关注列表"),
              ),
              ListTile(
                title: Text("我是关注列表"),
              ),
              ListTile(
                title: Text("我是关注列表"),
              ),
              ListTile(
                title: Text("我是关注列表"),
              ),
              ListTile(
                title: Text("我是关注列表"),
              ),
              ListTile(
                title: Text("我是关注列表"),
              ),
              ListTile(
                title: Text("我是关注列表"),
              ),
              ListTile(
                title: Text("我是关注列表"),
              ),
              ListTile(
                title: Text("我是关注列表"),
              ),
              ListTile(
                title: Text("我是关注列表"),
              ),
              ListTile(
                title: Text("我是关注列表"),
              ),
              ListTile(
                title: Text("我是关注列表"),
              ),
              ListTile(
                title: Text("缓存成功"),
              ),

            ],
          )),
          const ListTile(
            title: Text("我是热门列表"),
          ),
          const ListTile(
            title: Text("我是资讯列表"),
          ),
          const ListTile(
            title: Text("我是篮球列表"),
          ),
          const ListTile(
            title: Text("我是动漫列表"),
          ),
          const ListTile(
            title: Text("我是动画片列表"),
          ),
          const ListTile(
            title: Text("我是电影列表"),
          ),
          const ListTile(
            title: Text("我是电视剧列表"),
          ),
        ],
      ),
    );
  }

  @override
  void dispose() {
    // TODO: implement dispose
    super.dispose();
    _tabController.dispose();
    print("home销毁了");
  }
}

工具类KeepAliveWrapper

import 'package:flutter/material.dart';


//用来保存状态
class KeepAliveWrapper extends StatefulWidget {
  const KeepAliveWrapper(
      {Key? key, @required this.child, this.keepAlive = true})
      : super(key: key);

  final Widget? child;
  final bool keepAlive;
  @override
  State<KeepAliveWrapper> createState() => _KeepAliveWrapperState();
}

class _KeepAliveWrapperState extends State<KeepAliveWrapper> with AutomaticKeepAliveClientMixin {

  @override
  Widget build(BuildContext context) {
    return widget.child!;
  }

  @override
  // TODO: implement wantKeepAlive
  bool get wantKeepAlive => widget.keepAlive;

  @override
  void didUpdateWidget(covariant KeepAliveWrapper oldWidget) {
    // TODO: implement didUpdateWidget
    super.didUpdateWidget(oldWidget);
    if(oldWidget.keepAlive != widget.keepAlive){
      // keepAlive 状态需要更新, 是现在AutomaticKeepAliveClientMixin中
      updateKeepAlive();
    }
  }
}

这个类用来保存部件的状态的,比如当你输入到当前页面然后离开了,再回来页面会自己刷新,使用一个生命周期函数就能够保存原来的状态,这个生命周期函数是当widget修改的时候调用他

下面来说说底部导航栏

它是使用了bottomNavigationBar 来制作底部导航栏的,首先需要创建一个List<Widget>集合,用来跳转页面使用,再创建一个int类型的变量,用来获取集合跳转页面的索引

在BottomNavigationBar 里面写一个onTap :(index)再在里面调用setState函数修改索引

import 'package:flutter/material.dart';
import '../testAppBar/tabs/category.dart';
import '../testAppBar/tabs/message.dart';
import '../testAppBar/tabs/user.dart';
import '../testAppBar/tabs/home.dart';
import '../testAppBar/tabs/settings.dart';

class Tabs extends StatefulWidget {
  const Tabs({super.key});

  @override
  State<Tabs> createState() => _TabsState();
}

class _TabsState extends State<Tabs>{
  int _currentIndex =  0;

  final List<Widget> _pages = [
    HomePage(),
    CategoryPage(),
    MessagePage(),
    SettingsPage(),
    UserPage()
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("flutter demo"),
        backgroundColor: Colors.red,
        actions: [
          IconButton(onPressed: (){}, icon: Icon(Icons.search)),
          IconButton(onPressed: (){}, icon: Icon(Icons.more_horiz)),
        ],
      ),
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: _currentIndex,
        selectedItemColor: Colors.red,
        unselectedItemColor: Colors.black54,
        type: BottomNavigationBarType.fixed,
        onTap: (index){
          setState(() {
            _currentIndex = index;
          });
        },
        items: [
          BottomNavigationBarItem(icon: Icon(Icons.home),label: "首页"),
          BottomNavigationBarItem(icon: Icon(Icons.category),label: "分类"),
          BottomNavigationBarItem(icon: Icon(Icons.message),label: "消息"),
          BottomNavigationBarItem(icon: Icon(Icons.settings),label: "设置"),
          BottomNavigationBarItem(icon: Icon(Icons.people),label: "用户"),
        ],
      ),
      body: _pages[_currentIndex],
      drawer: const Drawer(
        child: Column(
          children: [
            Row(
              children: [
                Expanded(child: UserAccountsDrawerHeader(
                  currentAccountPicture: CircleAvatar(
                      backgroundImage: AssetImage("assets/My5.jpg")
                  ),
                  decoration: BoxDecoration(
                      image: DecorationImage(
                          image: AssetImage("assets/back1.jpeg"),
                          fit: BoxFit.cover
                      )
                  ),
                  accountName: Text("个人主页"),
                  accountEmail: Text("3093113975@qq.com"),
                ))
              ],
            ),
            ListTile(
              title: Text("编辑资料"),
              leading: CircleAvatar(
                child: Icon(Icons.edit),
              ),
            ),
            ListTile(
              title: Text("设置"),
              leading: CircleAvatar(
                child: Icon(Icons.settings),
              ),
            )
          ],
        ),
      ),
      floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
      floatingActionButton:  Container(
        height: 60,
        width: 60,
        padding: EdgeInsets.all(2),
        margin: EdgeInsets.only(top: 7),
        decoration: BoxDecoration(
          color: Colors.black12,
          borderRadius: BorderRadius.circular(30)
        ),
        child: FloatingActionButton(
          backgroundColor: _currentIndex == 2 ? Colors.red : Colors.blue,
          onPressed: (){
            setState(() {
              _currentIndex = 2;
            });
          },
          child: Icon(Icons.add),
        ),
      ),
    );
  }
}

drawer是标题栏的侧边导航,里面可以放widget,分析它的布局,一共有一列,三行,因为UserAccountDrawerHeader 用来制作侧边导航的头部的,它用了两个容器包裹的,头部使用了flex布局,给头部设置背景图,在Expanded使用BoxDecoration来设置背景,不能直接使用Image.assets 要使用它的一个DecorationImage 来使用图片,然后将图片覆盖整个空间

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值