Flutter实现Onboarding Screen仅首次显示且Splash Screen每次显示求助
问题修复方案
问题分析
- SharedPreferences键名不一致:Onboarding中存储用的是
'onBoard',但main中读取用的是'onboard',大小写差异导致无法正确读取状态。 - 未传递状态到SplashScreen:main中获取了
isViewed但没有传给SplashScreen,导致Splash无法判断是否需要跳转Onboarding。 - SplashScreen跳转逻辑缺失判断:动画完成后直接固定跳Onboarding,没有根据用户是否看过Onboarding做分支处理。
修复步骤
1. 统一键名并传递状态到MyApp
修改main.dart,将读取到的状态传递给SplashScreen,同时统一SharedPreferences的键名:
int? isViewed; Future<void> main() async { WidgetsFlutterBinding.ensureInitialized(); final prefs = await SharedPreferences.getInstance(); final showLogin = prefs.getBool('showLogin') ?? false; Paint.enableDithering = true; await Firebase.initializeApp(); // 统一键名为'onboard_viewed',避免大小写问题 isViewed = prefs.getInt('onboard_viewed'); runApp(MyApp(showLogin: showLogin, isViewed: isViewed)); } class MyApp extends StatelessWidget { final bool showLogin; final int? isViewed; const MyApp({ Key? key, required this.showLogin, required this.isViewed, }) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( title: 'Strength', debugShowCheckedModeBanner: false, theme: ThemeData( appBarTheme: const AppBarTheme( color: Colors.white, elevation: 0, brightness: Brightness.light, iconTheme: IconThemeData(color: Colors.black), textTheme: TextTheme( headline6: TextStyle(color: Color(0xff888888), fontSize: 18), ), ), ), home: SplashScreen(showLogin: showLogin, isViewed: isViewed), ); } }
2. 更新OnboardingScreen的存储键名
修改_storeOnboardingInfo方法,使用统一的键名:
_storeOnboardingInfo() async { int isViewed = 0; SharedPreferences prefs = await SharedPreferences.getInstance(); // 与main中统一的键名保持一致 await prefs.setInt('onboard_viewed', isViewed); }
3. 修改SplashScreen的跳转逻辑
让SplashScreen接收参数,并根据用户是否看过Onboarding决定跳转目标:
class SplashScreen extends StatefulWidget { final bool showLogin; final int? isViewed; const SplashScreen({ Key? key, required this.showLogin, required this.isViewed, }) : super(key: key); @override _SplashScreenState createState() => _SplashScreenState(); } class _SplashScreenState extends State<SplashScreen> with TickerProviderStateMixin { late AnimationController _screenController; @override void initState() { super.initState(); _screenController = AnimationController(vsync: this); } @override Widget build(BuildContext context) { return Scaffold( body: Container( height: double.infinity, width: double.infinity, child: Lottie.asset( 'assets/lottie/splashScreen.lottie.json', fit: BoxFit.fill, controller: _screenController, onLoaded: (composition) { _screenController ..duration = composition.duration ..forward().whenComplete(() { // 判断逻辑:已看过Onboarding则跳Landing,否则跳Onboarding Widget targetScreen = widget.isViewed != null ? LandingScreen() : const OnboardingScreen(); Navigator.of(context).pushReplacement( PageTransition( type: PageTransitionType.fade, duration: const Duration(milliseconds: 1800), child: targetScreen, ), ); }); }, ), ), ); } }
逻辑说明
- 首次启动:
isViewed为null,Splash动画结束后跳转到OnboardingScreen;用户点击"Get Started"后,存储onboard_viewed状态并跳转到LandingScreen。 - 非首次启动:
isViewed存在(值为0),Splash动画结束后直接跳转到LandingScreen,不再显示Onboarding。
内容的提问来源于stack exchange,提问作者Ta-Ty
相关产品推荐
相关产品推荐

