Flutter for OpenHarmony三方库适配实战:webview_flutter 网页视图

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
本文基于flutter3.27.5开发

一、webview_flutter 库概述

WebView 是移动应用开发中展示网页内容的核心组件,无论是展示富文本内容、嵌入 H5 页面、还是实现混合开发,都需要用到 WebView 组件。在 Flutter for OpenHarmony 应用开发中,webview_flutter 是一个功能强大的 WebView 插件,提供了完整的网页渲染和交互能力。

webview_flutter 库特点

webview_flutter 库基于 Flutter 平台接口实现,提供了以下核心特性:

完整的网页渲染:支持加载 URL、HTML 字符串、本地文件、Flutter 资源等多种内容源,满足各种网页展示需求。

JavaScript 交互:支持 JavaScript 执行、JavaScript 通道通信、JavaScript 对话框处理,实现 Flutter 与网页的双向通信。

导航控制:提供完整的导航代理机制,支持拦截导航请求、控制页面跳转、处理加载进度等。

Cookie 管理:提供独立的 Cookie 管理器,支持 Cookie 的读取、设置、清除等操作。

权限处理:支持处理网页的权限请求,如摄像头、麦克风、地理位置等权限。

支持平台对比

平台 支持情况 底层实现
Android ✅ 完全支持 WebView
iOS ✅ 完全支持 WKWebView
Web ✅ 完全支持 iframe
macOS ✅ 完全支持 WKWebView
Windows ✅ 完全支持 WebView2
Linux ✅ 完全支持 WebKitGTK
OpenHarmony ✅ 完全支持 Web组件

功能支持对比

功能 Android iOS OpenHarmony
加载 URL
加载 HTML
加载本地文件
JavaScript 执行
JavaScript 通道
Cookie 管理
导航代理
权限请求
自定义请求头

使用场景:嵌入 H5 页面、展示富文本内容、混合开发、网页应用壳等。


二、安装与配置

2.1 添加依赖

在项目的 pubspec.yaml 文件中添加 webview_flutter 依赖:

yaml 复制代码
dependencies:
  webview_flutter:
    git:
      url: https://atomgit.com/openharmony-tpc/flutter_packages.git
      path: packages/webview_flutter/webview_flutter

然后执行以下命令获取依赖:

bash 复制代码
flutter pub get

2.2 兼容性信息

项目 版本要求
Flutter SDK 3.7.12-ohos-1.0.6
OpenHarmony SDK 5.0.0 (API 12)
DevEco Studio 5.0.13.200
ROM 5.1.0.120 SP3

2.3 权限配置

webview_flutter 在 OpenHarmony 平台上需要配置网络权限:

在 entry 目录下的 module.json5 中添加权限

打开 ohos/entry/src/main/module.json5,在 requestPermissions 数组中添加:

json 复制代码
"requestPermissions": [
  {
    "name": "ohos.permission.INTERNET",
    "reason": "$string:network_reason",
    "usedScene": {
      "abilities": [
        "EntryAbility"
      ],
      "when": "inuse"
    }
  }
]
在 entry 目录下添加申请权限的原因

打开 ohos/entry/src/main/resources/base/element/string.json,在 string 数组中添加:

json 复制代码
{
  "string": [
    {
      "name": "network_reason",
      "value": "使用网络访问文件资源"
    }
  ]
}

三、核心 API 详解

3.1 WebViewController 类

WebViewController 是 WebView 的核心控制器,负责管理 WebView 的所有操作。

创建控制器

dart 复制代码
final WebViewController controller = WebViewController();

设置 JavaScript 模式

dart 复制代码
controller.setJavaScriptMode(JavaScriptMode.unrestricted);

JavaScriptMode.unrestricted 表示启用 JavaScript,JavaScriptMode.disabled 表示禁用 JavaScript。

加载 URL

dart 复制代码
controller.loadRequest(Uri.parse('https://flutter.dev'));

加载 HTML 字符串

dart 复制代码
controller.loadHtmlString('<html><body><h1>Hello WebView</h1></body></html>');

加载本地文件

dart 复制代码
controller.loadFile('/path/to/file.html');

加载 Flutter 资源

