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

如何让Flutter应用的引导页仅在首次安装时显示

实现Flutter引导页仅首次安装显示的方案

要让引导页只在用户首次安装应用时显示,核心是通过本地存储记录引导页的显示状态,这里使用Flutter官方推荐的shared_preferences轻量存储包实现。

步骤1:添加依赖

在项目的pubspec.yaml中添加依赖:

dependencies:
  flutter:
    sdk: flutter
  introduction_screen: ^3.1.1 # 保留你原有的版本
  shared_preferences: ^2.2.2 # 新增该依赖

执行flutter pub get完成依赖安装。

步骤2:修改引导页代码

更新IntroScreen,加入状态判断和存储逻辑:

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:introduction_screen/introduction_screen.dart';
import 'package:shared_preferences/shared_preferences.dart'; // 引入存储包

import 'widget_tree.dart';

class IntroScreen extends StatefulWidget {
  @override
  _IntroScreenState createState() => _IntroScreenState();
}

class _IntroScreenState extends State<IntroScreen> {
  final introKey = GlobalKey<IntroductionScreenState>();
  late SharedPreferences _prefs;

  @override
  void initState() {
    super.initState();
    _initPrefs();
  }

  // 初始化SharedPreferences
  Future<void> _initPrefs() async {
    _prefs = await SharedPreferences.getInstance();
  }

  void _onIntroEnd(context) async {
    // 标记引导页已完成
    await _prefs.setBool('hasShownIntro', true);
    Navigator.of(context).pushReplacement( // 替换路由,避免返回引导页
      MaterialPageRoute(builder: (_) => WidgetTree()),
    );
  }

  Widget _buildFullscreenImage() {
    return Image.asset(
      'assets/images/intro1.png',
      fit: BoxFit.cover,
      height: double.infinity,
      width: double.infinity,
      alignment: Alignment.center,
    );
  }

  Widget _buildImage(String assetName, [double width = 350]) {
    return Image.asset('assets/$assetName', width: width);
  }

  @override
  Widget build(BuildContext context) {
    const bodyStyle = TextStyle(fontSize: 19.0);

    const pageDecoration = PageDecoration(
      titleTextStyle: TextStyle(
          color: Color.fromARGB(255, 29, 116, 182),
          fontSize: 30.0,
          fontWeight: FontWeight.w700,
          fontFamily: 'assets/fonts/Ubuntu-B.ttf'),
      bodyTextStyle: bodyStyle,
      bodyPadding: EdgeInsets.fromLTRB(16.0, 0.0, 16.0, 16.0),
      pageColor: Color.fromARGB(0, 255, 255, 255),
      imagePadding: EdgeInsets.zero,
    );

    return IntroductionScreen(
      key: introKey,
      globalBackgroundColor: Colors.white,
      globalHeader: const Align(
        alignment: Alignment.topRight,
        child: SafeArea(
          child: Padding(
            padding: EdgeInsets.only(top: 16, right: 16),
          ),
        ),
      ),
      pages: [
        PageViewModel(
          title: '欢迎使用',
          body: '欢迎来到Mawed,你可以轻松预约诊所,选择适合你的医院和心仪的医生。',
          image: Image.asset(
            'assets/images/intro1.png',
            width: 400,
            height: 400,
            fit: BoxFit.fitWidth,
            alignment: AlignmentDirectional.bottomCenter,
          ),
          decoration: pageDecoration,
        ),
        PageViewModel(
          title: "立即预约",
          body: "足不出户轻松完成预约,就诊时出示预约凭证即可",
          image: Image.asset(
            'assets/images/intro2.jpg',
            width: 350,
            height: 350,
            fit: BoxFit.fitWidth,
            alignment: AlignmentDirectional.center,
          ),
          decoration: pageDecoration,
        ),
        PageViewModel(
          title: "避免拥挤",
          body: "告别排队浪费时间,按时就诊更高效",
          image: Image.asset(
            'assets/images/intro3.jpg',
            width: 350,
            height: 350,
            fit: BoxFit.fitWidth,
            alignment: AlignmentDirectional.center,
          ),
          decoration: pageDecoration,
        ),
        PageViewModel(
          title: "选择医院",
          body: "挑选最适合、离你最近的医院",
          image: Image.asset(
            'assets/images/intro4.jpg',
            width: 350,
            height: 350,
            fit: BoxFit.fitWidth,
            alignment: AlignmentDirectional.center,
          ),
          decoration: pageDecoration,
        ),
        PageViewModel(
          title: "你的症状是?",
          body: "仔细选择对应的科室或医疗专科",
          image: Image.asset('assets/images/intro5.png',
              width: 350,
              height: 350,
              fit: BoxFit.fitWidth,
              alignment: AlignmentDirectional.center),
          decoration: pageDecoration,
        ),
        PageViewModel(
          title: "了解医生档期",
          body: "查看医生的可预约时间,选择你方便的时段",
          image: Image.asset('assets/images/intro6.png',
              width: 400,
              height: 400,
              fit: BoxFit.fitWidth,
              alignment: AlignmentDirectional.bottomCenter),
          decoration: pageDecoration,
        ),
      ],
      onDone: () => _onIntroEnd(context),
      onSkip: () => _onIntroEnd(context),
      showSkipButton: false,
      skipOrBackFlex: 0,
      nextFlex: 0,
      showBackButton: true,
      back: const Icon(Icons.arrow_back),
      skip: const Text('跳过', style: TextStyle(fontWeight: FontWeight.w600)),
      next: const Icon(Icons.arrow_forward),
      done:
          const Text('立即预约', style: TextStyle(fontWeight: FontWeight.w600)),
      curve: Curves.fastLinearToSlowEaseIn,
      controlsMargin: const EdgeInsets.all(16),
      controlsPadding: kIsWeb
          ? const EdgeInsets.all(12.0)
          : const EdgeInsets.fromLTRB(8.0, 4.0, 8.0, 4.0),
      dotsDecorator: const DotsDecorator(
        size: Size(10.0, 10.0),
        color: Color(0xFFBDBDBD),
        activeSize: Size(22.0, 10.0),
        activeShape: RoundedRectangleBorder(
          borderRadius: BorderRadius.all(Radius.circular(25.0)),
        ),
      ),
      dotsContainerDecorator: const ShapeDecoration(
        color: Colors.black87,
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.all(Radius.circular(8.0)),
        ),
      ),
    );
  }
}

步骤3:修改应用入口逻辑

在main.dart中,启动时先检查引导页状态,决定跳转目标:

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

void main() async {
  WidgetsFlutterBinding.ensureInitialized(); // 确保初始化完成
  SharedPreferences prefs = await SharedPreferences.getInstance();
  bool hasShownIntro = prefs.getBool('hasShownIntro') ?? false;

  runApp(MyApp(hasShownIntro: hasShownIntro));
}

class MyApp extends StatelessWidget {
  final bool hasShownIntro;

  const MyApp({super.key, required this.hasShownIntro});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Mawed',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: hasShownIntro ? const WidgetTree() : const IntroScreen(),
    );
  }
}

关键说明

  • 用SharedPreferences存储布尔值hasShownIntro,默认false代表首次打开应用
  • 引导页完成后将hasShownIntro设为true,后续启动直接进入主页面
  • 使用pushReplacement替换路由,避免用户通过返回键回到引导页

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 02:56:52