目录
顶部Tab(DefaultTabController实现方式):
还有另一种实现方式:实现SingleTickerProviderStateMixin(适用于已确定要显示Tab标签)
底部Tab:使用BottomNavigationBar来实现。
class IndexPage extends StatefulWidget {
@override
_IndexPageState createState() => _IndexPageState();
}
class _IndexPageState extends State<IndexPage> {
List<BottomNavigationBarItem> tabs = [
BottomNavigationBarItem(
icon: Image.asset("images/home_unactive.png", width: 24, height: 24),
activeIcon: Image.asset("images/home_active.png", width: 24 height: 24),
title: Text("首页"),
),
BottomNavigationBarItem(
icon: Image.asset("images/home_unactive.png", width: 24, height: 24),
activeIcon: Image.asset("images/home_active.png", width: 24 height: 24),
title: Text("分类"),
),
BottomNavigationBarItem(
icon: Image.asset("images/home_unactive.png", width: 24, height: 24),
activeIcon: Image.asset("images/home_active.png", width: 24 height: 24),
title: Text("购物车"),
),
BottomNavigationBarItem(
icon: Image.asset("images/home_unactive.png", width: 24, height: 24),
activeIcon: Image.asset("images/home_active.png", width: 24 height: 24),
title: Text("我的"),
),
];
List<Widget> pages = [
HomePage(),
CategoryPage(),
ShoppingCartPage(),
MyPage(),
];
int currentIndex = 0;
PageController _controller = PageController();
@override
Widget build(BuildContext context) {
return Scaffold(
body: PageView(
controller: _controller,
children: pages,
physics: NeverScrollableScrollPhysics(), //禁止页面滚动
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: currentIndex,
items: tabs,
unselectedFontSize: 13,
selectedFontSize: 13,
type: BottomNavigationBarType.fixed,
onTap: (index) {
_controller.jumpToPage(index);
setState(() {
currentIndex = index;
});
},
),
);
}
}
顶部Tab(DefaultTabController实现方式):
body: DefaultTabController(
length: 3,//项的数量
initialIndex: 0,//默认选择第一项
child: Column(
children: <Widget>[
Container(
color: Colors.lightBlue,
child: AspectRatio(
aspectRatio: 8.0,
child: TabBar(
// isScrollable: true,//项少的话,无需滚动(自动均分屏幕宽度),多的话,设为true
indicatorColor: Colors.red,
indicatorWeight: 2,
unselectedLabelColor: Colors.white,
labelColor: Colors.red,
tabs: [
Tab(
text: "科技",
),
Tab(
text: "汽车",
),
Tab(
text: "金融",
),
],
),
),
),
Expanded(
child: TabBarView(
children: [
TechFragment(),
CarFragment(),
FinanceFragment(),
],
),
)
],
),
),
还有另一种实现方式:实现SingleTickerProviderStateMixin(适用于已确定要显示Tab标签)
initState中:
tabController = TabController(length: list.length, vsync: this);
如果是动态添加标签,就需要实现TickerProviderStateMixin,注意:不是SingleTickerProviderStateMixin,接口请求结束后,在setState中仍需要再初始化一遍TabController
setState(() {
tabController = TabController(length: list.length, vsync: this);
});
有些需求会要求实现某部分布局滑到顶部后吸顶悬浮
可以是一个tabbar,也可以是一个别的组件,但是目的都是让其吸顶悬浮在那,示例代码如下:
import 'package:flutter/material.dart';
class Home extends StatefulWidget {
@override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: PreferredSize(
child: AppBar(
backgroundColor: Colors.red,
),
preferredSize: Size.fromHeight(0),
),
body: NestedScrollView(
headerSliverBuilder: (_, innerBoxIsScrolled) {
return [
SliverToBoxAdapter(//其他内容用SliverToBoxAdapter包裹
child: Container(
height: 150,
color: Colors.greenAccent,
alignment: Alignment.center,
child: Text(
"头部",
style: TextStyle(
fontSize: 50,
color: Colors.white,
),
),
),
),
SliverPersistentHeader(//悬浮区
pinned: true,
delegate: _SliverAppBarDelegate(
minHeight: 70,
maxHeight: 70,
child: Container(
child: Row(
children: <Widget>[
Expanded(
child: InkWell(
onTap: () {
print("Tab1");
},
child: Container(
alignment: Alignment.center,
child: Text("Tab1"),
color: Colors.red,
),
),
),
Container(
width: 1,
height: 70,
color: Colors.white,
),
Expanded(
child: InkWell(
onTap: () {
print("Tab2");
},
child: Container(
alignment: Alignment.center,
child: Text("Tab2"),
color: Colors.lightBlue,
),
),
),
],
),
),
),
),
];
},
body: ListView.builder(//列表区
itemCount: 30,
itemBuilder: (_, index) {
return Container(
height: 60,
alignment: Alignment(0, 0),
child: Text("数据$index"),
);
},
),
),
);
}
}
class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
_SliverAppBarDelegate({
@required this.minHeight,
@required this.maxHeight,
@required this.child,
});
final double minHeight;
final double maxHeight;
final Widget child;
@override
double get minExtent => minHeight;
@override
double get maxExtent => maxHeight;
@override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
return SizedBox.expand(child: child);
}
@override
bool shouldRebuild(_SliverAppBarDelegate oldDelegate) {
return maxHeight != oldDelegate.maxHeight ||
minHeight != oldDelegate.minHeight ||
child != oldDelegate.child;
}
}