dart 复制代码
controller.loadFlutterAsset('assets/www/index.html');

导航操作

dart 复制代码
bool canGoBack = await controller.canGoBack();
bool canGoForward = await controller.canGoForward();
await controller.goBack();
await controller.goForward();
await controller.reload();

获取当前 URL

dart 复制代码
String? currentUrl = await controller.currentUrl();

获取页面标题

dart 复制代码
String? title = await controller.getTitle();

执行 JavaScript

dart 复制代码
await controller.runJavaScript('alert("Hello from Flutter")');
Object result = await controller.runJavaScriptReturningResult('document.title');

3.2 WebViewWidget 类

WebViewWidget 是用于显示 WebView 的 Widget,需要传入 WebViewController

dart 复制代码
WebViewWidget(controller: controller)

完整示例

dart 复制代码
class WebViewPage extends StatefulWidget {
  const WebViewPage({super.key});

  @override
  State<WebViewPage> createState() => _WebViewPageState();
}

class _WebViewPageState extends State<WebViewPage> {
  late final WebViewController _controller;

  @override
  void initState() {
    super.initState();
    _controller = WebViewController()
      ..setJavaScriptMode(JavaScriptMode.unrestricted)
      ..loadRequest(Uri.parse('https://flutter.dev'));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('WebView')),
      body: WebViewWidget(controller: _controller),
    );
  }
}

NavigationDelegate 用于处理导航事件,包括页面开始加载、加载完成、加载错误、导航请求等。

dart 复制代码
final NavigationDelegate delegate = NavigationDelegate(
  onNavigationRequest: (NavigationRequest request) {
    if (request.url.contains('blocked-site.com')) {
      return NavigationDecision.prevent;
    }
    return NavigationDecision.navigate;
  },
  onPageStarted: (String url) {
    print('页面开始加载: $url');
  },
  onPageFinished: (String url) {
    print('页面加载完成: $url');
  },
  onProgress: (int progress) {
    print('加载进度: $progress%');
  },
  onWebResourceError: (WebResourceError error) {
    print('加载错误: ${error.description}');
  },
);

controller.setNavigationDelegate(delegate);

NavigationDecision 枚举

NavigationDecision.navigate 表示允许导航。

NavigationDecision.prevent 表示阻止导航。

3.4 JavaScriptChannel 类

JavaScriptChannel 用于建立 Flutter 与网页之间的通信通道。

dart 复制代码
controller.addJavaScriptChannel(
  'Toaster',
  onMessageReceived: (JavaScriptMessage message) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text(message.message)),
    );
  },
);

在网页中调用:

javascript 复制代码
Toaster.postMessage('Hello from WebView');

3.5 WebViewCookieManager 类

WebViewCookieManager 用于管理 WebView 的 Cookie。

dart 复制代码
final WebViewCookieManager cookieManager = WebViewCookieManager();

await cookieManager.setCookie(
  WebViewCookie(
    name: 'session',
    value: 'abc123',
    domain: 'example.com',
    path: '/',
  ),
);

List<WebViewCookie> cookies = await cookieManager.getCookies('https://example.com');
await cookieManager.clearCookies();

四、实战案例

4.1 基础 WebView 页面

dart 复制代码
class BasicWebViewPage extends StatefulWidget {
  const BasicWebViewPage({super.key});

  @override
  State<BasicWebViewPage> createState() => _BasicWebViewPageState();
}

class _BasicWebViewPageState extends State<BasicWebViewPage> {
  late final WebViewController _controller;
  bool _isLoading = true;

  @override
  void initState() {
    super.initState();
    _controller = WebViewController()
      ..setJavaScriptMode(JavaScriptMode.unrestricted)
      ..setNavigationDelegate(NavigationDelegate(
        onPageStarted: (_) => setState(() => _isLoading = true),
        onPageFinished: (_) => setState(() => _isLoading = false),
      ))
      ..loadRequest(Uri.parse('https://flutter.dev'));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('基础 WebView')),
      body: Stack(
        children: [
          WebViewWidget(controller: _controller),
          if (_isLoading)
            const Center(child: CircularProgressIndicator()),
        ],
      ),
    );
  }
}

