欢迎光临
我们一直在努力

鸿蒙应用中使用Flutter三方库intro_slider实现美观引导页的实战指南

📑 目录

  • 📖 概述
  • 📦 引入三方库步骤
  • 🎨 具体页面实现功能
  • ⚠️ 常见错误及解决方案
  • 🔧 进阶功能
  • 📚 完整代码示例

  • 📖 概述

    本教程将详细介绍如何在 Flutter 应用中集成 intro_slider 三方库,实现一个美观的应用引导页,支持左右滑动查看功能介绍,并提供"跳过"按钮快速进入主界面。

    ✨ 功能特性

    #mermaid-svg-S4WorbQXw6K4OBab{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-S4WorbQXw6K4OBab .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-S4WorbQXw6K4OBab .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-S4WorbQXw6K4OBab .error-icon{fill:#552222;}#mermaid-svg-S4WorbQXw6K4OBab .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-S4WorbQXw6K4OBab .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-S4WorbQXw6K4OBab .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-S4WorbQXw6K4OBab .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-S4WorbQXw6K4OBab .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-S4WorbQXw6K4OBab .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-S4WorbQXw6K4OBab .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-S4WorbQXw6K4OBab .marker{fill:#333333;stroke:#333333;}#mermaid-svg-S4WorbQXw6K4OBab .marker.cross{stroke:#333333;}#mermaid-svg-S4WorbQXw6K4OBab svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-S4WorbQXw6K4OBab p{margin:0;}#mermaid-svg-S4WorbQXw6K4OBab .edge{stroke-width:3;}#mermaid-svg-S4WorbQXw6K4OBab .section–1 rect,#mermaid-svg-S4WorbQXw6K4OBab .section–1 path,#mermaid-svg-S4WorbQXw6K4OBab .section–1 circle,#mermaid-svg-S4WorbQXw6K4OBab .section–1 polygon,#mermaid-svg-S4WorbQXw6K4OBab .section–1 path{fill:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-S4WorbQXw6K4OBab .section–1 text{fill:#ffffff;}#mermaid-svg-S4WorbQXw6K4OBab .node-icon–1{font-size:40px;color:#ffffff;}#mermaid-svg-S4WorbQXw6K4OBab .section-edge–1{stroke:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-S4WorbQXw6K4OBab .edge-depth–1{stroke-width:17;}#mermaid-svg-S4WorbQXw6K4OBab .section–1 line{stroke:hsl(60, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-S4WorbQXw6K4OBab .disabled,#mermaid-svg-S4WorbQXw6K4OBab .disabled circle,#mermaid-svg-S4WorbQXw6K4OBab .disabled text{fill:lightgray;}#mermaid-svg-S4WorbQXw6K4OBab .disabled text{fill:#efefef;}#mermaid-svg-S4WorbQXw6K4OBab .section-0 rect,#mermaid-svg-S4WorbQXw6K4OBab .section-0 path,#mermaid-svg-S4WorbQXw6K4OBab .section-0 circle,#mermaid-svg-S4WorbQXw6K4OBab .section-0 polygon,#mermaid-svg-S4WorbQXw6K4OBab .section-0 path{fill:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-S4WorbQXw6K4OBab .section-0 text{fill:black;}#mermaid-svg-S4WorbQXw6K4OBab .node-icon-0{font-size:40px;color:black;}#mermaid-svg-S4WorbQXw6K4OBab .section-edge-0{stroke:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-S4WorbQXw6K4OBab .edge-depth-0{stroke-width:14;}#mermaid-svg-S4WorbQXw6K4OBab .section-0 line{stroke:hsl(240, 100%, 83.5294117647%);stroke-width:3;}#mermaid-svg-S4WorbQXw6K4OBab .disabled,#mermaid-svg-S4WorbQXw6K4OBab .disabled circle,#mermaid-svg-S4WorbQXw6K4OBab .disabled text{fill:lightgray;}#mermaid-svg-S4WorbQXw6K4OBab .disabled text{fill:#efefef;}#mermaid-svg-S4WorbQXw6K4OBab .section-1 rect,#mermaid-svg-S4WorbQXw6K4OBab .section-1 path,#mermaid-svg-S4WorbQXw6K4OBab .section-1 circle,#mermaid-svg-S4WorbQXw6K4OBab .section-1 polygon,#mermaid-svg-S4WorbQXw6K4OBab .section-1 path{fill:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-S4WorbQXw6K4OBab .section-1 text{fill:black;}#mermaid-svg-S4WorbQXw6K4OBab .node-icon-1{font-size:40px;color:black;}#mermaid-svg-S4WorbQXw6K4OBab .section-edge-1{stroke:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-S4WorbQXw6K4OBab .edge-depth-1{stroke-width:11;}#mermaid-svg-S4WorbQXw6K4OBab .section-1 line{stroke:hsl(260, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-S4WorbQXw6K4OBab .disabled,#mermaid-svg-S4WorbQXw6K4OBab .disabled circle,#mermaid-svg-S4WorbQXw6K4OBab .disabled text{fill:lightgray;}#mermaid-svg-S4WorbQXw6K4OBab .disabled text{fill:#efefef;}#mermaid-svg-S4WorbQXw6K4OBab .section-2 rect,#mermaid-svg-S4WorbQXw6K4OBab .section-2 path,#mermaid-svg-S4WorbQXw6K4OBab .section-2 circle,#mermaid-svg-S4WorbQXw6K4OBab .section-2 polygon,#mermaid-svg-S4WorbQXw6K4OBab .section-2 path{fill:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-S4WorbQXw6K4OBab .section-2 text{fill:#ffffff;}#mermaid-svg-S4WorbQXw6K4OBab .node-icon-2{font-size:40px;color:#ffffff;}#mermaid-svg-S4WorbQXw6K4OBab .section-edge-2{stroke:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-S4WorbQXw6K4OBab .edge-depth-2{stroke-width:8;}#mermaid-svg-S4WorbQXw6K4OBab .section-2 line{stroke:hsl(90, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-S4WorbQXw6K4OBab .disabled,#mermaid-svg-S4WorbQXw6K4OBab .disabled circle,#mermaid-svg-S4WorbQXw6K4OBab .disabled text{fill:lightgray;}#mermaid-svg-S4WorbQXw6K4OBab .disabled text{fill:#efefef;}#mermaid-svg-S4WorbQXw6K4OBab .section-3 rect,#mermaid-svg-S4WorbQXw6K4OBab .section-3 path,#mermaid-svg-S4WorbQXw6K4OBab .section-3 circle,#mermaid-svg-S4WorbQXw6K4OBab .section-3 polygon,#mermaid-svg-S4WorbQXw6K4OBab .section-3 path{fill:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-S4WorbQXw6K4OBab .section-3 text{fill:black;}#mermaid-svg-S4WorbQXw6K4OBab .node-icon-3{font-size:40px;color:black;}#mermaid-svg-S4WorbQXw6K4OBab .section-edge-3{stroke:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-S4WorbQXw6K4OBab .edge-depth-3{stroke-width:5;}#mermaid-svg-S4WorbQXw6K4OBab .section-3 line{stroke:hsl(120, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-S4WorbQXw6K4OBab .disabled,#mermaid-svg-S4WorbQXw6K4OBab .disabled circle,#mermaid-svg-S4WorbQXw6K4OBab .disabled text{fill:lightgray;}#mermaid-svg-S4WorbQXw6K4OBab .disabled text{fill:#efefef;}#mermaid-svg-S4WorbQXw6K4OBab .section-4 rect,#mermaid-svg-S4WorbQXw6K4OBab .section-4 path,#mermaid-svg-S4WorbQXw6K4OBab .section-4 circle,#mermaid-svg-S4WorbQXw6K4OBab .section-4 polygon,#mermaid-svg-S4WorbQXw6K4OBab .section-4 path{fill:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-S4WorbQXw6K4OBab .section-4 text{fill:black;}#mermaid-svg-S4WorbQXw6K4OBab .node-icon-4{font-size:40px;color:black;}#mermaid-svg-S4WorbQXw6K4OBab .section-edge-4{stroke:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-S4WorbQXw6K4OBab .edge-depth-4{stroke-width:2;}#mermaid-svg-S4WorbQXw6K4OBab .section-4 line{stroke:hsl(150, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-S4WorbQXw6K4OBab .disabled,#mermaid-svg-S4WorbQXw6K4OBab .disabled circle,#mermaid-svg-S4WorbQXw6K4OBab .disabled text{fill:lightgray;}#mermaid-svg-S4WorbQXw6K4OBab .disabled text{fill:#efefef;}#mermaid-svg-S4WorbQXw6K4OBab .section-5 rect,#mermaid-svg-S4WorbQXw6K4OBab .section-5 path,#mermaid-svg-S4WorbQXw6K4OBab .section-5 circle,#mermaid-svg-S4WorbQXw6K4OBab .section-5 polygon,#mermaid-svg-S4WorbQXw6K4OBab .section-5 path{fill:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-S4WorbQXw6K4OBab .section-5 text{fill:black;}#mermaid-svg-S4WorbQXw6K4OBab .node-icon-5{font-size:40px;color:black;}#mermaid-svg-S4WorbQXw6K4OBab .section-edge-5{stroke:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-S4WorbQXw6K4OBab .edge-depth-5{stroke-width:-1;}#mermaid-svg-S4WorbQXw6K4OBab .section-5 line{stroke:hsl(180, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-S4WorbQXw6K4OBab .disabled,#mermaid-svg-S4WorbQXw6K4OBab .disabled circle,#mermaid-svg-S4WorbQXw6K4OBab .disabled text{fill:lightgray;}#mermaid-svg-S4WorbQXw6K4OBab .disabled text{fill:#efefef;}#mermaid-svg-S4WorbQXw6K4OBab .section-6 rect,#mermaid-svg-S4WorbQXw6K4OBab .section-6 path,#mermaid-svg-S4WorbQXw6K4OBab .section-6 circle,#mermaid-svg-S4WorbQXw6K4OBab .section-6 polygon,#mermaid-svg-S4WorbQXw6K4OBab .section-6 path{fill:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-S4WorbQXw6K4OBab .section-6 text{fill:black;}#mermaid-svg-S4WorbQXw6K4OBab .node-icon-6{font-size:40px;color:black;}#mermaid-svg-S4WorbQXw6K4OBab .section-edge-6{stroke:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-S4WorbQXw6K4OBab .edge-depth-6{stroke-width:-4;}#mermaid-svg-S4WorbQXw6K4OBab .section-6 line{stroke:hsl(210, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-S4WorbQXw6K4OBab .disabled,#mermaid-svg-S4WorbQXw6K4OBab .disabled circle,#mermaid-svg-S4WorbQXw6K4OBab .disabled text{fill:lightgray;}#mermaid-svg-S4WorbQXw6K4OBab .disabled text{fill:#efefef;}#mermaid-svg-S4WorbQXw6K4OBab .section-7 rect,#mermaid-svg-S4WorbQXw6K4OBab .section-7 path,#mermaid-svg-S4WorbQXw6K4OBab .section-7 circle,#mermaid-svg-S4WorbQXw6K4OBab .section-7 polygon,#mermaid-svg-S4WorbQXw6K4OBab .section-7 path{fill:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-S4WorbQXw6K4OBab .section-7 text{fill:black;}#mermaid-svg-S4WorbQXw6K4OBab .node-icon-7{font-size:40px;color:black;}#mermaid-svg-S4WorbQXw6K4OBab .section-edge-7{stroke:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-S4WorbQXw6K4OBab .edge-depth-7{stroke-width:-7;}#mermaid-svg-S4WorbQXw6K4OBab .section-7 line{stroke:hsl(270, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-S4WorbQXw6K4OBab .disabled,#mermaid-svg-S4WorbQXw6K4OBab .disabled circle,#mermaid-svg-S4WorbQXw6K4OBab .disabled text{fill:lightgray;}#mermaid-svg-S4WorbQXw6K4OBab .disabled text{fill:#efefef;}#mermaid-svg-S4WorbQXw6K4OBab .section-8 rect,#mermaid-svg-S4WorbQXw6K4OBab .section-8 path,#mermaid-svg-S4WorbQXw6K4OBab .section-8 circle,#mermaid-svg-S4WorbQXw6K4OBab .section-8 polygon,#mermaid-svg-S4WorbQXw6K4OBab .section-8 path{fill:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-S4WorbQXw6K4OBab .section-8 text{fill:black;}#mermaid-svg-S4WorbQXw6K4OBab .node-icon-8{font-size:40px;color:black;}#mermaid-svg-S4WorbQXw6K4OBab .section-edge-8{stroke:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-S4WorbQXw6K4OBab .edge-depth-8{stroke-width:-10;}#mermaid-svg-S4WorbQXw6K4OBab .section-8 line{stroke:hsl(330, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-S4WorbQXw6K4OBab .disabled,#mermaid-svg-S4WorbQXw6K4OBab .disabled circle,#mermaid-svg-S4WorbQXw6K4OBab .disabled text{fill:lightgray;}#mermaid-svg-S4WorbQXw6K4OBab .disabled text{fill:#efefef;}#mermaid-svg-S4WorbQXw6K4OBab .section-9 rect,#mermaid-svg-S4WorbQXw6K4OBab .section-9 path,#mermaid-svg-S4WorbQXw6K4OBab .section-9 circle,#mermaid-svg-S4WorbQXw6K4OBab .section-9 polygon,#mermaid-svg-S4WorbQXw6K4OBab .section-9 path{fill:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-S4WorbQXw6K4OBab .section-9 text{fill:black;}#mermaid-svg-S4WorbQXw6K4OBab .node-icon-9{font-size:40px;color:black;}#mermaid-svg-S4WorbQXw6K4OBab .section-edge-9{stroke:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-S4WorbQXw6K4OBab .edge-depth-9{stroke-width:-13;}#mermaid-svg-S4WorbQXw6K4OBab .section-9 line{stroke:hsl(0, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-S4WorbQXw6K4OBab .disabled,#mermaid-svg-S4WorbQXw6K4OBab .disabled circle,#mermaid-svg-S4WorbQXw6K4OBab .disabled text{fill:lightgray;}#mermaid-svg-S4WorbQXw6K4OBab .disabled text{fill:#efefef;}#mermaid-svg-S4WorbQXw6K4OBab .section-10 rect,#mermaid-svg-S4WorbQXw6K4OBab .section-10 path,#mermaid-svg-S4WorbQXw6K4OBab .section-10 circle,#mermaid-svg-S4WorbQXw6K4OBab .section-10 polygon,#mermaid-svg-S4WorbQXw6K4OBab .section-10 path{fill:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-S4WorbQXw6K4OBab .section-10 text{fill:black;}#mermaid-svg-S4WorbQXw6K4OBab .node-icon-10{font-size:40px;color:black;}#mermaid-svg-S4WorbQXw6K4OBab .section-edge-10{stroke:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-S4WorbQXw6K4OBab .edge-depth-10{stroke-width:-16;}#mermaid-svg-S4WorbQXw6K4OBab .section-10 line{stroke:hsl(30, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-S4WorbQXw6K4OBab .disabled,#mermaid-svg-S4WorbQXw6K4OBab .disabled circle,#mermaid-svg-S4WorbQXw6K4OBab .disabled text{fill:lightgray;}#mermaid-svg-S4WorbQXw6K4OBab .disabled text{fill:#efefef;}#mermaid-svg-S4WorbQXw6K4OBab .section-root rect,#mermaid-svg-S4WorbQXw6K4OBab .section-root path,#mermaid-svg-S4WorbQXw6K4OBab .section-root circle,#mermaid-svg-S4WorbQXw6K4OBab .section-root polygon{fill:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-S4WorbQXw6K4OBab .section-root text{fill:#ffffff;}#mermaid-svg-S4WorbQXw6K4OBab .section-root span{color:#ffffff;}#mermaid-svg-S4WorbQXw6K4OBab .section-2 span{color:#ffffff;}#mermaid-svg-S4WorbQXw6K4OBab .icon-container{height:100%;display:flex;justify-content:center;align-items:center;}#mermaid-svg-S4WorbQXw6K4OBab .edge{fill:none;}#mermaid-svg-S4WorbQXw6K4OBab .mindmap-node-label{dy:1em;alignment-baseline:middle;text-anchor:middle;dominant-baseline:middle;text-align:center;}#mermaid-svg-S4WorbQXw6K4OBab :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    引导页功能

    页面导航

    左右滑动

    上一步按钮

    下一步按钮

    页面指示器

    自定义配置

    内容自定义

    颜色主题

    背景图片

    按钮样式

    用户体验

    跳过功能

    首次使用检测

    平滑动画

    状态保存

    详细特性:

    • ✅ 支持左右滑动查看多个引导页
    • 🎨 可自定义每个引导页的内容、颜色、图片
    • ⏭️ 提供"跳过"按钮,快速进入主界面
    • 🔄 支持"上一步"和"下一步"导航按钮
    • 📍 页面指示器显示当前位置
    • 💾 首次使用检测,只在首次启动时显示引导页
    • 🎯 平滑的页面过渡动画

    📋 前置要求

    • Flutter SDK 3.9.2 或更高版本
    • Dart SDK 3.9.2 或更高版本
    • 基本的 Flutter 开发知识

    📦 引入三方库步骤

    📝 步骤 1:添加依赖

    在 pubspec.yaml 文件的 dependencies 部分添加以下依赖:

    dependencies:
    flutter:
    sdk: flutter

    # intro_slider: 引导页滑动组件库
    intro_slider: ^3.1.0

    # shared_preferences: 本地数据存储库,用于保存首次使用标志
    shared_preferences: ^2.2.2

    📝 代码解读:

    • intro_slider: ^3.1.0:引导页滑动组件库,提供左右滑动、页面指示器、导航按钮等功能
    • shared_preferences: ^2.2.2:本地键值存储库,用于保存用户是否首次使用应用的标志

    💡 版本说明:

    • ^3.1.0 表示使用 3.1.0 或更高版本(但低于 4.0.0)
    • 使用 ^ 前缀可以自动获取兼容的更新版本

    📁 步骤 2:配置资源文件

    在 pubspec.yaml 的 flutter 部分添加资源路径(如果还没有添加):

    flutter:
    uses-material-design: true

    assets:
    assets/images/
    assets/animations/

    📝 代码解读:

    • assets/images/:存放引导页图片资源
    • 如果图片路径不存在,组件会显示占位符,不会导致应用崩溃

    📂 步骤 3:创建资源文件夹(可选)

    在项目根目录创建以下文件夹结构(如果还没有):

    项目根目录/
    ├── assets/
    │ ├── images/
    │ │ ├── logo.png # 应用 Logo
    │ │ ├── intro_1.png # 引导页第1页图片(可选)
    │ │ ├── intro_2.png # 引导页第2页图片(可选)
    │ │ ├── intro_3.png # 引导页第3页图片(可选)
    │ │ └── intro_4.png # 引导页第4页图片(可选)
    │ └── animations/ # 动画资源(可选)

    💡 提示:

    • 图片资源是可选的,如果不提供图片,组件会正常显示文字内容
    • 建议图片尺寸:1080×1920 或 750×1334(适配不同屏幕)
    • 图片格式:PNG(支持透明)或 JPG

    ⬇️ 步骤 4:安装依赖

    在终端执行以下命令:

    flutter pub get

    📝 命令说明:

    • flutter pub get:下载并安装 pubspec.yaml 中声明的所有依赖包
    • 执行成功后,依赖包会被下载到项目的 .dart_tool 目录

    ✅ 验证安装:

    安装成功后,可以在 pubspec.lock 文件中看到依赖包的版本信息。


    🎨 具体页面实现功能

    1️⃣ 创建首选项管理工具类

    创建文件 lib/utils/preferences_helper.dart:

    // 导入 shared_preferences 三方库,用于本地数据存储
    import 'package:shared_preferences/shared_preferences.dart';
    // 导入 Flutter 基础库,用于 debugPrint
    import 'package:flutter/foundation.dart';

    /// 首选项管理工具类
    /// 用于管理应用的本地存储数据,如首次使用标志等
    class PreferencesHelper {
    /// 首次使用标志的键名
    /// 用于在 SharedPreferences 中存储和读取是否首次使用应用的标志
    static const String _keyIsFirstLaunch = 'is_first_launch';

    /// 检查是否首次启动应用
    ///
    /// 返回值:
    /// – true: 首次启动,需要显示引导页
    /// – false: 非首次启动,直接进入主页面
    static Future<bool> isFirstLaunch() async {
    try {
    // 获取 SharedPreferences 实例
    final prefs = await SharedPreferences.getInstance();

    // 读取首次启动标志
    // getBool: 获取布尔值,如果键不存在则返回 null
    // ?? true: 如果返回 null(即键不存在),则默认为 true(首次启动)
    final isFirst = prefs.getBool(_keyIsFirstLaunch) ?? true;

    return isFirst;
    } catch (e) {
    // 如果读取失败,默认返回 true,确保用户能看到引导页
    debugPrint('读取首次启动标志失败: $e');
    return true;
    }
    }

    /// 设置已非首次启动
    ///
    /// 当用户完成引导页或点击跳过时调用此方法
    /// 将首次启动标志设置为 false,下次启动时不再显示引导页
    static Future<void> setNotFirstLaunch() async {
    try {
    final prefs = await SharedPreferences.getInstance();
    // 设置首次启动标志为 false
    await prefs.setBool(_keyIsFirstLaunch, false);
    } catch (e) {
    debugPrint('保存首次启动标志失败: $e');
    }
    }

    /// 重置首次启动标志(用于测试或重新显示引导页)
    static Future<void> resetFirstLaunch() async {
    try {
    final prefs = await SharedPreferences.getInstance();
    await prefs.setBool(_keyIsFirstLaunch, true);
    } catch (e) {
    debugPrint('重置首次启动标志失败: $e');
    }
    }
    }

    📝 代码解读:

    SharedPreferences 是什么?

    • Flutter 提供的本地键值存储工具
    • 数据会持久化保存在设备上,应用重启后仍然存在
    • 适合存储简单的配置信息,如用户偏好设置、首次使用标志等

    isFirstLaunch() 方法:

    #mermaid-svg-f2nSShXweyRZNBKA{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-f2nSShXweyRZNBKA .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-f2nSShXweyRZNBKA .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-f2nSShXweyRZNBKA .error-icon{fill:#552222;}#mermaid-svg-f2nSShXweyRZNBKA .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-f2nSShXweyRZNBKA .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-f2nSShXweyRZNBKA .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-f2nSShXweyRZNBKA .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-f2nSShXweyRZNBKA .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-f2nSShXweyRZNBKA .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-f2nSShXweyRZNBKA .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-f2nSShXweyRZNBKA .marker{fill:#333333;stroke:#333333;}#mermaid-svg-f2nSShXweyRZNBKA .marker.cross{stroke:#333333;}#mermaid-svg-f2nSShXweyRZNBKA svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-f2nSShXweyRZNBKA p{margin:0;}#mermaid-svg-f2nSShXweyRZNBKA .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-f2nSShXweyRZNBKA .cluster-label text{fill:#333;}#mermaid-svg-f2nSShXweyRZNBKA .cluster-label span{color:#333;}#mermaid-svg-f2nSShXweyRZNBKA .cluster-label span p{background-color:transparent;}#mermaid-svg-f2nSShXweyRZNBKA .label text,#mermaid-svg-f2nSShXweyRZNBKA span{fill:#333;color:#333;}#mermaid-svg-f2nSShXweyRZNBKA .node rect,#mermaid-svg-f2nSShXweyRZNBKA .node circle,#mermaid-svg-f2nSShXweyRZNBKA .node ellipse,#mermaid-svg-f2nSShXweyRZNBKA .node polygon,#mermaid-svg-f2nSShXweyRZNBKA .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-f2nSShXweyRZNBKA .rough-node .label text,#mermaid-svg-f2nSShXweyRZNBKA .node .label text,#mermaid-svg-f2nSShXweyRZNBKA .image-shape .label,#mermaid-svg-f2nSShXweyRZNBKA .icon-shape .label{text-anchor:middle;}#mermaid-svg-f2nSShXweyRZNBKA .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-f2nSShXweyRZNBKA .rough-node .label,#mermaid-svg-f2nSShXweyRZNBKA .node .label,#mermaid-svg-f2nSShXweyRZNBKA .image-shape .label,#mermaid-svg-f2nSShXweyRZNBKA .icon-shape .label{text-align:center;}#mermaid-svg-f2nSShXweyRZNBKA .node.clickable{cursor:pointer;}#mermaid-svg-f2nSShXweyRZNBKA .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-f2nSShXweyRZNBKA .arrowheadPath{fill:#333333;}#mermaid-svg-f2nSShXweyRZNBKA .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-f2nSShXweyRZNBKA .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-f2nSShXweyRZNBKA .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-f2nSShXweyRZNBKA .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-f2nSShXweyRZNBKA .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-f2nSShXweyRZNBKA .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-f2nSShXweyRZNBKA .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-f2nSShXweyRZNBKA .cluster text{fill:#333;}#mermaid-svg-f2nSShXweyRZNBKA .cluster span{color:#333;}#mermaid-svg-f2nSShXweyRZNBKA div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-f2nSShXweyRZNBKA .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-f2nSShXweyRZNBKA rect.text{fill:none;stroke-width:0;}#mermaid-svg-f2nSShXweyRZNBKA .icon-shape,#mermaid-svg-f2nSShXweyRZNBKA .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-f2nSShXweyRZNBKA .icon-shape p,#mermaid-svg-f2nSShXweyRZNBKA .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-f2nSShXweyRZNBKA .icon-shape rect,#mermaid-svg-f2nSShXweyRZNBKA .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-f2nSShXweyRZNBKA .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-f2nSShXweyRZNBKA .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-f2nSShXweyRZNBKA :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    存在

    不存在 null

    true

    false

    调用 isFirstLaunch

    获取 SharedPreferences 实例

    读取键值getBool _keyIsFirstLaunch

    键是否存在?

    返回存储的布尔值

    使用空值合并运算符?? true

    返回 true首次启动

    返回值

    显示引导页

    跳过引导页

    详细说明:

    • getBool(_keyIsFirstLaunch):读取布尔值
      • 如果键存在,返回对应的布尔值
      • 如果键不存在,返回 null
    • ?? true:空值合并运算符
      • 如果左侧为 null,返回右侧的值(true)
      • 首次安装应用时,键不存在,返回 true(需要显示引导页)

    setNotFirstLaunch() 方法:

    • setBool(_keyIsFirstLaunch, false):保存布尔值到本地存储
    • 调用此方法后,下次启动应用时 isFirstLaunch() 会返回 false

    💡 实际应用场景:

    #mermaid-svg-5FKoUyVOb4N26Dwt{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-5FKoUyVOb4N26Dwt .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-5FKoUyVOb4N26Dwt .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-5FKoUyVOb4N26Dwt .error-icon{fill:#552222;}#mermaid-svg-5FKoUyVOb4N26Dwt .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-5FKoUyVOb4N26Dwt .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-5FKoUyVOb4N26Dwt .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-5FKoUyVOb4N26Dwt .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-5FKoUyVOb4N26Dwt .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-5FKoUyVOb4N26Dwt .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-5FKoUyVOb4N26Dwt .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-5FKoUyVOb4N26Dwt .marker{fill:#333333;stroke:#333333;}#mermaid-svg-5FKoUyVOb4N26Dwt .marker.cross{stroke:#333333;}#mermaid-svg-5FKoUyVOb4N26Dwt svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-5FKoUyVOb4N26Dwt p{margin:0;}#mermaid-svg-5FKoUyVOb4N26Dwt .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-5FKoUyVOb4N26Dwt .cluster-label text{fill:#333;}#mermaid-svg-5FKoUyVOb4N26Dwt .cluster-label span{color:#333;}#mermaid-svg-5FKoUyVOb4N26Dwt .cluster-label span p{background-color:transparent;}#mermaid-svg-5FKoUyVOb4N26Dwt .label text,#mermaid-svg-5FKoUyVOb4N26Dwt span{fill:#333;color:#333;}#mermaid-svg-5FKoUyVOb4N26Dwt .node rect,#mermaid-svg-5FKoUyVOb4N26Dwt .node circle,#mermaid-svg-5FKoUyVOb4N26Dwt .node ellipse,#mermaid-svg-5FKoUyVOb4N26Dwt .node polygon,#mermaid-svg-5FKoUyVOb4N26Dwt .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-5FKoUyVOb4N26Dwt .rough-node .label text,#mermaid-svg-5FKoUyVOb4N26Dwt .node .label text,#mermaid-svg-5FKoUyVOb4N26Dwt .image-shape .label,#mermaid-svg-5FKoUyVOb4N26Dwt .icon-shape .label{text-anchor:middle;}#mermaid-svg-5FKoUyVOb4N26Dwt .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-5FKoUyVOb4N26Dwt .rough-node .label,#mermaid-svg-5FKoUyVOb4N26Dwt .node .label,#mermaid-svg-5FKoUyVOb4N26Dwt .image-shape .label,#mermaid-svg-5FKoUyVOb4N26Dwt .icon-shape .label{text-align:center;}#mermaid-svg-5FKoUyVOb4N26Dwt .node.clickable{cursor:pointer;}#mermaid-svg-5FKoUyVOb4N26Dwt .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-5FKoUyVOb4N26Dwt .arrowheadPath{fill:#333333;}#mermaid-svg-5FKoUyVOb4N26Dwt .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-5FKoUyVOb4N26Dwt .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-5FKoUyVOb4N26Dwt .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5FKoUyVOb4N26Dwt .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-5FKoUyVOb4N26Dwt .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5FKoUyVOb4N26Dwt .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-5FKoUyVOb4N26Dwt .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-5FKoUyVOb4N26Dwt .cluster text{fill:#333;}#mermaid-svg-5FKoUyVOb4N26Dwt .cluster span{color:#333;}#mermaid-svg-5FKoUyVOb4N26Dwt div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-5FKoUyVOb4N26Dwt .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-5FKoUyVOb4N26Dwt rect.text{fill:none;stroke-width:0;}#mermaid-svg-5FKoUyVOb4N26Dwt .icon-shape,#mermaid-svg-5FKoUyVOb4N26Dwt .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5FKoUyVOb4N26Dwt .icon-shape p,#mermaid-svg-5FKoUyVOb4N26Dwt .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-5FKoUyVOb4N26Dwt .icon-shape rect,#mermaid-svg-5FKoUyVOb4N26Dwt .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5FKoUyVOb4N26Dwt .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-5FKoUyVOb4N26Dwt .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-5FKoUyVOb4N26Dwt :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    true

    false

    应用启动

    调用 isFirstLaunch

    返回结果

    显示引导页

    显示主页面

    用户完成引导

    调用 setNotFirstLaunch

    保存标志为 false

    代码示例:

    // 检查是否首次启动
    bool isFirst = await PreferencesHelper.isFirstLaunch();
    if (isFirst) {
    // 显示引导页
    Navigator.push(context, MaterialPageRoute(builder: (_) => IntroScreen()));
    } else {
    // 直接进入主页面
    Navigator.push(context, MaterialPageRoute(builder: (_) => HomePage()));
    }

    // 用户完成引导后,标记已非首次启动
    await PreferencesHelper.setNotFirstLaunch();

    2️⃣ 创建引导页组件

    创建文件 lib/screens/intro_screen.dart:

    // 导入 Flutter Material 设计库,提供 UI 组件和主题
    import 'package:flutter/material.dart';
    // 导入 intro_slider 三方库,用于实现引导页滑动功能
    import 'package:intro_slider/intro_slider.dart';
    // 导入 intro_slider 的内容配置类
    import 'package:intro_slider/slide_object.dart';
    // 导入 intro_slider 的配置类
    import 'package:intro_slider/scrollbar_behavior_enum.dart';

    /// 引导页组件
    /// 用于首次启动应用时展示功能介绍
    /// 支持左右滑动查看多个引导页,提供"跳过"按钮快速进入主界面
    class IntroScreen extends StatefulWidget {
    /// 引导页完成后的回调函数
    /// 当用户完成引导或点击跳过时调用,用于跳转到主页面
    final VoidCallback onDone;

    /// 构造函数
    const IntroScreen({
    super.key,
    required this.onDone,
    });


    State<IntroScreen> createState() => _IntroScreenState();
    }

    /// 引导页状态类
    /// 管理引导页的配置和交互逻辑
    class _IntroScreenState extends State<IntroScreen> {
    /// 引导页列表
    /// 每个 Slide 对象代表一个引导页
    List<Slide> slides = [];

    /// Widget 初始化方法

    void initState() {
    super.initState();
    // 初始化引导页内容
    _initSlides();
    }

    /// 初始化引导页内容
    /// 创建多个引导页,展示天气预报应用的主要功能
    void _initSlides() {
    slides = [
    // 第一页:欢迎页
    Slide(
    title: "欢迎使用天气预报",
    description: "实时掌握天气变化,为您的生活出行提供精准的天气信息",
    pathImage: "assets/images/intro_1.png",
    backgroundColor: const Color(0xFF64B5F6), // 浅蓝色
    titleColor: Colors.white,
    descriptionColor: Colors.white,
    directionColorBegin: Alignment.topLeft,
    directionColorEnd: Alignment.bottomRight,
    ),
    // 第二页:实时天气功能
    Slide(
    title: "实时天气查询",
    description: "获取当前位置的实时天气状况,包括温度、湿度、风速等详细信息",
    pathImage: "assets/images/intro_2.png",
    backgroundColor: const Color(0xFF42A5F5), // 蓝色
    titleColor: Colors.white,
    descriptionColor: Colors.white,
    directionColorBegin: Alignment.topLeft,
    directionColorEnd: Alignment.bottomRight,
    ),
    // 第三页:多城市管理
    Slide(
    title: "多城市管理",
    description: "添加多个城市,快速切换查看不同地区的天气情况,方便出行规划",
    pathImage: "assets/images/intro_3.png",
    backgroundColor: const Color(0xFF1E88E5), // 深蓝色
    titleColor: Colors.white,
    descriptionColor: Colors.white,
    directionColorBegin: Alignment.topLeft,
    directionColorEnd: Alignment.bottomRight,
    ),
    // 第四页:天气预报
    Slide(
    title: "未来天气预报",
    description: "查看未来7天的天气预报,提前了解天气变化趋势,做好出行准备",
    pathImage: "assets/images/intro_4.png",
    backgroundColor: const Color(0xFF1565C0), // 更深蓝色
    titleColor: Colors.white,
    descriptionColor: Colors.white,
    directionColorBegin: Alignment.topLeft,
    directionColorEnd: Alignment.bottomRight,
    ),
    ];
    }

    /// 构建 Widget 树

    Widget build(BuildContext context) {
    return IntroSlider(
    // slides: 引导页列表
    slides: slides,
    // onDonePress: 完成按钮点击回调
    onDonePress: () {
    widget.onDone();
    },
    // onSkipPress: 跳过按钮点击回调
    onSkipPress: () {
    widget.onDone();
    },
    // showSkipBtn: 是否显示跳过按钮
    showSkipBtn: true,
    // 按钮样式配置
    skipButtonStyle: ButtonStyle(
    backgroundColor: WidgetStateProperty.all(Colors.white.withOpacity(0.3)),
    foregroundColor: WidgetStateProperty.all(Colors.white),
    padding: WidgetStateProperty.all(
    const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
    ),
    shape: WidgetStateProperty.all(
    RoundedRectangleBorder(
    borderRadius: BorderRadius.circular(20),
    ),
    ),
    ),
    // 页面指示器配置
    indicatorConfig: IndicatorConfig(
    sizeIndicator: 10,
    colorIndicator: Colors.white,
    colorActiveIndicator: Colors.white.withOpacity(0.5),
    typeIndicatorAnimation: TypeIndicatorAnimation.sizeTransition,
    ),
    // 其他配置
    scrollbarBehavior: ScrollbarBehavior.HIDE,
    hideStatusBar: false,
    backgroundColorAllSlides: Colors.blue,
    isShowPrevBtn: true,
    isShowNextBtn: true,
    isShowDoneBtn: true,
    );
    }
    }

    image-20260124214039345

    📝 代码解读:

    Slide 对象属性:

    • title:页面标题文字
    • description:页面描述文字
    • pathImage:页面背景图片路径(可选)
    • backgroundColor:页面背景颜色
    • titleColor:标题文字颜色
    • descriptionColor:描述文字颜色
    • directionColorBegin/End:渐变方向(如果使用渐变背景)

    IntroSlider 主要配置:

    #mermaid-svg-DmjzsRB583BLtrGL{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-DmjzsRB583BLtrGL .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-DmjzsRB583BLtrGL .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-DmjzsRB583BLtrGL .error-icon{fill:#552222;}#mermaid-svg-DmjzsRB583BLtrGL .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-DmjzsRB583BLtrGL .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-DmjzsRB583BLtrGL .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-DmjzsRB583BLtrGL .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-DmjzsRB583BLtrGL .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-DmjzsRB583BLtrGL .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-DmjzsRB583BLtrGL .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-DmjzsRB583BLtrGL .marker{fill:#333333;stroke:#333333;}#mermaid-svg-DmjzsRB583BLtrGL .marker.cross{stroke:#333333;}#mermaid-svg-DmjzsRB583BLtrGL svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-DmjzsRB583BLtrGL p{margin:0;}#mermaid-svg-DmjzsRB583BLtrGL .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-DmjzsRB583BLtrGL .cluster-label text{fill:#333;}#mermaid-svg-DmjzsRB583BLtrGL .cluster-label span{color:#333;}#mermaid-svg-DmjzsRB583BLtrGL .cluster-label span p{background-color:transparent;}#mermaid-svg-DmjzsRB583BLtrGL .label text,#mermaid-svg-DmjzsRB583BLtrGL span{fill:#333;color:#333;}#mermaid-svg-DmjzsRB583BLtrGL .node rect,#mermaid-svg-DmjzsRB583BLtrGL .node circle,#mermaid-svg-DmjzsRB583BLtrGL .node ellipse,#mermaid-svg-DmjzsRB583BLtrGL .node polygon,#mermaid-svg-DmjzsRB583BLtrGL .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-DmjzsRB583BLtrGL .rough-node .label text,#mermaid-svg-DmjzsRB583BLtrGL .node .label text,#mermaid-svg-DmjzsRB583BLtrGL .image-shape .label,#mermaid-svg-DmjzsRB583BLtrGL .icon-shape .label{text-anchor:middle;}#mermaid-svg-DmjzsRB583BLtrGL .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-DmjzsRB583BLtrGL .rough-node .label,#mermaid-svg-DmjzsRB583BLtrGL .node .label,#mermaid-svg-DmjzsRB583BLtrGL .image-shape .label,#mermaid-svg-DmjzsRB583BLtrGL .icon-shape .label{text-align:center;}#mermaid-svg-DmjzsRB583BLtrGL .node.clickable{cursor:pointer;}#mermaid-svg-DmjzsRB583BLtrGL .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-DmjzsRB583BLtrGL .arrowheadPath{fill:#333333;}#mermaid-svg-DmjzsRB583BLtrGL .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-DmjzsRB583BLtrGL .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-DmjzsRB583BLtrGL .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-DmjzsRB583BLtrGL .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-DmjzsRB583BLtrGL .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-DmjzsRB583BLtrGL .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-DmjzsRB583BLtrGL .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-DmjzsRB583BLtrGL .cluster text{fill:#333;}#mermaid-svg-DmjzsRB583BLtrGL .cluster span{color:#333;}#mermaid-svg-DmjzsRB583BLtrGL div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-DmjzsRB583BLtrGL .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-DmjzsRB583BLtrGL rect.text{fill:none;stroke-width:0;}#mermaid-svg-DmjzsRB583BLtrGL .icon-shape,#mermaid-svg-DmjzsRB583BLtrGL .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-DmjzsRB583BLtrGL .icon-shape p,#mermaid-svg-DmjzsRB583BLtrGL .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-DmjzsRB583BLtrGL .icon-shape rect,#mermaid-svg-DmjzsRB583BLtrGL .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-DmjzsRB583BLtrGL .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-DmjzsRB583BLtrGL .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-DmjzsRB583BLtrGL :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    IntroSlider 组件

    slides: 引导页列表

    onDonePress: 完成回调

    onSkipPress: 跳过回调

    showSkipBtn: 显示跳过按钮

    indicatorConfig: 指示器配置

    按钮显示控制

    Slide 1

    Slide 2

    Slide 3

    Slide 4

    sizeIndicator: 大小

    colorIndicator: 颜色

    typeIndicatorAnimation: 动画类型

    isShowPrevBtn: 上一步

    isShowNextBtn: 下一步

    isShowDoneBtn: 完成

    配置说明:

    • slides:引导页列表
    • onDonePress:完成按钮回调(最后一页的"开始使用"按钮)
    • onSkipPress:跳过按钮回调(右上角的"跳过"按钮)
    • showSkipBtn:是否显示跳过按钮
    • indicatorConfig:页面指示器配置(小圆点)
    • isShowPrevBtn:是否显示"上一步"按钮
    • isShowNextBtn:是否显示"下一步"按钮
    • isShowDoneBtn:是否显示"完成"按钮(最后一页)

    引导页交互流程:

    #mermaid-svg-lwJirc3DTNwOUntQ{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-lwJirc3DTNwOUntQ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-lwJirc3DTNwOUntQ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-lwJirc3DTNwOUntQ .error-icon{fill:#552222;}#mermaid-svg-lwJirc3DTNwOUntQ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-lwJirc3DTNwOUntQ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-lwJirc3DTNwOUntQ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-lwJirc3DTNwOUntQ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-lwJirc3DTNwOUntQ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-lwJirc3DTNwOUntQ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-lwJirc3DTNwOUntQ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-lwJirc3DTNwOUntQ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-lwJirc3DTNwOUntQ .marker.cross{stroke:#333333;}#mermaid-svg-lwJirc3DTNwOUntQ svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-lwJirc3DTNwOUntQ p{margin:0;}#mermaid-svg-lwJirc3DTNwOUntQ .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-lwJirc3DTNwOUntQ .cluster-label text{fill:#333;}#mermaid-svg-lwJirc3DTNwOUntQ .cluster-label span{color:#333;}#mermaid-svg-lwJirc3DTNwOUntQ .cluster-label span p{background-color:transparent;}#mermaid-svg-lwJirc3DTNwOUntQ .label text,#mermaid-svg-lwJirc3DTNwOUntQ span{fill:#333;color:#333;}#mermaid-svg-lwJirc3DTNwOUntQ .node rect,#mermaid-svg-lwJirc3DTNwOUntQ .node circle,#mermaid-svg-lwJirc3DTNwOUntQ .node ellipse,#mermaid-svg-lwJirc3DTNwOUntQ .node polygon,#mermaid-svg-lwJirc3DTNwOUntQ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-lwJirc3DTNwOUntQ .rough-node .label text,#mermaid-svg-lwJirc3DTNwOUntQ .node .label text,#mermaid-svg-lwJirc3DTNwOUntQ .image-shape .label,#mermaid-svg-lwJirc3DTNwOUntQ .icon-shape .label{text-anchor:middle;}#mermaid-svg-lwJirc3DTNwOUntQ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-lwJirc3DTNwOUntQ .rough-node .label,#mermaid-svg-lwJirc3DTNwOUntQ .node .label,#mermaid-svg-lwJirc3DTNwOUntQ .image-shape .label,#mermaid-svg-lwJirc3DTNwOUntQ .icon-shape .label{text-align:center;}#mermaid-svg-lwJirc3DTNwOUntQ .node.clickable{cursor:pointer;}#mermaid-svg-lwJirc3DTNwOUntQ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-lwJirc3DTNwOUntQ .arrowheadPath{fill:#333333;}#mermaid-svg-lwJirc3DTNwOUntQ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-lwJirc3DTNwOUntQ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-lwJirc3DTNwOUntQ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-lwJirc3DTNwOUntQ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-lwJirc3DTNwOUntQ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-lwJirc3DTNwOUntQ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-lwJirc3DTNwOUntQ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-lwJirc3DTNwOUntQ .cluster text{fill:#333;}#mermaid-svg-lwJirc3DTNwOUntQ .cluster span{color:#333;}#mermaid-svg-lwJirc3DTNwOUntQ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-lwJirc3DTNwOUntQ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-lwJirc3DTNwOUntQ rect.text{fill:none;stroke-width:0;}#mermaid-svg-lwJirc3DTNwOUntQ .icon-shape,#mermaid-svg-lwJirc3DTNwOUntQ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-lwJirc3DTNwOUntQ .icon-shape p,#mermaid-svg-lwJirc3DTNwOUntQ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-lwJirc3DTNwOUntQ .icon-shape rect,#mermaid-svg-lwJirc3DTNwOUntQ .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-lwJirc3DTNwOUntQ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-lwJirc3DTNwOUntQ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-lwJirc3DTNwOUntQ :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    点击跳过

    滑动到最后一页

    点击上一步

    点击下一步

    用户进入引导页

    用户操作

    触发 onSkipPress

    显示完成按钮

    返回上一页

    进入下一页

    点击开始使用

    触发 onDonePress

    调用回调函数

    保存首次使用标志

    跳转到主页面

    💡 自定义按钮样式:

    // 自定义跳过按钮
    renderSkipBtn: () {
    return Container(
    padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
    decoration: BoxDecoration(
    color: Colors.white.withOpacity(0.3),
    borderRadius: BorderRadius.circular(20),
    ),
    child: const Text(
    "跳过",
    style: TextStyle(
    color: Colors.white,
    fontSize: 16,
    fontWeight: FontWeight.w600,
    ),
    ),
    );
    },

    3️⃣ 修改主应用入口

    修改文件 lib/main.dart:

    // 导入必要的库
    import 'package:flutter/material.dart';
    import 'package:flutter_splash_screen/flutter_splash_screen.dart';
    import 'screens/splash_screen.dart';
    import 'screens/intro_screen.dart';
    import 'utils/preferences_helper.dart';

    void main() {
    runApp(const MyApp());
    }

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


    Widget build(BuildContext context) {
    return MaterialApp(
    title: '天气预报',
    theme: ThemeData(
    colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
    useMaterial3: true,
    ),
    home: const SplashWrapper(),
    debugShowCheckedModeBanner: false,
    );
    }
    }

    /// 启动页包装器 Widget
    /// 负责显示启动页、引导页和主页面的跳转逻辑
    class SplashWrapper extends StatefulWidget {
    const SplashWrapper({super.key});


    State<SplashWrapper> createState() => _SplashWrapperState();
    }

    /// 启动页包装器的状态类
    class _SplashWrapperState extends State<SplashWrapper> {
    /// 页面显示状态枚举
    enum PageState {
    splash, // 显示启动页
    intro, // 显示引导页(首次使用)
    home, // 显示主页面
    }

    PageState _pageState = PageState.splash;


    void initState() {
    super.initState();
    _hideNativeSplash();
    }

    /// 隐藏原生启动屏
    Future<void> _hideNativeSplash() async {
    await Future.delayed(const Duration(milliseconds: 100));
    if (mounted) {
    await FlutterSplashScreen.hide();
    }
    }

    /// 启动页完成回调函数
    /// 检查是否首次使用,决定显示引导页还是主页面
    void _onSplashComplete() async {
    if (!mounted) return;

    // 检查是否首次启动应用
    final isFirst = await PreferencesHelper.isFirstLaunch();

    if (mounted) {
    setState(() {
    if (isFirst) {
    // 首次启动,显示引导页
    _pageState = PageState.intro;
    } else {
    // 非首次启动,直接显示主页面
    _pageState = PageState.home;
    }
    });
    }
    }

    /// 引导页完成回调函数
    /// 标记已非首次启动,并跳转到主页面
    void _onIntroComplete() async {
    // 标记已非首次启动
    await PreferencesHelper.setNotFirstLaunch();

    if (mounted) {
    setState(() {
    _pageState = PageState.home;
    });
    }
    }

    /// 构建 Widget 树

    Widget build(BuildContext context) {
    switch (_pageState) {
    case PageState.splash:
    // 显示启动页
    return SplashScreen(
    duration: 2000,
    enableAnimation: true,
    logoPath: 'assets/images/logo.png',
    onComplete: _onSplashComplete,
    );

    case PageState.intro:
    // 显示引导页(首次使用)
    return IntroScreen(
    onDone: _onIntroComplete,
    );

    case PageState.home:
    // 显示主页面
    return const MyHomePage(title: '天气预报');
    }
    }
    }

    📝 代码解读:

    页面流程:

    主页面

    引导页

    PreferencesHelper

    启动页

    应用启动

    主页面

    引导页

    PreferencesHelper

    启动页

    应用启动

    #mermaid-svg-O1cPZOp9Fp2JKbA1{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-O1cPZOp9Fp2JKbA1 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-O1cPZOp9Fp2JKbA1 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-O1cPZOp9Fp2JKbA1 .error-icon{fill:#552222;}#mermaid-svg-O1cPZOp9Fp2JKbA1 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-O1cPZOp9Fp2JKbA1 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-O1cPZOp9Fp2JKbA1 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-O1cPZOp9Fp2JKbA1 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-O1cPZOp9Fp2JKbA1 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-O1cPZOp9Fp2JKbA1 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-O1cPZOp9Fp2JKbA1 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-O1cPZOp9Fp2JKbA1 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-O1cPZOp9Fp2JKbA1 .marker.cross{stroke:#333333;}#mermaid-svg-O1cPZOp9Fp2JKbA1 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-O1cPZOp9Fp2JKbA1 p{margin:0;}#mermaid-svg-O1cPZOp9Fp2JKbA1 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-O1cPZOp9Fp2JKbA1 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-O1cPZOp9Fp2JKbA1 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-O1cPZOp9Fp2JKbA1 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-O1cPZOp9Fp2JKbA1 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-O1cPZOp9Fp2JKbA1 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-O1cPZOp9Fp2JKbA1 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-O1cPZOp9Fp2JKbA1 .sequenceNumber{fill:white;}#mermaid-svg-O1cPZOp9Fp2JKbA1 #sequencenumber{fill:#333;}#mermaid-svg-O1cPZOp9Fp2JKbA1 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-O1cPZOp9Fp2JKbA1 .messageText{fill:#333;stroke:none;}#mermaid-svg-O1cPZOp9Fp2JKbA1 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-O1cPZOp9Fp2JKbA1 .labelText,#mermaid-svg-O1cPZOp9Fp2JKbA1 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-O1cPZOp9Fp2JKbA1 .loopText,#mermaid-svg-O1cPZOp9Fp2JKbA1 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-O1cPZOp9Fp2JKbA1 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-O1cPZOp9Fp2JKbA1 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-O1cPZOp9Fp2JKbA1 .noteText,#mermaid-svg-O1cPZOp9Fp2JKbA1 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-O1cPZOp9Fp2JKbA1 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-O1cPZOp9Fp2JKbA1 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-O1cPZOp9Fp2JKbA1 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-O1cPZOp9Fp2JKbA1 .actorPopupMenu{position:absolute;}#mermaid-svg-O1cPZOp9Fp2JKbA1 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-O1cPZOp9Fp2JKbA1 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-O1cPZOp9Fp2JKbA1 .actor-man circle,#mermaid-svg-O1cPZOp9Fp2JKbA1 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-O1cPZOp9Fp2JKbA1 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    alt

    [首次使用 (true)]

    [非首次使用 (false)]

    显示启动页

    持续2秒

    检查是否首次使用

    isFirstLaunch()

    返回结果

    显示引导页

    用户滑动/操作

    保存标志

    setNotFirstLaunch()

    跳转到主页面

    直接显示主页面

    详细步骤:

  • 启动页(Splash):应用启动时显示,持续 2 秒
  • 检查首次使用:启动页完成后,调用 PreferencesHelper.isFirstLaunch() 检查
  • 引导页(Intro):如果是首次使用,显示引导页
  • 主页面(Home):如果不是首次使用,或引导页完成后,显示主页面
  • 状态管理:

    #mermaid-svg-RZGGdoemE7iZBpGh{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-RZGGdoemE7iZBpGh .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-RZGGdoemE7iZBpGh .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-RZGGdoemE7iZBpGh .error-icon{fill:#552222;}#mermaid-svg-RZGGdoemE7iZBpGh .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-RZGGdoemE7iZBpGh .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-RZGGdoemE7iZBpGh .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-RZGGdoemE7iZBpGh .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-RZGGdoemE7iZBpGh .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-RZGGdoemE7iZBpGh .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-RZGGdoemE7iZBpGh .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-RZGGdoemE7iZBpGh .marker{fill:#333333;stroke:#333333;}#mermaid-svg-RZGGdoemE7iZBpGh .marker.cross{stroke:#333333;}#mermaid-svg-RZGGdoemE7iZBpGh svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-RZGGdoemE7iZBpGh p{margin:0;}#mermaid-svg-RZGGdoemE7iZBpGh defs #statediagram-barbEnd{fill:#333333;stroke:#333333;}#mermaid-svg-RZGGdoemE7iZBpGh g.stateGroup text{fill:#9370DB;stroke:none;font-size:10px;}#mermaid-svg-RZGGdoemE7iZBpGh g.stateGroup text{fill:#333;stroke:none;font-size:10px;}#mermaid-svg-RZGGdoemE7iZBpGh g.stateGroup .state-title{font-weight:bolder;fill:#131300;}#mermaid-svg-RZGGdoemE7iZBpGh g.stateGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-RZGGdoemE7iZBpGh g.stateGroup line{stroke:#333333;stroke-width:1;}#mermaid-svg-RZGGdoemE7iZBpGh .transition{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-RZGGdoemE7iZBpGh .stateGroup .composit{fill:white;border-bottom:1px;}#mermaid-svg-RZGGdoemE7iZBpGh .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px;}#mermaid-svg-RZGGdoemE7iZBpGh .state-note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-RZGGdoemE7iZBpGh .state-note text{fill:black;stroke:none;font-size:10px;}#mermaid-svg-RZGGdoemE7iZBpGh .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-RZGGdoemE7iZBpGh .edgeLabel .label rect{fill:#ECECFF;opacity:0.5;}#mermaid-svg-RZGGdoemE7iZBpGh .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RZGGdoemE7iZBpGh .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-RZGGdoemE7iZBpGh .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RZGGdoemE7iZBpGh .edgeLabel .label text{fill:#333;}#mermaid-svg-RZGGdoemE7iZBpGh .label div .edgeLabel{color:#333;}#mermaid-svg-RZGGdoemE7iZBpGh .stateLabel text{fill:#131300;font-size:10px;font-weight:bold;}#mermaid-svg-RZGGdoemE7iZBpGh .node circle.state-start{fill:#333333;stroke:#333333;}#mermaid-svg-RZGGdoemE7iZBpGh .node .fork-join{fill:#333333;stroke:#333333;}#mermaid-svg-RZGGdoemE7iZBpGh .node circle.state-end{fill:#9370DB;stroke:white;stroke-width:1.5;}#mermaid-svg-RZGGdoemE7iZBpGh .end-state-inner{fill:white;stroke-width:1.5;}#mermaid-svg-RZGGdoemE7iZBpGh .node rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-RZGGdoemE7iZBpGh .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-RZGGdoemE7iZBpGh #statediagram-barbEnd{fill:#333333;}#mermaid-svg-RZGGdoemE7iZBpGh .statediagram-cluster rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-RZGGdoemE7iZBpGh .cluster-label,#mermaid-svg-RZGGdoemE7iZBpGh .nodeLabel{color:#131300;}#mermaid-svg-RZGGdoemE7iZBpGh .statediagram-cluster rect.outer{rx:5px;ry:5px;}#mermaid-svg-RZGGdoemE7iZBpGh .statediagram-state .divider{stroke:#9370DB;}#mermaid-svg-RZGGdoemE7iZBpGh .statediagram-state .title-state{rx:5px;ry:5px;}#mermaid-svg-RZGGdoemE7iZBpGh .statediagram-cluster.statediagram-cluster .inner{fill:white;}#mermaid-svg-RZGGdoemE7iZBpGh .statediagram-cluster.statediagram-cluster-alt .inner{fill:#f0f0f0;}#mermaid-svg-RZGGdoemE7iZBpGh .statediagram-cluster .inner{rx:0;ry:0;}#mermaid-svg-RZGGdoemE7iZBpGh .statediagram-state rect.basic{rx:5px;ry:5px;}#mermaid-svg-RZGGdoemE7iZBpGh .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#f0f0f0;}#mermaid-svg-RZGGdoemE7iZBpGh .note-edge{stroke-dasharray:5;}#mermaid-svg-RZGGdoemE7iZBpGh .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-RZGGdoemE7iZBpGh .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-RZGGdoemE7iZBpGh .statediagram-note text{fill:black;}#mermaid-svg-RZGGdoemE7iZBpGh .statediagram-note .nodeLabel{color:black;}#mermaid-svg-RZGGdoemE7iZBpGh .statediagram .edgeLabel{color:red;}#mermaid-svg-RZGGdoemE7iZBpGh #dependencyStart,#mermaid-svg-RZGGdoemE7iZBpGh #dependencyEnd{fill:#333333;stroke:#333333;stroke-width:1;}#mermaid-svg-RZGGdoemE7iZBpGh .statediagramTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-RZGGdoemE7iZBpGh :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    应用启动

    首次使用isFirstLaunch = true

    非首次使用isFirstLaunch = false

    完成引导setNotFirstLaunch

    应用退出

    Splash

    Intro

    Home

    显示启动页持续2秒

    显示引导页用户可滑动查看可点击跳过

    显示主页面正常使用应用

    状态切换机制:

    • 使用 PageState 枚举管理页面状态
    • 通过 setState() 切换页面状态
    • 使用 mounted 检查确保 Widget 仍然存在

    💡 流程说明:

    #mermaid-svg-usTiZ44TfMKZIp78{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-usTiZ44TfMKZIp78 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-usTiZ44TfMKZIp78 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-usTiZ44TfMKZIp78 .error-icon{fill:#552222;}#mermaid-svg-usTiZ44TfMKZIp78 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-usTiZ44TfMKZIp78 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-usTiZ44TfMKZIp78 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-usTiZ44TfMKZIp78 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-usTiZ44TfMKZIp78 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-usTiZ44TfMKZIp78 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-usTiZ44TfMKZIp78 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-usTiZ44TfMKZIp78 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-usTiZ44TfMKZIp78 .marker.cross{stroke:#333333;}#mermaid-svg-usTiZ44TfMKZIp78 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-usTiZ44TfMKZIp78 p{margin:0;}#mermaid-svg-usTiZ44TfMKZIp78 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-usTiZ44TfMKZIp78 .cluster-label text{fill:#333;}#mermaid-svg-usTiZ44TfMKZIp78 .cluster-label span{color:#333;}#mermaid-svg-usTiZ44TfMKZIp78 .cluster-label span p{background-color:transparent;}#mermaid-svg-usTiZ44TfMKZIp78 .label text,#mermaid-svg-usTiZ44TfMKZIp78 span{fill:#333;color:#333;}#mermaid-svg-usTiZ44TfMKZIp78 .node rect,#mermaid-svg-usTiZ44TfMKZIp78 .node circle,#mermaid-svg-usTiZ44TfMKZIp78 .node ellipse,#mermaid-svg-usTiZ44TfMKZIp78 .node polygon,#mermaid-svg-usTiZ44TfMKZIp78 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-usTiZ44TfMKZIp78 .rough-node .label text,#mermaid-svg-usTiZ44TfMKZIp78 .node .label text,#mermaid-svg-usTiZ44TfMKZIp78 .image-shape .label,#mermaid-svg-usTiZ44TfMKZIp78 .icon-shape .label{text-anchor:middle;}#mermaid-svg-usTiZ44TfMKZIp78 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-usTiZ44TfMKZIp78 .rough-node .label,#mermaid-svg-usTiZ44TfMKZIp78 .node .label,#mermaid-svg-usTiZ44TfMKZIp78 .image-shape .label,#mermaid-svg-usTiZ44TfMKZIp78 .icon-shape .label{text-align:center;}#mermaid-svg-usTiZ44TfMKZIp78 .node.clickable{cursor:pointer;}#mermaid-svg-usTiZ44TfMKZIp78 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-usTiZ44TfMKZIp78 .arrowheadPath{fill:#333333;}#mermaid-svg-usTiZ44TfMKZIp78 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-usTiZ44TfMKZIp78 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-usTiZ44TfMKZIp78 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-usTiZ44TfMKZIp78 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-usTiZ44TfMKZIp78 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-usTiZ44TfMKZIp78 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-usTiZ44TfMKZIp78 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-usTiZ44TfMKZIp78 .cluster text{fill:#333;}#mermaid-svg-usTiZ44TfMKZIp78 .cluster span{color:#333;}#mermaid-svg-usTiZ44TfMKZIp78 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-usTiZ44TfMKZIp78 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-usTiZ44TfMKZIp78 rect.text{fill:none;stroke-width:0;}#mermaid-svg-usTiZ44TfMKZIp78 .icon-shape,#mermaid-svg-usTiZ44TfMKZIp78 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-usTiZ44TfMKZIp78 .icon-shape p,#mermaid-svg-usTiZ44TfMKZIp78 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-usTiZ44TfMKZIp78 .icon-shape rect,#mermaid-svg-usTiZ44TfMKZIp78 .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-usTiZ44TfMKZIp78 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-usTiZ44TfMKZIp78 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-usTiZ44TfMKZIp78 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    是 true

    否 false

    完成引导

    点击跳过

    应用启动

    显示启动页持续2秒

    检查是否首次使用PreferencesHelper.isFirstLaunch

    显示引导页IntroScreen

    显示主页面MyHomePage

    用户操作

    保存首次使用标志setNotFirstLaunch

    标记已非首次启动


    ⚠️ 常见错误及解决方案

    ❌ 错误 1:依赖安装失败

    错误信息:

    Running "flutter pub get" in ffohnotes…
    pub get failed

    可能原因:

  • 网络连接问题
  • pubspec.yaml 语法错误
  • Flutter SDK 版本不兼容
  • 解决方案:

    # 1. 检查网络连接
    ping pub.dev

    # 2. 清理缓存后重新安装
    flutter clean
    flutter pub get

    # 3. 检查 pubspec.yaml 语法
    # 确保缩进正确(使用空格,不要使用 Tab)
    # 确保版本号格式正确(如 ^3.1.0)

    # 4. 使用国内镜像(如果网络较慢)
    export PUB_HOSTED_URL=https://pub.flutter-io.cn
    export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn
    flutter pub get

    ❌ 错误 2:图片资源找不到

    错误信息:

    Unable to load asset: assets/images/intro_1.png

    可能原因:

  • 图片文件不存在
  • pubspec.yaml 中未配置资源路径
  • 图片路径拼写错误
  • 解决方案:

    // 方案1:检查图片是否存在
    // 确保 assets/images/intro_1.png 文件存在

    // 方案2:在 pubspec.yaml 中配置资源路径
    flutter:
    assets:
    assets/images/

    // 方案3:使用占位符(推荐)
    // intro_slider 会自动处理图片加载失败的情况
    // 如果图片不存在,会显示占位符,不会导致应用崩溃

    // 方案4:不提供图片路径(可选)
    Slide(
    title: "欢迎使用天气预报",
    description: "实时掌握天气变化…",
    // pathImage: "assets/images/intro_1.png", // 注释掉或删除
    backgroundColor: const Color(0xFF64B5F6),
    // …
    )

    ❌ 错误 3:SharedPreferences 初始化失败

    错误信息:

    PlatformException(channel-error, Unable to establish connection on channel., null, null)

    可能原因:

  • 平台特定配置缺失(Android/iOS)
  • 权限问题
  • 解决方案:

    Android 配置(android/app/src/main/AndroidManifest.xml):

    <manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <!– 添加存储权限(如果需要) –>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

    <application
    android:label="天气预报"
    android:name="${applicationName}"
    android:icon="@mipmap/ic_launcher">

    <!– … –>
    </application>
    </manifest>

    iOS 配置(通常不需要额外配置):

    • shared_preferences 在 iOS 上使用 UserDefaults,通常不需要额外权限

    错误处理:

    static Future<bool> isFirstLaunch() async {
    try {
    final prefs = await SharedPreferences.getInstance();
    return prefs.getBool(_keyIsFirstLaunch) ?? true;
    } catch (e) {
    // 如果初始化失败,返回 true(显示引导页)
    debugPrint('SharedPreferences 初始化失败: $e');
    return true;
    }
    }

    ❌ 错误 4:引导页不显示

    错误信息:

    • 应用启动后直接进入主页面,没有显示引导页

    问题诊断流程:

    #mermaid-svg-EO0apMDv2TRbyuYj{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-EO0apMDv2TRbyuYj .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-EO0apMDv2TRbyuYj .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-EO0apMDv2TRbyuYj .error-icon{fill:#552222;}#mermaid-svg-EO0apMDv2TRbyuYj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-EO0apMDv2TRbyuYj .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-EO0apMDv2TRbyuYj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-EO0apMDv2TRbyuYj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-EO0apMDv2TRbyuYj .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-EO0apMDv2TRbyuYj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-EO0apMDv2TRbyuYj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-EO0apMDv2TRbyuYj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-EO0apMDv2TRbyuYj .marker.cross{stroke:#333333;}#mermaid-svg-EO0apMDv2TRbyuYj svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-EO0apMDv2TRbyuYj p{margin:0;}#mermaid-svg-EO0apMDv2TRbyuYj .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-EO0apMDv2TRbyuYj .cluster-label text{fill:#333;}#mermaid-svg-EO0apMDv2TRbyuYj .cluster-label span{color:#333;}#mermaid-svg-EO0apMDv2TRbyuYj .cluster-label span p{background-color:transparent;}#mermaid-svg-EO0apMDv2TRbyuYj .label text,#mermaid-svg-EO0apMDv2TRbyuYj span{fill:#333;color:#333;}#mermaid-svg-EO0apMDv2TRbyuYj .node rect,#mermaid-svg-EO0apMDv2TRbyuYj .node circle,#mermaid-svg-EO0apMDv2TRbyuYj .node ellipse,#mermaid-svg-EO0apMDv2TRbyuYj .node polygon,#mermaid-svg-EO0apMDv2TRbyuYj .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-EO0apMDv2TRbyuYj .rough-node .label text,#mermaid-svg-EO0apMDv2TRbyuYj .node .label text,#mermaid-svg-EO0apMDv2TRbyuYj .image-shape .label,#mermaid-svg-EO0apMDv2TRbyuYj .icon-shape .label{text-anchor:middle;}#mermaid-svg-EO0apMDv2TRbyuYj .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-EO0apMDv2TRbyuYj .rough-node .label,#mermaid-svg-EO0apMDv2TRbyuYj .node .label,#mermaid-svg-EO0apMDv2TRbyuYj .image-shape .label,#mermaid-svg-EO0apMDv2TRbyuYj .icon-shape .label{text-align:center;}#mermaid-svg-EO0apMDv2TRbyuYj .node.clickable{cursor:pointer;}#mermaid-svg-EO0apMDv2TRbyuYj .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-EO0apMDv2TRbyuYj .arrowheadPath{fill:#333333;}#mermaid-svg-EO0apMDv2TRbyuYj .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-EO0apMDv2TRbyuYj .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-EO0apMDv2TRbyuYj .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-EO0apMDv2TRbyuYj .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-EO0apMDv2TRbyuYj .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-EO0apMDv2TRbyuYj .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-EO0apMDv2TRbyuYj .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-EO0apMDv2TRbyuYj .cluster text{fill:#333;}#mermaid-svg-EO0apMDv2TRbyuYj .cluster span{color:#333;}#mermaid-svg-EO0apMDv2TRbyuYj div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-EO0apMDv2TRbyuYj .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-EO0apMDv2TRbyuYj rect.text{fill:none;stroke-width:0;}#mermaid-svg-EO0apMDv2TRbyuYj .icon-shape,#mermaid-svg-EO0apMDv2TRbyuYj .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-EO0apMDv2TRbyuYj .icon-shape p,#mermaid-svg-EO0apMDv2TRbyuYj .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-EO0apMDv2TRbyuYj .icon-shape rect,#mermaid-svg-EO0apMDv2TRbyuYj .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-EO0apMDv2TRbyuYj .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-EO0apMDv2TRbyuYj .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-EO0apMDv2TRbyuYj :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    返回 false

    返回 true

    未导入

    已导入

    setState 未调用

    状态切换错误

    引导页不显示

    检查 isFirstLaunch 返回值

    调用 resetFirstLaunch重置标志

    检查组件集成

    添加 import 语句

    检查状态管理

    确保调用 setState

    检查 PageState 枚举

    重新运行应用

    引导页是否显示?

    问题解决

    查看调试日志

    检查回调函数

    可能原因:

  • PreferencesHelper.isFirstLaunch() 返回 false
  • 引导页组件未正确集成
  • 状态管理问题
  • 解决方案:

    // 1. 检查首次使用标志
    // 在调试时,可以重置标志来测试引导页
    await PreferencesHelper.resetFirstLaunch();

    // 2. 添加调试日志
    void _onSplashComplete() async {
    if (!mounted) return;

    final isFirst = await PreferencesHelper.isFirstLaunch();
    debugPrint('是否首次启动: $isFirst'); // 添加日志

    if (mounted) {
    setState(() {
    if (isFirst) {
    _pageState = PageState.intro;
    debugPrint('切换到引导页'); // 添加日志
    } else {
    _pageState = PageState.home;
    debugPrint('切换到主页面'); // 添加日志
    }
    });
    }
    }

    // 3. 检查引导页组件是否正确导入
    import 'screens/intro_screen.dart';

    // 4. 确保 IntroScreen 组件正确创建
    case PageState.intro:
    return IntroScreen(
    onDone: _onIntroComplete, // 确保回调函数正确传递
    );

    ❌ 错误 5:按钮点击无响应

    错误信息:

    • 点击"跳过"或"开始使用"按钮没有反应

    问题排查流程:

    #mermaid-svg-43F8sQk4CgUGc1p1{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-43F8sQk4CgUGc1p1 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-43F8sQk4CgUGc1p1 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-43F8sQk4CgUGc1p1 .error-icon{fill:#552222;}#mermaid-svg-43F8sQk4CgUGc1p1 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-43F8sQk4CgUGc1p1 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-43F8sQk4CgUGc1p1 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-43F8sQk4CgUGc1p1 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-43F8sQk4CgUGc1p1 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-43F8sQk4CgUGc1p1 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-43F8sQk4CgUGc1p1 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-43F8sQk4CgUGc1p1 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-43F8sQk4CgUGc1p1 .marker.cross{stroke:#333333;}#mermaid-svg-43F8sQk4CgUGc1p1 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-43F8sQk4CgUGc1p1 p{margin:0;}#mermaid-svg-43F8sQk4CgUGc1p1 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-43F8sQk4CgUGc1p1 .cluster-label text{fill:#333;}#mermaid-svg-43F8sQk4CgUGc1p1 .cluster-label span{color:#333;}#mermaid-svg-43F8sQk4CgUGc1p1 .cluster-label span p{background-color:transparent;}#mermaid-svg-43F8sQk4CgUGc1p1 .label text,#mermaid-svg-43F8sQk4CgUGc1p1 span{fill:#333;color:#333;}#mermaid-svg-43F8sQk4CgUGc1p1 .node rect,#mermaid-svg-43F8sQk4CgUGc1p1 .node circle,#mermaid-svg-43F8sQk4CgUGc1p1 .node ellipse,#mermaid-svg-43F8sQk4CgUGc1p1 .node polygon,#mermaid-svg-43F8sQk4CgUGc1p1 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-43F8sQk4CgUGc1p1 .rough-node .label text,#mermaid-svg-43F8sQk4CgUGc1p1 .node .label text,#mermaid-svg-43F8sQk4CgUGc1p1 .image-shape .label,#mermaid-svg-43F8sQk4CgUGc1p1 .icon-shape .label{text-anchor:middle;}#mermaid-svg-43F8sQk4CgUGc1p1 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-43F8sQk4CgUGc1p1 .rough-node .label,#mermaid-svg-43F8sQk4CgUGc1p1 .node .label,#mermaid-svg-43F8sQk4CgUGc1p1 .image-shape .label,#mermaid-svg-43F8sQk4CgUGc1p1 .icon-shape .label{text-align:center;}#mermaid-svg-43F8sQk4CgUGc1p1 .node.clickable{cursor:pointer;}#mermaid-svg-43F8sQk4CgUGc1p1 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-43F8sQk4CgUGc1p1 .arrowheadPath{fill:#333333;}#mermaid-svg-43F8sQk4CgUGc1p1 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-43F8sQk4CgUGc1p1 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-43F8sQk4CgUGc1p1 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-43F8sQk4CgUGc1p1 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-43F8sQk4CgUGc1p1 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-43F8sQk4CgUGc1p1 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-43F8sQk4CgUGc1p1 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-43F8sQk4CgUGc1p1 .cluster text{fill:#333;}#mermaid-svg-43F8sQk4CgUGc1p1 .cluster span{color:#333;}#mermaid-svg-43F8sQk4CgUGc1p1 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-43F8sQk4CgUGc1p1 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-43F8sQk4CgUGc1p1 rect.text{fill:none;stroke-width:0;}#mermaid-svg-43F8sQk4CgUGc1p1 .icon-shape,#mermaid-svg-43F8sQk4CgUGc1p1 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-43F8sQk4CgUGc1p1 .icon-shape p,#mermaid-svg-43F8sQk4CgUGc1p1 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-43F8sQk4CgUGc1p1 .icon-shape rect,#mermaid-svg-43F8sQk4CgUGc1p1 .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-43F8sQk4CgUGc1p1 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-43F8sQk4CgUGc1p1 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-43F8sQk4CgUGc1p1 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    未传递

    已传递

    未实现

    已实现

    未调用 widget.onDone

    已调用

    按钮点击无响应

    检查回调函数传递

    添加 onDone 参数

    检查回调函数实现

    实现 _onIntroComplete

    检查 IntroScreen 内部调用

    确保调用 widget.onDone

    添加调试日志

    查看控制台输出

    日志是否打印?

    回调函数正常执行

    检查 Widget 生命周期

    可能原因:

  • 回调函数未正确传递
  • onDone 回调函数为空或未实现
  • 解决方案:

    // 1. 检查回调函数是否正确传递
    IntroScreen(
    onDone: _onIntroComplete, // 确保传递了回调函数
    )

    // 2. 确保回调函数已实现
    void _onIntroComplete() async {
    debugPrint('引导页完成回调被调用'); // 添加日志验证
    await PreferencesHelper.setNotFirstLaunch();

    if (mounted) {
    setState(() {
    _pageState = PageState.home;
    });
    }
    }

    // 3. 检查 IntroScreen 内部是否正确调用回调
    onDonePress: () {
    widget.onDone(); // 确保调用了外部传入的回调
    },
    onSkipPress: () {
    widget.onDone(); // 确保调用了外部传入的回调
    },

    ❌ 错误 6:页面指示器不显示

    错误信息:

    • 引导页底部没有显示页面指示器(小圆点)

    可能原因:

  • indicatorConfig 配置错误
  • 颜色设置与背景色相同,导致看不见
  • 解决方案:

    // 1. 检查指示器配置
    indicatorConfig: IndicatorConfig(
    sizeIndicator: 10, // 确保大小不为 0
    colorIndicator: Colors.white, // 确保颜色与背景色不同
    colorActiveIndicator: Colors.white.withOpacity(0.5),
    typeIndicatorAnimation: TypeIndicatorAnimation.sizeTransition,
    ),

    // 2. 如果背景是白色,使用深色指示器
    indicatorConfig: IndicatorConfig(
    sizeIndicator: 10,
    colorIndicator: Colors.blue, // 使用深色
    colorActiveIndicator: Colors.blue.withOpacity(0.5),
    typeIndicatorAnimation: TypeIndicatorAnimation.sizeTransition,
    ),

    ❌ 错误 7:滑动不流畅

    错误信息:

    • 左右滑动引导页时卡顿或不流畅

    可能原因:

  • 图片过大
  • 动画效果过多
  • 设备性能问题
  • 解决方案:

    // 1. 优化图片大小
    // 建议图片尺寸:1080×1920 或 750×1334
    // 使用图片压缩工具减小文件大小

    // 2. 减少动画效果
    indicatorConfig: IndicatorConfig(
    typeIndicatorAnimation: TypeIndicatorAnimation.none, // 禁用动画
    ),

    // 3. 使用占位符代替大图片
    // 如果图片加载慢,可以先显示占位符
    Slide(
    title: "欢迎使用天气预报",
    description: "实时掌握天气变化…",
    // pathImage: "assets/images/intro_1.png", // 不提供图片
    backgroundColor: const Color(0xFF64B5F6),
    // …
    )


    🔧 进阶功能

    1️⃣ 自定义引导页样式

    自定义按钮样式:

    IntroSlider(
    slides: slides,
    // 自定义跳过按钮
    renderSkipBtn: () {
    return Container(
    padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
    decoration: BoxDecoration(
    color: Colors.white.withOpacity(0.3),
    borderRadius: BorderRadius.circular(20),
    border: Border.all(color: Colors.white, width: 2),
    ),
    child: const Row(
    mainAxisSize: MainAxisSize.min,
    children: [
    Icon(Icons.close, color: Colors.white, size: 18),
    SizedBox(width: 4),
    Text(
    "跳过",
    style: TextStyle(
    color: Colors.white,
    fontSize: 16,
    fontWeight: FontWeight.w600,
    ),
    ),
    ],
    ),
    );
    },
    // 自定义完成按钮
    renderDoneBtn: () {
    return Container(
    padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 12),
    decoration: BoxDecoration(
    gradient: LinearGradient(
    colors: [Colors.white, Colors.blue.shade100],
    ),
    borderRadius: BorderRadius.circular(25),
    boxShadow: [
    BoxShadow(
    color: Colors.black.withOpacity(0.2),
    blurRadius: 8,
    offset: const Offset(0, 4),
    ),
    ],
    ),
    child: const Row(
    mainAxisSize: MainAxisSize.min,
    children: [
    Text(
    "开始使用",
    style: TextStyle(
    color: Color(0xFF1565C0),
    fontSize: 18,
    fontWeight: FontWeight.bold,
    ),
    ),
    SizedBox(width: 8),
    Icon(Icons.arrow_forward, color: Color(0xFF1565C0), size: 20),
    ],
    ),
    );
    },
    )

    自定义页面布局:

    // 使用自定义 Widget 构建页面内容
    Slide(
    title: "欢迎使用天气预报",
    description: "实时掌握天气变化…",
    // 使用自定义 Widget
    widgetDescription: Column(
    mainAxisAlignment: MainAxisAlignment.center,
    children: [
    Icon(Icons.wb_sunny, size: 100, color: Colors.white),
    const SizedBox(height: 20),
    const Text(
    "欢迎使用天气预报",
    style: TextStyle(
    fontSize: 28,
    fontWeight: FontWeight.bold,
    color: Colors.white,
    ),
    ),
    const SizedBox(height: 16),
    const Text(
    "实时掌握天气变化,为您的生活出行提供精准的天气信息",
    textAlign: TextAlign.center,
    style: TextStyle(
    fontSize: 16,
    color: Colors.white,
    ),
    ),
    ],
    ),
    backgroundColor: const Color(0xFF64B5F6),
    )

    2️⃣ 添加页面切换动画

    IntroSlider(
    slides: slides,
    // 页面切换动画配置
    animationDuration: const Duration(milliseconds: 300),
    // 滑动方向(水平或垂直)
    verticalScrollbarBehavior: ScrollbarBehavior.HIDE,
    )

    3️⃣ 根据设备语言显示不同内容

    void _initSlides() {
    // 获取设备语言
    final locale = Localizations.localeOf(context);
    final isChinese = locale.languageCode == 'zh';

    slides = [
    Slide(
    title: isChinese ? "欢迎使用天气预报" : "Welcome to Weather App",
    description: isChinese
    ? "实时掌握天气变化,为您的生活出行提供精准的天气信息"
    : "Get real-time weather updates for your daily life and travel",
    backgroundColor: const Color(0xFF64B5F6),
    titleColor: Colors.white,
    descriptionColor: Colors.white,
    ),
    // … 其他页面
    ];
    }

    4️⃣ 添加页面切换监听

    IntroSlider(
    slides: slides,
    // 页面切换回调
    onTabChangeCompleted: (index) {
    debugPrint('切换到第 ${index + 1} 页');
    // 可以在这里添加页面切换动画、统计等
    },
    )

    5️⃣ 条件显示引导页

    版本更新检测流程:

    #mermaid-svg-cqI175uDyD3o1OPM{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-cqI175uDyD3o1OPM .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-cqI175uDyD3o1OPM .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-cqI175uDyD3o1OPM .error-icon{fill:#552222;}#mermaid-svg-cqI175uDyD3o1OPM .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-cqI175uDyD3o1OPM .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-cqI175uDyD3o1OPM .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-cqI175uDyD3o1OPM .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-cqI175uDyD3o1OPM .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-cqI175uDyD3o1OPM .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-cqI175uDyD3o1OPM .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-cqI175uDyD3o1OPM .marker{fill:#333333;stroke:#333333;}#mermaid-svg-cqI175uDyD3o1OPM .marker.cross{stroke:#333333;}#mermaid-svg-cqI175uDyD3o1OPM svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-cqI175uDyD3o1OPM p{margin:0;}#mermaid-svg-cqI175uDyD3o1OPM .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-cqI175uDyD3o1OPM .cluster-label text{fill:#333;}#mermaid-svg-cqI175uDyD3o1OPM .cluster-label span{color:#333;}#mermaid-svg-cqI175uDyD3o1OPM .cluster-label span p{background-color:transparent;}#mermaid-svg-cqI175uDyD3o1OPM .label text,#mermaid-svg-cqI175uDyD3o1OPM span{fill:#333;color:#333;}#mermaid-svg-cqI175uDyD3o1OPM .node rect,#mermaid-svg-cqI175uDyD3o1OPM .node circle,#mermaid-svg-cqI175uDyD3o1OPM .node ellipse,#mermaid-svg-cqI175uDyD3o1OPM .node polygon,#mermaid-svg-cqI175uDyD3o1OPM .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-cqI175uDyD3o1OPM .rough-node .label text,#mermaid-svg-cqI175uDyD3o1OPM .node .label text,#mermaid-svg-cqI175uDyD3o1OPM .image-shape .label,#mermaid-svg-cqI175uDyD3o1OPM .icon-shape .label{text-anchor:middle;}#mermaid-svg-cqI175uDyD3o1OPM .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-cqI175uDyD3o1OPM .rough-node .label,#mermaid-svg-cqI175uDyD3o1OPM .node .label,#mermaid-svg-cqI175uDyD3o1OPM .image-shape .label,#mermaid-svg-cqI175uDyD3o1OPM .icon-shape .label{text-align:center;}#mermaid-svg-cqI175uDyD3o1OPM .node.clickable{cursor:pointer;}#mermaid-svg-cqI175uDyD3o1OPM .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-cqI175uDyD3o1OPM .arrowheadPath{fill:#333333;}#mermaid-svg-cqI175uDyD3o1OPM .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-cqI175uDyD3o1OPM .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-cqI175uDyD3o1OPM .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-cqI175uDyD3o1OPM .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-cqI175uDyD3o1OPM .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-cqI175uDyD3o1OPM .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-cqI175uDyD3o1OPM .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-cqI175uDyD3o1OPM .cluster text{fill:#333;}#mermaid-svg-cqI175uDyD3o1OPM .cluster span{color:#333;}#mermaid-svg-cqI175uDyD3o1OPM div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-cqI175uDyD3o1OPM .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-cqI175uDyD3o1OPM rect.text{fill:none;stroke-width:0;}#mermaid-svg-cqI175uDyD3o1OPM .icon-shape,#mermaid-svg-cqI175uDyD3o1OPM .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-cqI175uDyD3o1OPM .icon-shape p,#mermaid-svg-cqI175uDyD3o1OPM .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-cqI175uDyD3o1OPM .icon-shape rect,#mermaid-svg-cqI175uDyD3o1OPM .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-cqI175uDyD3o1OPM .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-cqI175uDyD3o1OPM .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-cqI175uDyD3o1OPM :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    不同

    相同

    检查是否显示引导页

    获取上次引导页版本last_intro_version

    获取当前应用版本currentVersion

    版本是否相同?

    版本已更新

    检查首次启动标志

    保存当前版本号

    返回 true显示引导页

    是否首次启动?

    返回 false不显示引导页

    代码实现:

    // 根据应用版本决定是否显示引导页
    Future<bool> shouldShowIntro() async {
    final prefs = await SharedPreferences.getInstance();
    final lastVersion = prefs.getString('last_intro_version');
    final currentVersion = '1.0.0'; // 从 package_info_plus 获取

    // 如果版本更新,重新显示引导页
    if (lastVersion != currentVersion) {
    await prefs.setString('last_intro_version', currentVersion);
    return true;
    }

    // 检查是否首次启动
    return prefs.getBool('is_first_launch') ?? true;
    }


    📚 完整代码示例

    文件结构

    #mermaid-svg-8tdbx43g7KGMCC1W{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-8tdbx43g7KGMCC1W .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-8tdbx43g7KGMCC1W .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-8tdbx43g7KGMCC1W .error-icon{fill:#552222;}#mermaid-svg-8tdbx43g7KGMCC1W .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-8tdbx43g7KGMCC1W .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-8tdbx43g7KGMCC1W .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-8tdbx43g7KGMCC1W .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-8tdbx43g7KGMCC1W .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-8tdbx43g7KGMCC1W .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-8tdbx43g7KGMCC1W .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-8tdbx43g7KGMCC1W .marker{fill:#333333;stroke:#333333;}#mermaid-svg-8tdbx43g7KGMCC1W .marker.cross{stroke:#333333;}#mermaid-svg-8tdbx43g7KGMCC1W svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-8tdbx43g7KGMCC1W p{margin:0;}#mermaid-svg-8tdbx43g7KGMCC1W .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-8tdbx43g7KGMCC1W .cluster-label text{fill:#333;}#mermaid-svg-8tdbx43g7KGMCC1W .cluster-label span{color:#333;}#mermaid-svg-8tdbx43g7KGMCC1W .cluster-label span p{background-color:transparent;}#mermaid-svg-8tdbx43g7KGMCC1W .label text,#mermaid-svg-8tdbx43g7KGMCC1W span{fill:#333;color:#333;}#mermaid-svg-8tdbx43g7KGMCC1W .node rect,#mermaid-svg-8tdbx43g7KGMCC1W .node circle,#mermaid-svg-8tdbx43g7KGMCC1W .node ellipse,#mermaid-svg-8tdbx43g7KGMCC1W .node polygon,#mermaid-svg-8tdbx43g7KGMCC1W .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-8tdbx43g7KGMCC1W .rough-node .label text,#mermaid-svg-8tdbx43g7KGMCC1W .node .label text,#mermaid-svg-8tdbx43g7KGMCC1W .image-shape .label,#mermaid-svg-8tdbx43g7KGMCC1W .icon-shape .label{text-anchor:middle;}#mermaid-svg-8tdbx43g7KGMCC1W .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-8tdbx43g7KGMCC1W .rough-node .label,#mermaid-svg-8tdbx43g7KGMCC1W .node .label,#mermaid-svg-8tdbx43g7KGMCC1W .image-shape .label,#mermaid-svg-8tdbx43g7KGMCC1W .icon-shape .label{text-align:center;}#mermaid-svg-8tdbx43g7KGMCC1W .node.clickable{cursor:pointer;}#mermaid-svg-8tdbx43g7KGMCC1W .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-8tdbx43g7KGMCC1W .arrowheadPath{fill:#333333;}#mermaid-svg-8tdbx43g7KGMCC1W .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-8tdbx43g7KGMCC1W .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-8tdbx43g7KGMCC1W .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8tdbx43g7KGMCC1W .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-8tdbx43g7KGMCC1W .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8tdbx43g7KGMCC1W .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-8tdbx43g7KGMCC1W .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-8tdbx43g7KGMCC1W .cluster text{fill:#333;}#mermaid-svg-8tdbx43g7KGMCC1W .cluster span{color:#333;}#mermaid-svg-8tdbx43g7KGMCC1W div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-8tdbx43g7KGMCC1W .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-8tdbx43g7KGMCC1W rect.text{fill:none;stroke-width:0;}#mermaid-svg-8tdbx43g7KGMCC1W .icon-shape,#mermaid-svg-8tdbx43g7KGMCC1W .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8tdbx43g7KGMCC1W .icon-shape p,#mermaid-svg-8tdbx43g7KGMCC1W .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-8tdbx43g7KGMCC1W .icon-shape rect,#mermaid-svg-8tdbx43g7KGMCC1W .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8tdbx43g7KGMCC1W .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-8tdbx43g7KGMCC1W .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-8tdbx43g7KGMCC1W :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    lib/

    main.dart应用入口

    screens/

    utils/

    splash_screen.dart启动页

    intro_screen.dart引导页

    preferences_helper.dart首选项管理工具

    SplashWrapper页面状态管理

    MyHomePage主页面

    目录说明:

    lib/
    ├── main.dart # 应用入口
    ├── screens/
    │ ├── splash_screen.dart # 启动页
    │ └── intro_screen.dart # 引导页
    └── utils/
    └── preferences_helper.dart # 首选项管理工具

    完整代码

    所有代码文件已在前面章节提供,这里不再重复。完整的项目代码包括:

  • lib/utils/preferences_helper.dart:首选项管理工具类
  • lib/screens/intro_screen.dart:引导页组件
  • lib/main.dart:主应用入口(已修改)
  • 运行项目

    # 1. 安装依赖
    flutter pub get

    # 2. 运行应用
    flutter run

    # 3. 测试引导页
    # 首次运行会显示引导页
    # 完成引导后,再次运行不会显示引导页

    # 4. 重置引导页(用于测试)
    # 在代码中调用:
    await PreferencesHelper.resetFirstLaunch();


    📖 总结

    本教程详细介绍了如何使用 intro_slider 三方库实现 Flutter 应用引导页,包括:

    ✅ 引入三方库步骤:添加依赖、配置资源、安装依赖 ✅ 具体页面实现:创建工具类、引导页组件、集成到主应用 ✅ 常见错误解决:依赖安装、资源加载、状态管理等问题 ✅ 进阶功能:自定义样式、多语言支持、条件显示等

    🎯 关键要点

  • 首次使用检测:使用 SharedPreferences 保存首次使用标志
  • 页面状态管理:使用枚举管理启动页、引导页、主页面的切换
  • 用户体验:提供"跳过"按钮,允许用户快速进入主界面
  • 错误处理:添加异常处理,确保应用稳定运行
  • 📚 相关资源

    • intro_slider 官方文档
    • shared_preferences 官方文档
    • Flutter 官方文档

    🎉 祝你开发顺利! 🚀

    欢迎加入开源鸿蒙跨平台社区

    赞(0)
    未经允许不得转载:171主机测评 » 鸿蒙应用中使用Flutter三方库intro_slider实现美观引导页的实战指南
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址