Flutter中文网

Flutter中文网

Flutter类微信小程序,InAppWebview关闭后保持状态KeepAlive

31
2024-07-13

最近接到一个需求,Flutter应用中用户打开一些Web3的网页游戏加载比较耗时,希望临时退出的时候可以像微信小程序一样,网页可以恢复退出前的状态,最好可以做到后台挂机。

实现方式

恰好前几个月更新InAppWebview6.0版本插件的时候,看到一个新特KeepAlive 。官方解释是:该功能用于在从布局树中移除与之对应的 Flutter 组件后,使原生 WebView 继续在后台运行,而不是将其销毁。

关键属性keepAlive 的值是一个InAppWebViewKeepAlive实例,所有使用相同实例的其它 InAppWebView 都将以相同的设置渲染相同的原生 WebView。

每个  InAppWebViewKeepAlive  实例只能有一个活跃的  InAppWebView  。 试图将同一个  InAppWebViewKeepAlive  实例用于两个活跃的  InAppWebView  会导致崩溃。

示例

下面是一个使用相同的 InAppWebViewKeepAlive 在两个不同的页面中渲染相同WebView的例子。 当你从页面A切换到页面B或反之亦然时, InAppWebView 会重新加载相同的原生WebView而不会重新加载,并保持当前WebView的原生状态(当前URL、滚动位置等),就好像你从未从Flutter组件树中删除它一样。

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';

final keepAlive = InAppWebViewKeepAlive();

Future main() async {
  WidgetsFlutterBinding.ensureInitialized();

  if (!kIsWeb && defaultTargetPlatform == TargetPlatform.android) {
    await InAppWebViewController.setWebContentsDebuggingEnabled(kDebugMode);
  }

  runApp(const MaterialApp(home: PageA()));
}

class PageA extends StatelessWidget {
  const PageA({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Page A - Keep Alive Example'),
        actions: [
          ElevatedButton(
              onPressed: () {
                Navigator.of(context).pushReplacement(
                  MaterialPageRoute(
                    builder: (context) => const PageB(),
                  ),
                );
              },
              child: const Text('Go To Page B'))
        ],
      ),
      body: Column(children: <Widget>[
        Expanded(
          child: InAppWebView(
            keepAlive: keepAlive,
            initialUrlRequest:
                URLRequest(url: WebUri("http://github.com/flutter")),
            initialSettings: InAppWebViewSettings(
              isInspectable: kDebugMode,
            ),
          ),
        )
      ]),
    );
  }
}

class PageB extends StatelessWidget {
  const PageB({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Page B - Keep Alive Example'),
        actions: [
          ElevatedButton(
              onPressed: () {
                Navigator.of(context).pushReplacement(
                  MaterialPageRoute(
                    builder: (context) => const PageA(),
                  ),
                );
              },
              child: const Text('Go To Page A'))
        ],
      ),
      body: Column(children: <Widget>[
        Expanded(
          child: InAppWebView(
            keepAlive: keepAlive,
          ),
        )
      ]),
    );
  }
}

结合咱们的需求,只需要首次创建Webview的时候创建InAppWebViewKeepAlive 实例,退出页面后保存该实例,恢复页面的时候使用保存的实例恢复。

注意事项

  1. 恢复WebView的时候无需设置initialUrlRequest ,即使设置了也不会生效。

  2. 使用同一个InAppWebViewKeepAlive 实例的页面,只有一个能处于活跃状态,否则会引发崩溃。

  3. 由于我的需求要求很低,没做过多测试。目前发现网页进入后台,似乎计数器还在工作,音乐停止了。更多细节比如内存占用等需要自行研究,这里只是提供一个思路。

  4. 这点非常重要!保持状态只能保持InAppWebview这个Widget的状态,页面内部的其他自定义的状态并不会保存,需要自己处理,建议页面和状态分离比较好操作。