You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter实现首次启动应用引导页:组件选择与首次启动检测

嗨,这个问题我刚好有不少实践经验,给你梳理下完整的解决方案:

一、检测应用首次启动状态

要判断是不是首次启动,核心是持久化存储一个标记,下次启动时读取这个标记就能知道用户有没有看过引导页了。Flutter里最常用的轻量存储方案就是shared_preferences,步骤如下:

  1. 先在pubspec.yaml里添加依赖:
dependencies:
  flutter:
    sdk: flutter
  shared_preferences: ^2.2.2 # 版本号可以用最新的
  1. 初始化并读取标记:
    我们可以在应用启动时异步读取存储的hasSeenOnboarding字段——如果这个字段不存在或者为false,就说明是首次启动;反之则直接进入主页面。
二、选择合适的Widget实现引导页

首推PageView,它天生适配多页滑动的引导场景,自定义自由度极高。你可以给每个引导页设计不同的布局(比如图片+文字+按钮),最后一页放一个「开始使用」的按钮,点击后更新存储标记并跳转到主页面。

当然如果不想自己从零写,也可以用成熟的第三方库(比如introduction_screen),但原生PageView的优势是完全可控,能贴合你的UI设计。

三、完整代码示例

1. 入口处的判断逻辑

用FutureBuilder来处理异步读取存储的操作,避免阻塞UI:

import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '引导页示例',
      home: FutureBuilder<bool>(
        future: _checkIfFirstLaunch(),
        builder: (context, snapshot) {
          // 等待异步操作完成
          if (snapshot.connectionState != ConnectionState.done) {
            return const Scaffold(body: Center(child: CircularProgressIndicator()));
          }
          // 根据结果跳转到引导页或主页
          return snapshot.data == false ? const OnboardingScreen() : const HomeScreen();
        },
      ),
    );
  }

  // 检查是否首次启动
  Future<bool> _checkIfFirstLaunch() async {
    final prefs = await SharedPreferences.getInstance();
    return prefs.getBool('hasSeenOnboarding') ?? false;
  }
}

2. 引导页Widget实现

用PageView做滑动页面,最后一页添加跳转按钮:

class OnboardingScreen extends StatefulWidget {
  const OnboardingScreen({super.key});

  @override
  State<OnboardingScreen> createState() => _OnboardingScreenState();
}

class _OnboardingScreenState extends State<OnboardingScreen> {
  final PageController _pageController = PageController();
  int _currentPage = 0;

  // 引导页数据
  final List<Map<String, String>> _pages = [
    {
      'title': '欢迎使用',
      'description': '这是你的第一个引导页面',
      'image': 'assets/onboard1.png' // 替换成你的图片路径
    },
    {
      'title': '功能介绍',
      'description': '这里展示应用的核心功能',
      'image': 'assets/onboard2.png'
    },
    {
      'title': '开始探索',
      'description': '点击按钮进入应用',
      'image': 'assets/onboard3.png'
    },
  ];

  // 标记引导页已查看并跳转到主页
  Future<void> _markOnboardingSeen() async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.setBool('hasSeenOnboarding', true);
    if (mounted) {
      Navigator.pushReplacement(
        context,
        MaterialPageRoute(builder: (context) => const HomeScreen()),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [
          Expanded(
            child: PageView.builder(
              controller: _pageController,
              itemCount: _pages.length,
              onPageChanged: (index) {
                setState(() => _currentPage = index);
              },
              itemBuilder: (context, index) {
                return Padding(
                  padding: const EdgeInsets.all(32.0),
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      Image.asset(_pages[index]['image']!),
                      const SizedBox(height: 40),
                      Text(
                        _pages[index]['title']!,
                        style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
                      ),
                      const SizedBox(height: 16),
                      Text(
                        _pages[index]['description']!,
                        textAlign: TextAlign.center,
                        style: const TextStyle(fontSize: 16),
                      ),
                    ],
                  ),
                );
              },
            ),
          ),
          // 页码指示器
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: List.generate(
              _pages.length,
              (index) => Container(
                width: 8,
                height: 8,
                margin: const EdgeInsets.symmetric(horizontal: 4),
                decoration: BoxDecoration(
                  shape: BoxShape.circle,
                  color: _currentPage == index ? Colors.blue : Colors.grey,
                ),
              ),
            ),
          ),
          const SizedBox(height: 40),
          // 跳转按钮
          _currentPage == _pages.length - 1
              ? ElevatedButton(
                  onPressed: _markOnboardingSeen,
                  child: const Text('开始使用'),
                )
              : TextButton(
                  onPressed: () => _pageController.nextPage(
                    duration: const Duration(milliseconds: 300),
                    curve: Curves.easeInOut,
                  ),
                  child: const Text('下一步'),
                ),
          const SizedBox(height: 40),
        ],
      ),
    );
  }
}

// 主页示例
class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('主页')),
      body: const Center(child: Text('欢迎进入应用!')),
    );
  }
}
一些额外提示
  • 如果需要重置引导页(比如测试时),可以在设置页添加一个按钮,调用prefs.remove('hasSeenOnboarding')来清除标记。
  • 引导页的动画可以再加一些细节,比如页面切换时的渐入效果,提升用户体验。
  • 对于安卓和iOS的沉浸式状态栏,可以在引导页里设置SystemChrome来适配。

内容的提问来源于stack exchange,提问作者Darkhan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 10:24:02