Flutter(GoRouter)如何预加载路由或让Widget离屏时保持加载?
解决GoRouter下QuillHtmlEditor页面重复加载的问题
要实现QuillHtmlEditor页面始终保持加载状态、避免每次导航重建,最简便的方案是利用页面状态保持容器结合GoRouter的路由配置,以下是两种可行的实现方式:
方式一:自定义IndexedStack容器管理页面状态
将需要预加载的页面统一放在IndexedStack中,切换路由时仅切换容器的索引,页面不会被销毁重建。
1. 创建全局页面容器
class AppContainer extends StatefulWidget { const AppContainer({super.key}); @override State<AppContainer> createState() => _AppContainerState(); } class _AppContainerState extends State<AppContainer> { int _currentPageIndex = 0; // 预加载所有需要保持状态的页面 final List<Widget> _pages = const [ HomePage(), QuillEditorPage(), // 你的富文本编辑器页面 SettingsPage(), ]; // 提供切换页面的方法 void switchPage(int index) { setState(() { _currentPageIndex = index; }); } @override Widget build(BuildContext context) { return IndexedStack( index: _currentPageIndex, children: _pages, ); } }
2. 配置GoRouter路由
让路由指向容器,通过路由跳转时触发页面索引切换:
final GoRouter router = GoRouter( routes: [ GoRoute( path: '/', builder: (context, state) => const AppContainer(), routes: [ GoRoute( path: 'home', builder: (context, state) { (context.findAncestorStateOfType<_AppContainerState>() as _AppContainerState).switchPage(0); return const SizedBox.shrink(); }, ), GoRoute( path: 'editor', builder: (context, state) { (context.findAncestorStateOfType<_AppContainerState>() as _AppContainerState).switchPage(1); return const SizedBox.shrink(); }, ), GoRoute( path: 'settings', builder: (context, state) { (context.findAncestorStateOfType<_AppContainerState>() as _AppContainerState).switchPage(2); return const SizedBox.shrink(); }, ), ], ), ], );
3. 给Quill页面添加保活
在Quill编辑器页面混入AutomaticKeepAliveClientMixin,确保页面状态不被回收:
class QuillEditorPage extends StatefulWidget { const QuillEditorPage({super.key}); @override State<QuillEditorPage> createState() => _QuillEditorPageState(); } class _QuillEditorPageState extends State<QuillEditorPage> with AutomaticKeepAliveClientMixin { late QuillHtmlEditorController _editorController; @override bool get wantKeepAlive => true; // 开启页面保活 @override void initState() { super.initState(); // 仅初始化一次编辑器 _editorController = QuillHtmlEditorController(); // 这里可以加载初始内容、配置编辑器,只会执行一次 } @override Widget build(BuildContext context) { super.build(context); // 必须调用此方法触发保活逻辑 return Scaffold( body: QuillHtmlEditor( controller: _editorController, // 你的编辑器配置 ), ); } }
方式二:使用GoRouter的StatefulShellRoute(底部导航场景)
如果你的App是底部导航结构,直接用GoRouter内置的StatefulShellRoute,它会自动用IndexedStack管理各分支页面的状态,无需手动实现容器:
final GoRouter router = GoRouter( routes: [ StatefulShellRoute.indexedStack( builder: (context, state, navigationShell) { return Scaffold( body: navigationShell, bottomNavigationBar: BottomNavigationBar( currentIndex: navigationShell.currentIndex, onTap: (index) { navigationShell.goBranch( index, initialLocation: index == navigationShell.currentIndex, ); }, items: const [ BottomNavigationBarItem(icon: Icon(Icons.home), label: '首页'), BottomNavigationBarItem(icon: Icon(Icons.edit), label: '编辑器'), BottomNavigationBarItem(icon: Icon(Icons.settings), label: '设置'), ], ), ); }, branches: [ StatefulShellBranch( routes: [ GoRoute( path: '/home', builder: (context, state) => const HomePage(), ), ], ), StatefulShellBranch( routes: [ GoRoute( path: '/editor', builder: (context, state) => const QuillEditorPage(), ), ], ), StatefulShellBranch( routes: [ GoRoute( path: '/settings', builder: (context, state) => const SettingsPage(), ), ], ), ], ), ], );
这种方式下,切换底部导航时,Quill页面会保持之前的加载状态,不会重新初始化。
关键注意点
- 确保Quill的初始化逻辑(如加载内容、配置控制器)放在
initState中,仅执行一次; - 如果使用状态管理(如Provider、GetX),可以替代直接查找父状态的方式,让页面切换逻辑更优雅;
- 若App不是底部导航结构,方式一的自定义IndexedStack容器是更通用的解决方案。
内容的提问来源于stack exchange,提问作者bf8
相关产品推荐
相关产品推荐

