目录
这个案例中用了哪些东西?
先来分析一下,头部有三个图标,使用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 来使用图片,然后将图片覆盖整个空间