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

如何在Flutter引导页实现指定自定义动画?GetX尝试遇报错

Flutter 引导页动画实现(结合GetX)

从你提供的动图来看,目标效果是引导页切换时的内容滑入淡入动画 + 底部指示器联动,以下是用GetX实现的完整方案,同时解决常见报错问题:

1. 定义引导页数据模型

先封装每个引导页的内容数据:

class OnBoardItem {
  final String title;
  final String description;
  final String imageAsset;

  OnBoardItem({required this.title, required this.description, required this.imageAsset});
}

2. 实现GetX状态控制器

负责管理页面索引、动画控制器和页面切换逻辑,注意要混入SingleGetTickerProviderMixin来支持动画:

class OnBoardController extends GetxController with SingleGetTickerProviderMixin {
  late PageController pageController;
  late AnimationController animationController;
  final currentIndex = 0.obs; // 响应式当前页面索引

  // 引导页数据列表,根据需求替换成你的内容
  final List<OnBoardItem> items = [
    OnBoardItem(title: "探索新功能", description: "全新的界面设计,带来更流畅的体验", imageAsset: "assets/onboard1.png"),
    OnBoardItem(title: "智能同步", description: "数据自动同步,随时随地访问你的内容", imageAsset: "assets/onboard2.png"),
    OnBoardItem(title: "开始使用", description: "点击下方按钮,开启你的旅程", imageAsset: "assets/onboard3.png"),
  ];

  @override
  void onInit() {
    super.onInit();
    // 初始化PageController和动画控制器
    pageController = PageController();
    animationController = AnimationController(
      duration: const Duration(milliseconds: 800),
      vsync: this,
    );
    animationController.forward(); // 初始页面播放动画
  }

  @override
  void onClose() {
    // 销毁控制器,避免内存泄漏
    pageController.dispose();
    animationController.dispose();
    super.onClose();
  }

  // 页面切换时触发,重置并播放动画
  void onPageChanged(int index) {
    currentIndex.value = index;
    animationController.reset();
    animationController.forward();
  }
}

3. 构建引导页UI

结合PageView和GetX的响应式状态,实现动画和指示器联动:

class OnBoardScreen extends StatelessWidget {
  // 初始化控制器
  final OnBoardController controller = Get.put(OnBoardController());

  OnBoardScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Column(
          children: [
            Expanded(
              child: PageView.builder(
                controller: controller.pageController,
                onPageChanged: controller.onPageChanged,
                itemCount: controller.items.length,
                itemBuilder: (context, index) {
                  final item = controller.items[index];
                  // 用Obx监听动画状态变化
                  return Obx(() {
                    // 定义位移动画:从下方20%位置滑入
                    final slideAnimation = Tween<Offset>(
                      begin: const Offset(0, 0.2),
                      end: Offset.zero,
                    ).animate(CurvedAnimation(
                      parent: controller.animationController,
                      curve: Curves.easeInOut,
                    ));
                    // 定义淡入动画
                    final fadeAnimation = controller.animationController;

                    return SlideTransition(
                      position: slideAnimation,
                      child: FadeTransition(
                        opacity: fadeAnimation,
                        child: Column(
                          mainAxisAlignment: MainAxisAlignment.center,
                          children: [
                            Image.asset(item.imageAsset, height: 250),
                            const SizedBox(height: 40),
                            Text(item.title, style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
                            const SizedBox(height: 16),
                            Padding(
                              padding: const EdgeInsets.symmetric(horizontal: 32),
                              child: Text(item.description, textAlign: TextAlign.center, style: const TextStyle(fontSize: 16)),
                            ),
                          ],
                        ),
                      ),
                    );
                  });
                },
              ),
            ),
            // 底部指示器
            Obx(() {
              return Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: List.generate(controller.items.length, (index) {
                  return Container(
                    width: controller.currentIndex.value == index ? 24 : 8,
                    height: 8,
                    margin: const EdgeInsets.symmetric(horizontal: 4),
                    decoration: BoxDecoration(
                      color: controller.currentIndex.value == index ? Colors.blue : Colors.grey.shade300,
                      borderRadius: BorderRadius.circular(4),
                    ),
                  );
                }),
              );
            }),
            const SizedBox(height: 40),
            // 操作按钮组
            Padding(
              padding: const EdgeInsets.symmetric(horizontal: 32),
              child: Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  TextButton(
                    onPressed: () {
                      // 跳过引导逻辑,比如直接跳转到首页
                    },
                    child: const Text("跳过"),
                  ),
                  ElevatedButton(
                    onPressed: () {
                      if (controller.currentIndex.value == controller.items.length - 1) {
                        // 完成引导,执行后续逻辑(比如存标记、跳首页)
                      } else {
                        // 切换到下一页
                        controller.pageController.nextPage(
                          duration: const Duration(milliseconds: 500),
                          curve: Curves.easeInOut,
                        );
                      }
                    },
                    child: Text(controller.currentIndex.value == controller.items.length - 1 ? "完成" : "下一步"),
                  ),
                ],
              ),
            ),
            const SizedBox(height: 20),
          ],
        ),
      ),
    );
  }
}

4. 常见报错排查

报错1: LateInitializationError

  • 原因:pageController或animationController未在onInit中初始化就被使用
  • 解决:确保在Controller的onInit方法中完成初始化,且在UI中使用前通过Get.put(OnBoardController())初始化控制器

报错2: 动画不触发/无效果

  • 原因:页面切换时未重置并重启动画控制器,或者未用Obx监听状态变化
  • 解决:在onPageChanged方法中调用animationController.reset()和animationController.forward(),且动画相关组件包裹在Obx内

报错3: GetX找不到Controller

  • 原因:UI中使用Get.find()时,控制器尚未被初始化
  • 解决:优先在Widget的顶层用Get.put(OnBoardController())初始化,避免在子Widget中延迟初始化

报错4: 内存泄漏警告

  • 原因:未销毁PageController和AnimationController
  • 解决:在Controller的onClose方法中调用dispose()释放资源

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 12:42:44