简述
在移动应用开发中,启动页和引导页是用户初次接触应用时的重要组成部分,能够提升用户体验和导航用户了解应用功能。本文将介绍如何使用Flutter实现启动页和引导页,并展示相关代码实现。
启动页
启动页是应用的第一个页面,首次进入需要进入应用引导页面,非首次进入欢迎界面(广告界面),所以我们需要保存是否首次进入APP,这里采用:
shared_preferences: ^2.2.2
我们可以定义一个工具类SpUtil
/// 键值对 key
class SPKey{
static const String isFirstOpen = 'isFirstOpen';
}
/// 键值对存储
class SpUtil {
///是否第一次打开
static bool isFirstOpen() {
SharedPreferences sp = Get.find<SharedPreferences>();
return sp.getBool(SPKey.isFirstOpen) ?? true;
}
/// 已打开APP
static void appIsOpen() {
Get.find<SharedPreferences>().setBool(SPKey.isFirstOpen, false);
}
}
这里配合了Getx一起使用了。
然后在启动页里判断是否是首次来切换是欢迎界面还是引导页面。
/// 启动页面-欢迎界面/引导页面
class SplashPage extends StatelessWidget {
const SplashPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
ScreenUtil.init(context, designSize: const Size(375, 812));
var child = SpUtil.isFirstOpen() ? const GuidePage() : const WelcomePage();
return Scaffold(
body: child,
resizeToAvoidBottomInset: false,
);
}
}
欢迎界面
欢迎界面通常用于展示应用的logo或者欢迎界面。在我们的Flutter项目中,我们通过WelcomePage
来实现启动页功能。
效果
Screen_Recording_20240401-131351.gif代码
/// 欢迎页面
class WelcomePage extends StatelessWidget {
const WelcomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final logic = Get.put(WelcomeLogic());
return WillPopScope(
onWillPop: () {
return Future.value(false);
},
child: Stack(
children: [
Positioned.fill(
child: Container(
color: const Color(0xFF40C2BB),
width: double.infinity,
height: double.infinity,
child: Image.asset(
R.splash_bg_jpg,
fit: BoxFit.cover,
),
),
),
Obx(
() => Positioned(
right: 16.w,
top: 30.w,
child: InkWell(
child: Container(
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 3.w),
decoration: BoxDecoration(
border: Border.all(color: Colors.white, width: 1.w),
borderRadius: BorderRadius.all(Radius.circular(8.w)),
),
child: Text(
logic.state.adStr.value,
style: const TextStyle(
color: Colors.white,
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
),
onTap: () {
logic.openHomePage();
},
),
),
),
],
),
);
}
}
class WelcomeLogic extends GetxController {
final WelcomeState state = WelcomeState();
int _timeCount = 3;
Timer? _timer;
@override
void onReady() {
super.onReady();
_startTimer();
}
///打开计时器
void _startTimer() {
_timer = Timer.periodic(const Duration(seconds: 1), (Timer t) {
state.adStr.value = "广告$_timeCount秒跳过";
if (_timeCount <= 0) {
openHomePage();
return;
}
_timeCount--;
});
}
///停止计时器
void _stopTimer() {
_timer?.cancel();
_timer = null;
}
/// 打开首页
void openHomePage() {
_stopTimer();
Get.offAndToNamed(Routers.homePage);
}
@override
void onClose() {
_stopTimer();
super.onClose();
}
}
class WelcomeState {
RxString adStr = "广告3秒跳过".obs;
}
在WelcomePage
中,我们展示了一个背景图和一个跳过广告的按钮。在逻辑部分,我们设置了一个计时器,3秒后自动跳转到首页/登录页。
引导页
引导页用于向用户介绍应用的功能和特点,帮助用户快速上手。在我们的Flutter项目中,我们通过GuidePage
来实现引导页功能。
效果
Screen_Recording_20240401-131332.gif代码
/// 引导页
class GuidePage extends StatelessWidget {
const GuidePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final logic = Get.put(GuideLogic());
return Stack(
children: [
Positioned.fill(
child: PageView(
controller: logic.pageController,
onPageChanged: (index) {
logic.state.currentPageIndex.value = index;
},
children: _guideWidgets(),
),
),
Positioned(
bottom: 50,
left: 50,
right: 50,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Expanded(
child: InkWell(
onTap: () {
logic.findPeople();
},
child: Container(
alignment: Alignment.center,
padding: EdgeInsets.symmetric(vertical: 10.w),
decoration: BoxDecoration(
color: const Color(0xFF40C2BB),
borderRadius: BorderRadius.circular(8.r),
),
child: Text(
RS.findPeople.tr,
style: TextStyle(
fontWeight: FontWeight.w700,
fontSize: 16.sp,
color: Colors.white,
),
),
),
),
),
SizedBox(
width: 40.w,
),
Expanded(
child: InkWell(
onTap: () {
logic.findWork();
},
child: Container(
alignment: Alignment.center,
padding: EdgeInsets.symmetric(vertical: 10.w),
decoration: BoxDecoration(
color: const Color(0xFF40C2BB),
borderRadius: BorderRadius.circular(8.r),
),
child: Text(
RS.findWork.tr,
style: TextStyle(
fontWeight: FontWeight.w700,
fontSize: 16.sp,
color: Colors.white,
),
),
),
),
),
],
),
),
],
);
}
///引导页子布局们
List<Widget> _guideWidgets() {
return [
_itemGuideWidget(
index: 0,
icon: R.guide_one_png,
title: '与未来上司直接沟通',
des: '百万数量boss已入驻,等你开聊',
),
_itemGuideWidget(
index: 1,
icon: R.guide_two_png,
title: '聊着天把工作搞定',
des: '谈薪资,聊待遇,直接沟通,解答疑问',
),
_itemGuideWidget(
index: 2,
icon: R.guide_three_png,
title: '快速融入新单位',
des: '找工作到入职,最快只要一天',
),
];
}
///单个子布局
Widget _itemGuideWidget({
required int index,
required String icon,
required String title,
required String des,
}) {
return Column(
children: [
AspectRatio(
aspectRatio: 640 / 628,
child: Image.asset(
icon,
fit: BoxFit.fitWidth,
),
),
Container(
height: 50.w,
color: Colors.grey.withAlpha(20),
),
SizedBox(height: 25.w),
_guideIndexWidget(index: index),
SizedBox(height: 25.w),
Text(
title,
style: TextStyle(
fontSize: 30.sp,
fontWeight: FontWeight.w800,
color: Colors.black,
),
),
SizedBox(height: 10.w),
Text(
des,
style: TextStyle(
fontSize: 14.sp,
fontWeight: FontWeight.w400,
color: Colors.grey,
),
),
Expanded(child: Container()),
],
);
}
///子布局索引
Widget _guideIndexWidget({required int index}) {
return Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: 10,
height: 10,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: (index == 0)
? const Color(0xFF40C2BB)
: Colors.grey.withAlpha(50),
),
),
SizedBox(width: 10.w),
Container(
width: 10,
height: 10,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: (index == 1)
? const Color(0xFF40C2BB)
: Colors.grey.withAlpha(50),
),
),
SizedBox(width: 10.w),
Container(
width: 10,
height: 10,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: (index == 2)
? const Color(0xFF40C2BB)
: Colors.grey.withAlpha(50),
),
),
],
);
}
}
class GuideLogic extends GetxController {
final GuideState state = GuideState();
PageController pageController = PageController();
Timer? _timer;
@override
void onReady() {
super.onReady();
_startLoopGuide();
}
/// 启动轮询器,每隔3秒切换到下一页
void _startLoopGuide() {
_timer = Timer.periodic(const Duration(seconds: 3), (timer) {
state.currentPageIndex.value = state.currentPageIndex.value == 2
? 0
: state.currentPageIndex.value + 1;
pageController.animateToPage(
state.currentPageIndex.value,
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
});
}
///停止轮询
void _stopLoopGuide() {
_timer?.cancel();
_timer == null;
}
///我要招人
void findPeople() {
_stopLoopGuide();
SpUtil.appIsOpen();
Get.offAndToNamed(Routers.homePage);
}
///我要应聘
void findWork() {
_stopLoopGuide();
SpUtil.appIsOpen();
Get.offAndToNamed(Routers.homePage);
}
@override
void onClose() {
_stopLoopGuide();
super.onClose();
}
}
class GuideState {
RxInt currentPageIndex = 0.obs;
}
在GuidePage
中,我们展示了一个PageView来滑动展示多个引导页内容。用户可以通过滑动页面了解应用的功能和特点。在底部我们放置了两个按钮,分别用于“我要招人”和“我要应聘”,点击按钮后跳转到首页。
通过以上的实现,我们完成了Flutter仿Boss应用的启动页和引导页功能,帮助用户更好地了解应用,并提供了快速导航到首页的功能。
详情见:github.com/yixiaolunhui/flutter_project
网友评论