开启安卓「不保留活动」后,Flutter应用路由栈如何保持或恢复?
解决Flutter在Android「不保留活动」下的路由栈恢复问题
这个问题我之前做测试的时候也碰到过——开启「不保留活动」后,Flutter应用重启就直接回到首页,之前的路由栈全丢了,确实挺影响体验的。下面分享几个经过验证的解决方案,你可以根据项目情况选择:
方案1:自定义NavigatorObserver持久化路由栈
核心思路是监听路由的跳转、返回操作,把当前路由栈的信息(名称、参数)保存到本地存储(比如SharedPreferences或Hive),待应用重启时读取并恢复。
实现步骤:
- 自定义一个
NavigatorObserver,监听路由变化并保存栈信息:
import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; class PersistentRouteObserver extends NavigatorObserver { final SharedPreferences prefs; PersistentRouteObserver(this.prefs); @override void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) { super.didPush(route, previousRoute); _saveCurrentRouteStack(); } @override void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) { super.didPop(route, previousRoute); _saveCurrentRouteStack(); } @override void didRemove(Route<dynamic> route, Route<dynamic>? previousRoute) { super.didRemove(route, previousRoute); _saveCurrentRouteStack(); } void _saveCurrentRouteStack() { if (navigator == null) return; // 收集当前栈中所有路由的名称和参数 final routeStack = navigator!.history.map((entry) { return { 'name': entry.settings.name, 'arguments': entry.settings.arguments, }; }).toList(); // 转成JSON字符串存储 prefs.setString('persistent_route_stack', jsonEncode(routeStack)); } }
- 应用初始化时读取存储的路由栈并恢复:
import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); final prefs = await SharedPreferences.getInstance(); runApp(MyApp(prefs: prefs)); } class MyApp extends StatelessWidget { final SharedPreferences prefs; const MyApp({super.key, required this.prefs}); @override Widget build(BuildContext context) { return MaterialApp( navigatorObservers: [PersistentRouteObserver(prefs)], initialRoute: '/', onGenerateRoute: (settings) { // 基础路由生成逻辑 switch (settings.name) { case '/': return MaterialPageRoute( builder: (_) => const HomePage(), settings: settings, ); case '/details': return MaterialPageRoute( builder: (_) => DetailsPage(args: settings.arguments), settings: settings, ); default: return MaterialPageRoute(builder: (_) => const NotFoundPage()); } }, // 在首页初始化后恢复路由栈 home: FutureBuilder( future: _getSavedRouteStack(), builder: (context, snapshot) { if (snapshot.connectionState != ConnectionState.done) { return const SplashScreen(); } final routeStack = snapshot.data ?? []; return HomePage(onInit: () { // 跳过首页,恢复后续路由 for (var i = 1; i < routeStack.length; i++) { final routeData = routeStack[i]; Navigator.of(context).pushNamed( routeData['name'] as String, arguments: routeData['arguments'], ); } }); }, ), ); } Future<List<Map<String, dynamic>>> _getSavedRouteStack() async { final stackJson = prefs.getString('persistent_route_stack'); if (stackJson == null) return []; return (jsonDecode(stackJson) as List).cast<Map<String, dynamic>>(); } }
注意:如果你的路由需要携带复杂参数,要确保参数可以被JSON序列化,或者使用支持对象存储的库(比如Hive)。
方案2:结合Android原生状态保存机制
因为问题本质是Android系统回收Activity时未保存Flutter的路由状态,所以可以在原生MainActivity中重写onSaveInstanceState和onRestoreInstanceState,通过MethodChannel传递路由栈信息。
原生Kotlin代码:
import android.os.Bundle import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugin.common.MethodChannel class MainActivity : FlutterActivity() { private val CHANNEL = "com.your.app/route_stack" private var savedRouteStack: String? = null override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) outState.putString("route_stack", savedRouteStack) } override fun onRestoreInstanceState(savedInstanceState: Bundle) { super.onRestoreInstanceState(savedInstanceState) savedRouteStack = savedInstanceState.getString("route_stack") savedRouteStack?.let { MethodChannel(flutterEngine?.dartExecutor?.binaryMessenger, CHANNEL) .invokeMethod("restoreRouteStack", it) } } override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result -> when (call.method) { "saveRouteStack" -> { savedRouteStack = call.arguments as String result.success(null) } else -> result.notImplemented() } } // 启动时检查是否有保存的路由栈 intent.extras?.getString("route_stack")?.let { MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL) .invokeMethod("restoreRouteStack", it) } } }
Flutter端处理:
import 'dart:convert'; import 'package:flutter/services.dart'; import 'package:flutter/material.dart'; class RouteStackManager { static const MethodChannel _channel = MethodChannel('com.your.app/route_stack'); static final RouteStackManager _instance = RouteStackManager._(); RouteStackManager._(); factory RouteStackManager() => _instance; // 保存路由栈到原生 Future<void> saveRouteStack(BuildContext context) async { final routeStack = Navigator.of(context).history.map((entry) { return { 'name': entry.settings.name, 'arguments': entry.settings.arguments, }; }).toList(); await _channel.invokeMethod('saveRouteStack', jsonEncode(routeStack)); } // 监听原生传来的恢复指令 void listenForRestore(BuildContext context, Function(List<Map<String, dynamic>>) onRestore) { _channel.setMethodCallHandler((call) async { if (call.method == 'restoreRouteStack') { final stackJson = call.arguments as String; final routeStack = (jsonDecode(stackJson) as List).cast<Map<String, dynamic>>(); onRestore(routeStack); } return null; }); } } // 在首页中使用 class HomePage extends StatefulWidget { const HomePage({super.key}); @override State<HomePage> createState() => _HomePageState(); } class _HomePageState extends State<HomePage> { @override void initState() { super.initState(); RouteStackManager().listenForRestore(context, (routeStack) { // 恢复路由栈 for (var i = 1; i < routeStack.length; i++) { final routeData = routeStack[i]; Navigator.of(context).pushNamed( routeData['name'] as String, arguments: routeData['arguments'], ); } }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Home')), body: Center( child: ElevatedButton( onPressed: () { Navigator.pushNamed(context, '/details', arguments: 'test data'); // 跳转后保存栈 RouteStackManager().saveRouteStack(context); }, child: const Text('Go to Details'), ), ), ); } }
这个方案更贴合Android系统的原生行为,适合需要和原生深度集成的项目。
方案3:使用第三方路由库
如果你的项目已经使用了go_router、auto_route这类成熟的路由库,它们大多内置了路由状态持久化的能力,或者可以通过扩展轻松实现。
以go_router为例,你可以结合本地存储和redirect机制恢复路由栈:
import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:shared_preferences/shared_preferences.dart'; final prefs = await SharedPreferences.getInstance(); final router = GoRouter( initialLocation: '/', observers: [ // 自定义Observer保存路由栈 NavigatorObserver( didPush: (route, previous) => _saveRouteStack(), didPop: (route, previous) => _saveRouteStack(), ), ], routes: [ GoRoute( path: '/', builder: (context, state) => const HomePage(), routes: [ GoRoute( path: 'details', builder: (context, state) => DetailsPage(args: state.extra), ), ], ), ], redirect: (context, state) async { final savedStackJson = prefs.getString('go_route_stack'); if (savedStackJson != null && state.location == '/') { final savedStack = (jsonDecode(savedStackJson) as List).cast<String>(); // 跳转到栈的最后一个路由 return savedStack.last; } return null; }, ); void _saveRouteStack() { final locationList = router.routeInformationProvider.value.uri.pathSegments; prefs.setString('go_route_stack', jsonEncode(locationList)); }
第三方库的优势是封装完善,减少重复造轮子的工作量,适合中大型项目。
内容的提问来源于stack exchange,提问作者h0102




