简述
在移动应用开发中,启动页和引导页是用户初次接触应用时的重要组成部分,能够提升用户体验和导航用户了解应用功能。本文将介绍如何使用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
来实现启动页功能。
效果
代码
/// 欢迎页面 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
来实现引导页功能。
效果
代码
/// 引导页 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