You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

开启安卓「不保留活动」后,Flutter应用路由栈如何保持或恢复?

解决Flutter在Android「不保留活动」下的路由栈恢复问题

这个问题我之前做测试的时候也碰到过——开启「不保留活动」后,Flutter应用重启就直接回到首页,之前的路由栈全丢了,确实挺影响体验的。下面分享几个经过验证的解决方案,你可以根据项目情况选择:

方案1:自定义NavigatorObserver持久化路由栈

核心思路是监听路由的跳转、返回操作,把当前路由栈的信息(名称、参数)保存到本地存储(比如SharedPreferencesHive),待应用重启时读取并恢复。

实现步骤:

  1. 自定义一个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));
  }
}
  1. 应用初始化时读取存储的路由栈并恢复:
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中重写onSaveInstanceStateonRestoreInstanceState,通过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_routerauto_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

火山引擎 最新活动