前言
之前做了个仿掘金的flutter程序,代码已经放到了guthub上去,由于一些个人问题,有较长一段时间没有更新flutter的相关文章,本篇将会介绍如何构建tabbar,让tabbar有用滑动切屏的效果
要构建tabbar的滑动切屏,主要有以下几点:
- 入口文件的配置
- 页面的一个整体构造
- 上:标题栏组件
- 中:页面跳转、滑动渲染中间部分的页面
- 下:tabbar的渲染及相关交互逻辑
入口文件的配置
main.dart 文件
该文件主要进行一些数据、缓存的初始化,以及启动页面时的页面跳转至什么地方
有些应用还需判断用户是否已经登陆,并跳转至登陆页面,这里由于时一个博客网站,并把入口跳登陆的一些逻辑删除了。
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_application/config/storage_manager.dart';
import 'package:flutter_application/config/theme.dart';
import 'package:flutter_application/root/root_page.dart';
import 'package:flutter_application/route/route.dart';
import 'mine.dart';
void main() async{
FlutterError.onError = (FlutterErrorDetails details) {
FlutterError.dumpErrorToConsole(details);
if (kReleaseMode) exit(1);
};
// 确保初始化
WidgetsFlutterBinding.ensureInitialized();
// 缓存初始化
await StorageManager.init();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO判断是否登陆
return MaterialApp(
navigatorKey: navGk,
title: 'Flutter Blog 练习',
theme: ThemeData(
scaffoldBackgroundColor: bgColor,
hintColor: Colors.grey.withOpacity(0.3),
splashColor: Colors.transparent,
canvasColor: Colors.transparent
),
debugShowCheckedModeBanner: false,
routes: {
'/': (context) {
return RootPage();
}
},
);
}
}
页面整体的构建
一般情况,tab页面主要分为了以下三部分
- 上:标题栏组件
- 中:页面跳转、滑动渲染中间部分的页面
- 下:tabbar的渲染及相关交互逻辑
标题栏组件
common_bar.dart
该组件主要配置了标题、交互图标、主题色及一些相关样式等的部分
交互逻辑中主要实现的点击的交互效果
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_application/config/theme.dart';
class CommonBar extends StatelessWidget implements PreferredSizeWidget {
final String title;
final bool showShadow;
final List<Widget>? rightDMActions;
final Color backgroundColor;
final Color mainColor;
final Widget? titleW;
final Widget? leadingW;
final PreferredSizeWidget? bottom;
final String leadingImg;
CommonBar(
{this.title = '',
this.showShadow = false,
this.rightDMActions,
this.backgroundColor = appBarColor,
this.mainColor = Colors.white,
this.titleW,
this.leadingW,
this.bottom,
this.leadingImg = ''});
// tabBar 左侧图标及相关操作逻辑
Widget? leading(BuildContext context) {
bool isShow = Navigator.canPop(context);
if (isShow) {
return new InkWell(
child: new Container(
width: 15,
height: 28,
child: leadingImg != ''
? new Image.asset(leadingImg)
: new Icon(CupertinoIcons.back, color: mainColor),
),
onTap: () {
if (Navigator.canPop(context)) {
FocusScope.of(context).requestFocus(new FocusNode());
Navigator.pop(context);
}
},
);
} else {
return null;
}
}
@override
Widget build(BuildContext context) {
// 封装appBar
Widget appBar = new AppBar(
title: titleW != null
? titleW
: new Text(
title,
style: new TextStyle(
color: mainColor,
fontSize: 16.0,
fontWeight: FontWeight.w600,
),
),
backgroundColor: backgroundColor,
elevation: 0,
brightness: Brightness.light,
leading: leadingW ?? leading(context),
centerTitle: true,
actions: rightDMActions ?? [new Center()],
bottom: bottom ?? null,
);
return showShadow
? new Container(
decoration: BoxDecoration(
border: Border(
bottom: new BorderSide(color: Colors.grey, width: 0.5))),
child: appBar,
)
: appBar;
}
@override
// TODO: implement preferredSize
Size get preferredSize => new Size(100, 50);
}
页面跳转及滑动的效果
root_tabbar.dart
滑动的主要是先判断应用环境为Android还是IOS
Android的滑动方法:ClampingScrollPhysics()
IOS的滑动方法:NeverScrollableScrollPhysics()
然后根据索引值切换相关tabbarModel数组的相关页面
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_application/config/theme.dart';
import 'package:flutter_application/pages/common/bar/common_bar.dart';
class RootTabBar extends StatefulWidget {
final List pages;
final int currentIndex;
RootTabBar({required this.pages, this.currentIndex = 0});
State<StatefulWidget> createState() => new RootTabBarState();
}
class RootTabBarState extends State<RootTabBar> {
List<BottomNavigationBarItem> pages = [];
late int currentIndex;
late PageController pageController;
@override
void initState() {
super.initState();
currentIndex = widget.currentIndex;
pageController = PageController(initialPage: currentIndex);
widget.pages.forEach((element) {
TabBarModel model = element;
pages.add(new BottomNavigationBarItem(
icon: model.icon,
activeIcon: model.selectIcon,
title: new Text(
model.title!,
style: new TextStyle(fontSize: 12.0),
)));
});
}
@override
Widget build(BuildContext context) {
// if(pageController) {
pageController = PageController(initialPage: currentIndex);
// }
final BottomNavigationBar bottomNavigationBar = new BottomNavigationBar(
items: pages,
type: BottomNavigationBarType.fixed,
currentIndex: currentIndex,
onTap: (int index) {
setState(() {
currentIndex = index;
pageController.jumpToPage(currentIndex);
});
},
unselectedFontSize: 12.0,
selectedFontSize: 12.0,
selectedItemColor: primaryColor,
elevation: 0,
);
var appBar = new CommonBar(
title: widget.pages[currentIndex].title, showShadow: false);
return Scaffold(
resizeToAvoidBottomInset: false, // 键盘弹起引起的挤压问题
// 底部tab栏
bottomNavigationBar: new Theme(
data: new ThemeData(
canvasColor: Colors.grey[50],
highlightColor: Colors.transparent,
splashColor: Colors.transparent),
child: new Container(
decoration: BoxDecoration(
border: Border(top: BorderSide(color: lineColor, width: 0.2))),
child: bottomNavigationBar,
)),
// 头部tab栏
appBar: appBar,
// body
body: new PageView.builder(
itemBuilder: (BuildContext context, int index) =>
widget.pages[index].page,
controller: pageController,
itemCount: pages.length,
physics: Platform.isAndroid
? new ClampingScrollPhysics()
: new NeverScrollableScrollPhysics(),
onPageChanged: (int index) => setState(() => currentIndex = index),
),
);
}
}
class TabBarModel {
final String? title;
final Widget icon;
final Widget? selectIcon;
final Widget? page;
TabBarModel({this.title, required this.icon, this.selectIcon, this.page});
}
tabbar的相关页面配置
root_page.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_application/config/theme.dart';
import 'package:flutter_application/pages/tabbar/Index.dart';
import 'package:flutter_application/pages/tabbar/mine.dart';
import 'package:flutter_application/pages/tabbar/boil.dart';
import 'package:flutter_application/root/root_tabbar.dart';
import 'package:flutter_application/route/route.dart';
class RootPage extends StatefulWidget {
_RootPageState createState() => _RootPageState();
}
class _RootPageState extends State<RootPage> {
@override
Widget build(BuildContext context) {
List<TabBarModel> pages = <TabBarModel>[
new TabBarModel(
title: "首页",
icon: const Icon(IconData(0xe7c0,fontFamily: 'MyIcons')),
selectIcon: const Icon(IconData(0xe7c0,fontFamily: 'MyIcons'),color: primaryColor,),
page: new IndexPage()),
// new TabBarModel(
// title: '沸点',
// icon: new LoadImage("assets/images/tab_order_normal_icon.png"),
// selectIcon:
// new LoadImage("assets/images/tab_order_selected_icon.png"),
// page: new BoilPage()),
new TabBarModel(
title: '发现',
icon: const Icon(IconData(0xe8d6,fontFamily: 'MyIcons')),
selectIcon:const Icon(IconData(0xe8d6,fontFamily: 'MyIcons'),color: primaryColor,),
page: new BoilPage()),
// new TabBarModel(
// title: '小册',
// icon: new LoadImage("assets/images/tab_order_normal_icon.png"),
// selectIcon:
// new LoadImage("assets/images/tab_order_selected_icon.png"),
// page: new BoilPage()),
new TabBarModel(
title: '我的',
icon: const Icon(IconData(0xe613,fontFamily: 'MyIcons')),
selectIcon:const Icon(IconData(0xe613,fontFamily: 'MyIcons'),color: primaryColor,),
page: new MinePage())
];
return new Scaffold(
key: scaffoldGK,
body: new RootTabBar(pages: pages, currentIndex: 0),
);
}
}
class LoadImage extends StatelessWidget {
final String img;
LoadImage(this.img);
@override
Widget build(BuildContext context) {
return new Container(
margin: EdgeInsets.only(bottom: 2.0),
child: new Image.asset(img, fit: BoxFit.cover, gaplessPlayback: true),
);
}
}