AppBar自定义顶部按钮图标、颜色
常见属性:
| 属性 | 描述 |
|---|---|
| leading | 在标题前面显示的一个控件,在首页通常显示应用的 logo;在其他界面通常显示为返回按钮 |
| title | 标题,通常显示为当前界面的标题文字,可以放组件 |
| actions | 通常使用 IconButton 来表示,可以放按钮组 |
| bottom | 通常放tabBar,标题下面显示一个 Tab 导航栏 |
| backgroundColor | 导航背景颜色 |
| iconTheme | 图标颜色 |
| centerTitle | 标题是否居中显示 |
Flutter AppBar结合TabBar实现顶部Tab切换
TabBar常见属性:
| 属性 | 描述 |
|---|---|
| tabs | 显示的标签内容,一般使用Tab对象,也可以是其他的Widget |
| controller | TabController对象,控制tab切换和监听 |
| isScrollable | 是否可滚动 |
| indicatorColor | 指示器颜色 |
| indicatorWeight | 指示器高度 |
| indicatorPadding | 底部指示器的Padding |
| indicator | 指示器decoration,例如边框等 |
| indicatorSize | 指示器大小计算方式,TabBarIndicatorSize.label 跟文字等宽,TabBarIndicatorSize.tab 跟每个tab等宽 |
| labelColor | 选中label颜色 |
| labelStyle | 选中label的Style |
| labelPadding | 每个label的padding值 |
| unselectedLabelColor | 未选中label颜色 |
| unselectedLabelStyle | 未选中label的Style |
Tabbar TabBarView实现类似头条顶部导航
混入SingleTickerProviderStateMixin
class _HomePageState extends State<HomePage> with
SingleTickerProviderStateMixin{}
定义TabController
late TabController _tabController;
void initState() {
super.initState();
_tabController = TabController(length: 8, vsync: this);
_tabController.addListener(() {
if (_tabController.animation!.value == _tabController.index) {
print(_tabController.index); //获取点击或滑动页面的索引值
}
});
}
配置TabBar和TabBarView
import 'package:flutter/material.dart';
import 'package:flutter_application_1/user.dart';
import 'home.dart';
import 'category.dart';
import 'setting.dart';
import 'message.dart';
class TabsPage extends StatefulWidget {
const TabsPage({super.key});
@override
State<TabsPage> createState() => _TabsPageState();
}
class _TabsPageState extends State<TabsPage> {
int _currentIndex = 0;
final List _pageList = [
const HomePage(),
const CategoryPage(),
const MessagePage(),
const SettingPage(),
const UserPage(),
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: const Icon(Icons.menu),
onPressed: (){
print('menu');
},
),
title: const Text('Flutter App'),
actions: [
IconButton(
icon: const Icon(Icons.search),
onPressed: (){
print('search');
},
),
IconButton(
icon: const Icon(Icons.settings),
onPressed: (){
print('settings');
},
),
],
backgroundColor: Colors.red,
),
body: _pageList[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
type: BottomNavigationBarType.fixed, // 如果有4个以上的按钮,必须设置type
fixedColor: Colors.red, // 选中的颜色
//iconSize: 35, // 图标大小
onTap: (int index){
print(index);
setState(() {
_currentIndex = index;
});
},
items: const [
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.person),
label: '我的',
),
],
),
floatingActionButton: Container(
width: 60,
height: 60,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30),
color: Colors.white,
),
margin: const EdgeInsets.only(top: 4),
padding: const EdgeInsets.all(4),
child: FloatingActionButton(
onPressed: (){
setState(() {
_currentIndex = 2;
});
},
backgroundColor: _currentIndex == 2 ? Colors.red :Colors.blue,
child: const Icon(Icons.add),
),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
);
}
}

BottomNavigationBar 的页面中使用Tabbar 和 preferredSize组件
import 'package:flutter/material.dart';
class HomePage extends StatefulWidget {
const HomePage({super.key});
get list => null;
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin {
late TabController _tabController;
@override
void initState() {
super.initState();
// 初始化
_tabController = TabController(length: 19, vsync: this);
}
final List<String> list = [];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: PreferredSize(
preferredSize: const Size.fromHeight(40),
child: AppBar(
elevation: 1,
title: SizedBox(
height: 30,
child: TabBar(
controller: _tabController,
indicatorColor: Colors.red,
unselectedLabelColor: Colors.black,
labelColor: Colors.red,
indicatorSize: TabBarIndicatorSize.label,
isScrollable: true,
tabs: const [
Tab(text: '热门'),
Tab(text: '推荐'),
Tab(text: '关注'),
Tab(text: '收藏'),
Tab(text: '视频'),
Tab(text: '疫情'),
Tab(text: '新闻'),
Tab(text: '体育'),
Tab(text: '娱乐'),
Tab(text: '军事'),
Tab(text: '科技'),
Tab(text: '财经'),
Tab(text: '汽车'),
Tab(text: '房产'),
Tab(text: '国际'),
Tab(text: '时尚'),
Tab(text: '旅游'),
Tab(text: '历史'),
Tab(text: '其他'),
],
),
),
backgroundColor: Colors.white,
)
),
body: TabBarView(
controller: _tabController,
children: const [
Center(child: Text('热门')),
Center(child: Text('推荐')),
Center(child: Text('关注')),
Center(child: Text('收藏')),
Center(child: Text('视频')),
Center(child: Text('疫情')),
Center(child: Text('新闻')),
Center(child: Text('体育')),
Center(child: Text('娱乐')),
Center(child: Text('军事')),
Center(child: Text('科技')),
Center(child: Text('财经')),
Center(child: Text('汽车')),
Center(child: Text('房产')),
Center(child: Text('国际')),
Center(child: Text('时尚')),
Center(child: Text('旅游')),
Center(child: Text('历史')),
Center(child: Text('其他')),
],
)
);
}
}