4.2 带导航控制的 WebView

dart 复制代码
class NavigationWebViewPage extends StatefulWidget {
  const NavigationWebViewPage({super.key});

  @override
  State<NavigationWebViewPage> createState() => _NavigationWebViewPageState();
}

class _NavigationWebViewPageState extends State<NavigationWebViewPage> {
  late final WebViewController _controller;
  bool _canGoBack = false;
  bool _canGoForward = false;

  @override
  void initState() {
    super.initState();
    _controller = WebViewController()
      ..setJavaScriptMode(JavaScriptMode.unrestricted)
      ..setNavigationDelegate(NavigationDelegate(
        onPageFinished: (_) async {
          setState(() {
            _canGoBack = _controller.canGoBack() as bool;
            _canGoForward = _controller.canGoForward() as bool;
          });
        },
      ))
      ..loadRequest(Uri.parse('https://flutter.dev'));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('导航控制'),
        actions: [
          IconButton(
            icon: const Icon(Icons.arrow_back),
            onPressed: _canGoBack ? () => _controller.goBack() : null,
          ),
          IconButton(
            icon: const Icon(Icons.arrow_forward),
            onPressed: _canGoForward ? () => _controller.goForward() : null,
          ),
          IconButton(
            icon: const Icon(Icons.refresh),
            onPressed: () => _controller.reload(),
          ),
        ],
      ),
      body: WebViewWidget(controller: _controller),
    );
  }
}

4.3 Flutter 与 WebView 通信

dart 复制代码
class CommunicationWebViewPage extends StatefulWidget {
  const CommunicationWebViewPage({super.key});

  @override
  State<CommunicationWebViewPage> createState() => _CommunicationWebViewPageState();
}

class _CommunicationWebViewPageState extends State<CommunicationWebViewPage> {
  late final WebViewController _controller;

  @override
  void initState() {
    super.initState();
    _controller = WebViewController()
      ..setJavaScriptMode(JavaScriptMode.unrestricted)
      ..addJavaScriptChannel(
        'FlutterChannel',
        onMessageReceived: (JavaScriptMessage message) {
          ScaffoldMessenger.of(context).showSnackBar(
            SnackBar(content: Text('收到消息: ${message.message}')),
          );
        },
      )
      ..loadHtmlString('''
        <html>
          <body>
            <h1>Flutter 与 WebView 通信</h1>
            <button onclick="sendMessage()">发送消息到 Flutter</button>
            <script>
              function sendMessage() {
                FlutterChannel.postMessage('Hello from WebView!');
              }
            </script>
          </body>
        </html>
      ''');
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('通信示例')),
      body: WebViewWidget(controller: _controller),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          _controller.runJavaScript('document.body.style.backgroundColor = "lightblue"');
        },
        child: const Icon(Icons.color_lens),
      ),
    );
  }
}

4.4 拦截特定 URL

dart 复制代码
class BlockedUrlWebViewPage extends StatefulWidget {
  const BlockedUrlWebViewPage({super.key});

  @override
  State<BlockedUrlWebViewPage> createState() => _BlockedUrlWebViewPageState();
}

class _BlockedUrlWebViewPageState extends State<BlockedUrlWebViewPage> {
  late final WebViewController _controller;

  @override
  void initState() {
    super.initState();
    _controller = WebViewController()
      ..setJavaScriptMode(JavaScriptMode.unrestricted)
      ..setNavigationDelegate(NavigationDelegate(
        onNavigationRequest: (NavigationRequest request) {
          if (request.url.contains('youtube.com')) {
            ScaffoldMessenger.of(context).showSnackBar(
              const SnackBar(content: Text('已阻止访问 YouTube')),
            );
            return NavigationDecision.prevent;
          }
          return NavigationDecision.navigate;
        },
      ))
      ..loadRequest(Uri.parse('https://flutter.dev'));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('URL 拦截')),
      body: WebViewWidget(controller: _controller),
    );
  }
}

五、最佳实践

5.1 处理加载状态

始终显示加载指示器,提升用户体验:

dart 复制代码
class LoadingIndicatorWebView extends StatefulWidget {
  const LoadingIndicatorWebView({super.key, required this.url});
  final String url;

