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

Flutter用intro_slider做手册后切换导航栏触发State异常排查

切换底部导航栏时IntroSlider组件触发"已卸载组件使用上下文"异常

我用intro_slider在Flutter应用内制作用户手册页面,将手册组件ManageRecipe放在Profile页面中,用户登录后可查看。但通过底部导航栏切换各模块时,触发了FlutterError异常:

This widget has been unmounted, so the State no longer has a context (and should be considered defunct). Consider canceling any active work during "dispose" or using the "mounted" getter to determine if the State is still active.

相关代码

manageRecipe.dart

import 'package:intro_slider/intro_slider.dart';

class ManageRecipe extends StatefulWidget {
  @override
  State<ManageRecipe> createState() => _ManageRecipeState();
}

class _ManageRecipeState extends State<ManageRecipe> {
  List<Slide> listSlides = [];

  @override
  Widget build(BuildContext context) {
    return IntroSlider(
      slides: listSlides,
      onDonePress: onPressedDone,
    );
  }

  @override
  void initState() {
    super.initState();
    listSlides.add(Slide(
      title: "Premier League",
      description: "English Premier League or EPL",
      pathImage: "assets/userManual/userManual_background.png",
      backgroundColor: Colors.pinkAccent,
    ));
    listSlides.add(Slide(
      title: "La Liga",
      description: "The Campeonato Nacional de Liga de Primera División",
      pathImage: "assets/userManual/userManual_background.png",
      backgroundColor: Colors.deepOrangeAccent,
    ));
    listSlides.add(Slide(
      title: "Bundesliga",
      description: "Federal League",
      pathImage: "assets/userManual/userManual_background.png",
      backgroundColor: Colors.redAccent,
    ));
    listSlides.add(Slide(
      title: "Serie A",
      description: "Lega Nazionale Professionisti Serie A",
      pathImage: "assets/userManual/userManual_background.png",
      backgroundColor: Colors.blueAccent,
    ));
    listSlides.add(Slide(
      title: "Ligue 1",
      description: "Ligue 1 Conforama",
      pathImage: "assets/userManual/userManual_background.png",
      backgroundColor: Colors.amberAccent,
    ));
  }

  onPressedDone() {}
}

navigationBar.dart

import 'package:convex_bottom_bar/convex_bottom_bar.dart';
import 'package:recipe_collector/All%20Recipes/viewRecipe.dart';
import 'package:recipe_collector/OCR/OCR_Page.dart';
import 'package:recipe_collector/Profile/Profile.dart';
import 'package:recipe_collector/Recipes%20Collection/RecipeSearch.dart';
import 'package:recipe_collector/Recipes%20Collection/recipe_collection_home.dart';
import 'package:recipe_collector/BottomNavigationBar.dart';
import 'BuyingList/buyingHome.dart';

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

  @override
  State<BottomNav> createState() => _BottomNavState();
}

class _BottomNavState extends State<BottomNav> {
  int _currentindex = 2;
  List pages = [
    RecipesViewPage(),
    RecipeCollectionHomePage(),
    OCRPage(),
    HomeBuying(),
    Profile()
  ];

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
          body: pages[_currentindex],
          bottomNavigationBar: ConvexAppBar(
              style: TabStyle.reactCircle,
              backgroundColor: Color.fromARGB(255, 51, 44, 25),
              items: [
                TabItem(icon: Icons.menu_book_rounded, title: 'All Recipes'),
                TabItem(icon: Icons.folder_copy_rounded, title: 'Collection'),
                TabItem(icon: Icons.document_scanner_rounded, title: 'OCR'),
                TabItem(icon: Icons.checklist_outlined, title: 'Buying List'),
                TabItem(icon: Icons.perm_contact_cal, title: 'Profile'),
              ],
              initialActiveIndex: 2, //optional, default as 0
              onTap: (int i) {
                if (mounted) {
                  setState(() {
                    _currentindex = i;
                  });
                }
              })),
    );
  }
}

问题根源

  1. 页面实例提前初始化引发状态冲突:pages列表直接创建了所有页面的实例(包括包含ManageRecipe的Profile),切换导航栏时,这些实例会被卸载再重新挂载,而IntroSlider内部可能持有已卸载组件的上下文引用,或者在组件卸载后仍有异步操作(比如图片加载)尝试更新状态,导致异常。
  2. IntroSlider内部资源未及时清理:该组件在页面卸载后,可能存在未终止的回调或状态更新逻辑,试图访问已失效的context。

修复方案

方案1:延迟创建页面实例(推荐)

把pages列表改为返回Widget的函数列表,每次切换导航时才创建新的页面实例,避免复用已卸载的组件:

class _BottomNavState extends State<BottomNav> {
  int _currentindex = 2;
  // 改为函数列表,每次调用才生成页面实例
  List<Widget Function()> pages = [
    () => RecipesViewPage(),
    () => RecipeCollectionHomePage(),
    () => OCRPage(),
    () => HomeBuying(),
    () => Profile()
  ];

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
          // 调用函数创建当前页面
          body: pages[_currentindex](),
          bottomNavigationBar: ConvexAppBar(
              style: TabStyle.reactCircle,
              backgroundColor: Color.fromARGB(255, 51, 44, 25),
              items: [
                TabItem(icon: Icons.menu_book_rounded, title: 'All Recipes'),
                TabItem(icon: Icons.folder_copy_rounded, title: 'Collection'),
                TabItem(icon: Icons.document_scanner_rounded, title: 'OCR'),
                TabItem(icon: Icons.checklist_outlined, title: 'Buying List'),
                TabItem(icon: Icons.perm_contact_cal, title: 'Profile'),
              ],
              initialActiveIndex: 2,
              onTap: (int i) {
                if (mounted) {
                  setState(() {
                    _currentindex = i;
                  });
                }
              })),
    );
  }
}

方案2:手动清理IntroSlider资源

如果需要保留页面实例复用,在_ManageRecipeState中重写dispose方法,清理IntroSlider的内部资源:

class _ManageRecipeState extends State<ManageRecipe> {
  List<Slide> listSlides = [];
  IntroSliderController? _sliderController;

  @override
  void initState() {
    super.initState();
    _sliderController = IntroSliderController();
    // 原有初始化slide的代码不变
    listSlides.add(Slide(...));
    // ... 其他slide添加逻辑
  }

  @override
  void dispose() {
    // 销毁控制器,终止内部异步操作
    _sliderController?.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return IntroSlider(
      controller: _sliderController,
      slides: listSlides,
      onDonePress: onPressedDone,
    );
  }

  onPressedDone() {}
}

方案3:保留页面状态(可选)

如果希望Profile页面切换后保留状态,使用AutomaticKeepAliveClientMixin,避免页面被频繁卸载:

class Profile extends StatefulWidget {
  @override
  State<Profile> createState() => _ProfileState();
}

class _ProfileState extends State<Profile> with AutomaticKeepAliveClientMixin {
  @override
  bool get wantKeepAlive => true;

  @override
  Widget build(BuildContext context) {
    super.build(context); // 必须调用此方法
    return ManageRecipe();
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 00:42:03