自定义KeepAliveWrapper 缓存页面
AutomaticKeepAliveClientMixin 可以快速的实现页面缓存功能,但是通过混入的方式实现不是很优 雅, 所以我们有必要对AutomaticKeepAliveClientMixin 混入进行封装
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
bool get wantKeepAlive => widget.keepAlive;
@override
void didUpdateWidget(covariant KeepAliveWrapper oldWidget) {
if (oldWidget.keepAlive != widget.keepAlive) {
// keepAlive 状态需要更新,实现在 AutomaticKeepAliveClientMixin 中
updateKeepAlive();
}
super.didUpdateWidget(oldWidget);
}
}
导入组件使用KeepAliveWrapper包裹需要缓存的页面
监听TabController改变事件
void initState() {
super.initState();
// 初始化
_tabController = TabController(length: 19, vsync: this);
// 监听_tabController的改变事件
_tabController.addListener(() {
if(_tabController.animation!.value == _tabController.index){
print(_tabController.index); // 当前点击或者滑动的tab的下标
}
});
}
销毁事件
@override
void dispose() {
_tabController.dispose();
super.dispose();
}
import 'package:flutter/material.dart';
import 'package:flutter_application_1/tools/keepAliveWrapper.dart';
class HomePage extends StatefulWidget {
const HomePage({super.key});
get list => null;
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin {
late TabController _tabController;
@override
void initState() {
super.initState();
// 初始化
_tabController = TabController(length: 19, vsync: this);
// 监听_tabController的改变事件
_tabController.addListener(() {
if(_tabController.animation!.value == _tabController.index){
print(_tabController.index); // 当前点击或者滑动的tab的下标
}
});
}
// 销毁事件
@override
void dispose() {
_tabController.dispose();
super.dispose();
print('销毁');
}
final List<String> list = [];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: PreferredSize(
preferredSize: const Size.fromHeight(40),
child: AppBar(
elevation: 1,
title: SizedBox(
height: 30,
child: TabBar(
controller: _tabController,
indicatorColor: Colors.red,
unselectedLabelColor: Colors.black,
labelColor: Colors.red,
indicatorSize: TabBarIndicatorSize.label,
isScrollable: true,
tabs: const [
Tab(text: '热门'),
Tab(text: '推荐'),
Tab(text: '关注'),
Tab(text: '收藏'),
Tab(text: '视频'),
Tab(text: '疫情'),
Tab(text: '新闻'),
Tab(text: '体育'),
Tab(text: '娱乐'),
Tab(text: '军事'),
Tab(text: '科技'),
Tab(text: '财经'),
Tab(text: '汽车'),
Tab(text: '房产'),
Tab(text: '国际'),
Tab(text: '时尚'),
Tab(text: '旅游'),
Tab(text: '历史'),
Tab(text: '其他'),
],
),
),
backgroundColor: Colors.white,
)
),
body: TabBarView(
controller: _tabController,
children: [
KeepAliveWrapper(
child: ListView(
children: const[
ListTile(
title: Text('第一个tab1'),
),
ListTile(
title: Text('第一个tab2'),
),
ListTile(
title: Text('第一个tab3'),
),
ListTile(
title: Text('第一个tab4'),
),
ListTile(
title: Text('第一个tab5'),
),
ListTile(
title: Text('第一个tab6'),
),
ListTile(
title: Text('第一个tab7'),
),
ListTile(
title: Text('第一个tab8'),
),
ListTile(
title: Text('第一个tab9'),
),
ListTile(
title: Text('第一个tab10'),
),
ListTile(
title: Text('第一个tab11'),
),
ListTile(
title: Text('第一个tab12'),
),
ListTile(
title: Text('第一个tab13'),
),
ListTile(
title: Text('第一个tab14'),
),
ListTile(
title: Text('第一个tab15'),
),
ListTile(
title: Text('第一个tab16'),
),
ListTile(
title: Text('第一个tab17'),
),
ListTile(
title: Text('第一个tab18'),
),
ListTile(
title: Text('第一个tab19'),
),
],
),
),
Center(child: Text('推荐')),
Center(child: Text('关注')),
Center(child: Text('收藏')),
Center(child: Text('视频')),
Center(child: Text('疫情')),
Center(child: Text('新闻')),
Center(child: Text('体育')),
Center(child: Text('娱乐')),
Center(child: Text('军事')),
Center(child: Text('科技')),
Center(child: Text('财经')),
Center(child: Text('汽车')),
Center(child: Text('房产')),
Center(child: Text('国际')),
Center(child: Text('时尚')),
Center(child: Text('旅游')),
Center(child: Text('历史')),
Center(child: Text('其他')),
],
)
);
}
}
MaterialApp 去掉debug图标
MaterialApp(
debugShowCheckedModeBanner: false, //去掉debug图标
home: TabsPage(),
);