  @override
  State<LoadingIndicatorWebView> createState() => _LoadingIndicatorWebViewState();
}

class _LoadingIndicatorWebViewState extends State<LoadingIndicatorWebView> {
  late final WebViewController _controller;
  int _loadingProgress = 0;

  @override
  void initState() {
    super.initState();
    _controller = WebViewController()
      ..setJavaScriptMode(JavaScriptMode.unrestricted)
      ..setNavigationDelegate(NavigationDelegate(
        onProgress: (int progress) {
          setState(() => _loadingProgress = progress);
        },
      ))
      ..loadRequest(Uri.parse(widget.url));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('WebView'),
        bottom: PreferredSize(
          preferredSize: const Size.fromHeight(3),
          child: LinearProgressIndicator(
            value: _loadingProgress / 100,
            backgroundColor: Colors.transparent,
          ),
        ),
      ),
      body: WebViewWidget(controller: _controller),
    );
  }
}

5.2 错误处理

妥善处理加载错误:

dart 复制代码
controller.setNavigationDelegate(NavigationDelegate(
  onWebResourceError: (WebResourceError error) {
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text('加载失败'),
        content: Text('错误代码: ${error.errorCode}\n错误信息: ${error.description}'),
        actions: [
          TextButton(
            onPressed: () {
              Navigator.pop(context);
              controller.reload();
            },
            child: const Text('重试'),
          ),
        ],
      ),
    );
  },
));

5.3 处理权限请求

处理网页的权限请求:

dart 复制代码
controller = WebViewController(
  onPermissionRequest: (WebViewPermissionRequest request) {
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text('权限请求'),
        content: Text('网页请求以下权限: ${request.types.map((t) => t.name).join(', ')}'),
        actions: [
          TextButton(
            onPressed: () {
              request.deny();
              Navigator.pop(context);
            },
            child: const Text('拒绝'),
          ),
          TextButton(
            onPressed: () {
              request.grant();
              Navigator.pop(context);
            },
            child: const Text('允许'),
          ),
        ],
      ),
    );
  },
);

六、常见问题

Q1:如何获取 WebView 内容高度?

dart 复制代码
Future<double> getContentHeight() async {
  final result = await controller.runJavaScriptReturningResult(
    'document.body.scrollHeight',
  );
  return double.parse(result.toString());
}

Q2:如何注入自定义 CSS?

dart 复制代码
await controller.runJavaScript('''
  var style = document.createElement('style');
  style.innerHTML = 'body { background-color: #f0f0f0; }';
  document.head.appendChild(style);
''');

Q3:如何处理文件下载?

WebView 本身不直接支持文件下载,需要通过导航代理拦截下载链接:

dart 复制代码
controller.setNavigationDelegate(NavigationDelegate(
  onNavigationRequest: (request) {
    if (request.url.endsWith('.pdf') || request.url.endsWith('.zip')) {
      downloadFile(request.url);
      return NavigationDecision.prevent;
    }
    return NavigationDecision.navigate;
  },
));

Q4:如何清除缓存?

dart 复制代码
await controller.clearCache();
await controller.clearLocalStorage();

七、总结

webview_flutter 库为 Flutter for OpenHarmony 开发提供了完整的 WebView 功能。通过 WebViewController、WebViewWidget、NavigationDelegate 等核心类,开发者可以轻松实现网页展示、JavaScript 交互、导航控制、Cookie 管理等功能。


八、完整代码示例

以下是一个完整的可运行示例,展示了 webview_flutter 库的核心功能:

pubspec.yaml

yaml 复制代码
name: webview_flutter_demo
description: Flutter for OpenHarmony webview_flutter 演示项目
publish_to: 'none'
version: 1.0.0+1

environment:
  sdk: '>=3.0.0 <4.0.0'

dependencies:
  flutter:
    sdk: flutter
  webview_flutter:
    git:
      url: https://atomgit.com/openharmony-tpc/flutter_packages.git
      path: packages/webview_flutter/webview_flutter

flutter:
  uses-material-design: true
  assets:
    - assets/www/

assets/www/index.html

html 复制代码
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>WebView Demo</title>
  <style>
    body {
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
      padding: 20px;
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      min-height: 100vh;
      margin: 0;
    }
    .container {
      background: white;
      border-radius: 16px;
      padding: 24px;
      box-shadow: 0 10px 40px rgba(0,0,0,0.2);
    }
    h1 {
      color: #333;
      margin-bottom: 20px;
    }
    button {
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      color: white;
      border: none;
      padding: 12px 24px;
      border-radius: 8px;
      font-size: 16px;
      cursor: pointer;
      margin: 8px 0;
      width: 100%;
    }
    button:active {
      transform: scale(0.98);
    }
    #output {
      background: #f5f5f5;
      padding: 16px;
      border-radius: 8px;
      margin-top: 16px;
      min-height: 60px;
    }
  </style>
</head>
<body>
  <div class="container">
    <h1>WebView 通信演示</h1>
    <p>点击按钮向 Flutter 发送消息</p>
    <button onclick="sendToFlutter()">发送消息到 Flutter</button>
    <button onclick="changeBackground()">改变背景色</button>
    <button onclick="showInfo()">获取设备信息</button>
    <div id="output">等待操作...</div>
  </div>
  
  <script>
    function sendToFlutter() {
      FlutterChannel.postMessage('来自 WebView 的问候!时间: ' + new Date().toLocaleTimeString());
      document.getElementById('output').innerText = '消息已发送到 Flutter';
    }
  
    function changeBackground() {
      const colors = ['#667eea', '#764ba2', '#f093fb', '#f5576c', '#4facfe'];
      const randomColor = colors[Math.floor(Math.random() * colors.length)];
      document.body.style.background = 'linear-gradient(135deg, ' + randomColor + ' 0%, ' + colors[(colors.indexOf(randomColor) + 1) % colors.length] + ' 100%)';
      document.getElementById('output').innerText = '背景色已改变';
    }
  
    function showInfo() {
      const info = 'User Agent: ' + navigator.userAgent + '\n屏幕: ' + screen.width + 'x' + screen.height;
      document.getElementById('output').innerText = info;
    }
  </script>
</body>
</html>

main.dart

dart 复制代码
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'WebView Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const HomePage(),
    );
  }
}

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

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  late final WebViewController _controller;
  int _loadingProgress = 0;
  String _currentUrl = '';
  bool _canGoBack = false;
  bool _canGoForward = false;

  @override
  void initState() {
    super.initState();
    _initController();
  }

  void _initController() {
    _controller = WebViewController()
      ..setJavaScriptMode(JavaScriptMode.unrestricted)
      ..setBackgroundColor(Colors.white)
      ..setNavigationDelegate(NavigationDelegate(
        onProgress: (int progress) {
          setState(() => _loadingProgress = progress);
        },
        onPageStarted: (String url) {
          setState(() => _currentUrl = url);
        },
        onPageFinished: (String url) async {
          setState(() {
            _canGoBack = await _controller.canGoBack();
            _canGoForward = await _controller.canGoForward();
          });
        },
        onWebResourceError: (WebResourceError error) {
          debugPrint('WebView error: ${error.description}');
        },
        onNavigationRequest: (NavigationRequest request) {
          if (request.url.contains('blocked')) {
            ScaffoldMessenger.of(context).showSnackBar(
              const SnackBar(content: Text('已阻止访问该页面')),
            );
            return NavigationDecision.prevent;
          }
          return NavigationDecision.navigate;
        },
      ))
      ..addJavaScriptChannel(
        'FlutterChannel',
        onMessageReceived: (JavaScriptMessage message) {
          ScaffoldMessenger.of(context).showSnackBar(
            SnackBar(
              content: Text(message.message),
              duration: const Duration(seconds: 3),
            ),
          );
        },
      )
      ..loadFlutterAsset('assets/www/index.html');
  }

  Future<void> _goBack() async {
    if (await _controller.canGoBack()) {
      await _controller.goBack();
    }
  }

  Future<void> _goForward() async {
    if (await _controller.canGoForward()) {
      await _controller.goForward();
    }
  }

  Future<void> _reload() async {
    await _controller.reload();
  }

  Future<void> _loadUrl(String url) async {
    await _controller.loadRequest(Uri.parse(url));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('WebView 演示'),
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        actions: [
          IconButton(
            icon: const Icon(Icons.home),
            onPressed: () => _loadUrl('https://flutter.dev'),
            tooltip: 'Flutter 官网',
          ),
          PopupMenuButton<String>(
            onSelected: (String value) => _loadUrl(value),
            itemBuilder: (BuildContext context) => [
              const PopupMenuItem(
                value: 'https://flutter.dev',
                child: Text('Flutter 官网'),
              ),
              const PopupMenuItem(
                value: 'https://pub.dev',
                child: Text('Pub.dev'),
              ),
              const PopupMenuItem(
                value: 'https://github.com',
                child: Text('GitHub'),
              ),
            ],
          ),
        ],
        bottom: PreferredSize(
          preferredSize: const Size.fromHeight(4),
          child: LinearProgressIndicator(
            value: _loadingProgress / 100,
            backgroundColor: Colors.transparent,
          ),
        ),
      ),
      body: Column(
        children: [
          Container(
            padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
            color: Colors.grey.shade100,
            child: Row(
              children: [
                Expanded(
                  child: Text(
                    _currentUrl.isEmpty ? '加载中...' : _currentUrl,
                    style: const TextStyle(fontSize: 12, color: Colors.grey),
                    overflow: TextOverflow.ellipsis,
                  ),
                ),
                Text(
                  '$_loadingProgress%',
                  style: const TextStyle(fontSize: 12, color: Colors.grey),
                ),
              ],
            ),
          ),
          Expanded(
            child: WebViewWidget(controller: _controller),
          ),
        ],
      ),
      bottomNavigationBar: BottomAppBar(
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: [
            IconButton(
              icon: const Icon(Icons.arrow_back),
              onPressed: _canGoBack ? _goBack : null,
              tooltip: '后退',
            ),
            IconButton(
              icon: const Icon(Icons.arrow_forward),
              onPressed: _canGoForward ? _goForward : null,
              tooltip: '前进',
            ),
            IconButton(
              icon: const Icon(Icons.refresh),
              onPressed: _reload,
              tooltip: '刷新',
            ),
            IconButton(
              icon: const Icon(Icons.code),
              onPressed: () {
                _controller.runJavaScript('''
                  FlutterChannel.postMessage('执行了 JavaScript 代码');
                ''');
              },
              tooltip: '执行 JS',
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () async {
          final String? title = await _controller.getTitle();
          if (mounted && title != null) {
            ScaffoldMessenger.of(context).showSnackBar(
              SnackBar(content: Text('页面标题: $title')),
            );
          }
        },
        child: const Icon(Icons.title),
      ),
    );
  }
}

九、参考资源

相关推荐
里欧跑得慢2 小时前
Flutter 导航路由:构建流畅的应用导航体验
前端·css·flutter·web
李李李勃谦3 小时前
Flutter 框架跨平台鸿蒙开发 - 废话生成器
flutter·华为·harmonyos
2301_822703203 小时前
开源鸿蒙跨平台Flutter开发:非侵入式血压预估:基于 HRV 与脉搏波的建模与实现
flutter·开源·harmonyos
一直在想名4 小时前
Flutter 框架跨平台鸿蒙开发 - 胶片相机模拟
数码相机·flutter·华为·harmonyos
世人万千丶5 小时前
开源鸿蒙跨平台Flutter开发:儿童数理认知与神经塑性演化引擎_突触发生与工作记忆测绘架构
学习·flutter·华为·开源·harmonyos
Utopia^5 小时前
Flutter 框架跨平台鸿蒙开发 - 时光倒流
服务器·flutter·华为·harmonyos
2501_921930835 小时前
Flutter for OpenHarmony三方库适配实战:image_picker 图片视频选择
flutter·音视频
独特的螺狮粉6 小时前
开源鸿蒙跨平台Flutter开发:量子态波函数坍缩系统-波动力学与概率云渲染架构
开发语言·flutter·华为·架构·开源·harmonyos
浮芷.6 小时前
Flutter 框架跨平台鸿蒙开发 - 思维录音机应用
flutter·华为·harmonyos