Flutter Desktop Mac版(二) 插件初探

文章目录

上篇粗略的查看了一下 desktop 的基本使用, 本篇探索一下插件的使用

环境变量的配置请查看上篇,本篇不再赘述

更新

继上一篇文章过去了几天, 这个桌面引擎有了一点点的更改: 现在完全用 swift 了,不用 oc 了,无论是 example 还是 plugin 模板都是如此

所以, 为了省事,我重新 clone 了一个仓库, 然后准备用 swift 来创建插件

$ git clone https://github.com/google/flutter-desktop-embedding.git flutter-desktop-embedding-2

然后分别打开工程和插件 $ code flutter-desktop-embedding-2/example/ and $ code flutter-desktop-embedding-2/plugins/example_plugin

这个 example_plugin 是给的模板, 最好是复制一份出来

复制插件目录

1cd flutter-desktop-embedding-2/plugins
2cp -r example_plugin math_desktop
3code math_desktop

查看一下目录结构

 1tree math_desktop
 2
 3math_desktop
 4├── LICENSE
 5├── lib
 6│   └── example_plugin.dart
 7├── linux
 8│   ├── Makefile
 9│   ├── example_plugin.cc
10│   └── example_plugin.h
11├── macos
12│   ├── Classes
13│   │   └── ExamplePlugin.swift
14│   └── example_plugin.podspec
15├── pubspec.yaml
16└── windows
17    ├── ExamplePlugin.vcxproj
18    ├── ExamplePlugin.vcxproj.filters
19    ├── example_plugin.cpp
20    ├── example_plugin.h
21    └── scripts
22        └── cache_flutter.bat

基本是约定式的,和移动版的很类似

pubspec.yaml 在根目录,lib 放 dart 文件,然后根据 Platform 的不同使用不同的文件

依赖插件并打开插件

修改 example 文件,添加依赖

1dependencies:
2  flutter:
3    sdk: flutter
4
5  cupertino_icons: ^0.1.0
6  math_desktop: # add it
7    path: ../plugins/math_desktop

然后在 example 目录下执行flutter pub get, 这个是flutter packages get的另一种写法, 比较简单点

接着进入 macos 目录下,执行$ pod install

等待完成后 执行 $ open Runner.xcworkspace, 这样正常情况下会用 xcode 打开这个工程

接着出现的就和 iOS 的插件差不多一样了 是这个 SHI 样的:

20190612171224.png

需要简化目录结构的可以搜索我写的 appcode 索引那个文章, 有简化方式,我这里不管它了

编写插件

dart

插件目录下的 lib 文件夹

添加一个方法

1  static Future<int> add(int a, int b) async {
2    return _channel.invokeMethod('add', [a, b]);
3  }

swift

很显然,中间那段是我自己加的,没什么实际意义, 就为了演示而已

 1 public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
 2    if (call.method == "getPlatformVersion") {
 3      result("macOS " + ProcessInfo.processInfo.operatingSystemVersionString)
 4    } else if(call.method == "add"){
 5        let args = call.arguments as! [Any]
 6        let a = args[0] as! Int
 7        let b = args[1] as! Int
 8        result(a + b)
 9    } else {
10      result(FlutterMethodNotImplemented);
11    }
12  }

调用

在 example 中调用这个方法

 1FutureBuilder<int>(
 2    future: ExamplePlugin.add(_counter, 5),
 3    builder: (context, snapshot) {
 4        if (snapshot.hasData) {
 5            return Text(snapshot.data.toString());
 6        } else {
 7            return Text('计算中');
 8        }
 9    },
10)

目前完整的 example 部分的代码如下:

 1// Copyright 2018 Google LLC
 2//
 3// Licensed under the Apache License, Version 2.0 (the "License");
 4// you may not use this file except in compliance with the License.
 5// You may obtain a copy of the License at
 6//
 7//      http://www.apache.org/licenses/LICENSE-2.0
 8//
 9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15import 'package:flutter/foundation.dart'
16    show debugDefaultTargetPlatformOverride;
17import 'package:flutter/material.dart';
18import 'package:math_desktop/example_plugin.dart';
19
20void main() {
21  // See https://github.com/flutter/flutter/wiki/Desktop-shells#target-platform-override
22  debugDefaultTargetPlatformOverride = TargetPlatform.fuchsia;
23
24  runApp(new MyApp());
25}
26
27class MyApp extends StatelessWidget {
28  @override
29  Widget build(BuildContext context) {
30    return MaterialApp(
31      title: 'Flutter Demo',
32      theme: ThemeData(
33        primarySwatch: Colors.blue,
34        // See https://github.com/flutter/flutter/wiki/Desktop-shells#fonts
35        fontFamily: 'Roboto',
36      ),
37      home: MyHomePage(title: 'Flutter Demo Home Page'),
38    );
39  }
40}
41
42class MyHomePage extends StatefulWidget {
43  MyHomePage({Key key, this.title}) : super(key: key);
44
45  final String title;
46
47  @override
48  _MyHomePageState createState() => _MyHomePageState();
49}
50
51class _MyHomePageState extends State<MyHomePage> {
52  int _counter = 0;
53
54  void _incrementCounter() {
55    setState(() {
56      _counter++;
57    });
58  }
59
60  @override
61  Widget build(BuildContext context) {
62    return Scaffold(
63      appBar: AppBar(
64        title: Text(widget.title),
65      ),
66      body: Center(
67        child: Column(
68          mainAxisAlignment: MainAxisAlignment.center,
69          children: <Widget>[
70            Text(
71              'You have pushed the button this many times:',
72            ),
73            Text(
74              '$_counter',
75              style: Theme.of(context).textTheme.display1,
76            ),
77            FutureBuilder<int>(
78              future: ExamplePlugin.add(_counter, 5),
79              builder: (context, snapshot) {
80                if (snapshot.hasData) {
81                  return Text(snapshot.data.toString());
82                } else {
83                  return Text('计算中');
84                }
85              },
86            ),
87          ],
88        ),
89      ),
90      floatingActionButton: FloatingActionButton(
91        onPressed: _incrementCounter,
92        tooltip: 'Increment',
93        child: Icon(Icons.add),
94      ),
95    );
96  }
97}

运行代码

这里会发现这种错误

1[ERROR:flutter/lib/ui/ui_dart_state.cc(148)] Unhandled Exception: MissingPluginException(No implementation found for method add on channel example_plugin)

首先我在 MathDesktop 类的注册方法中加入了 NSLog 的日志, 发现并没有输出, 所以应该是插件没有注册的原因

1public static func register(with registrar: FlutterPluginRegistrar) {
2  let channel = FlutterMethodChannel(name: "example_plugin", binaryMessenger: registrar.messenger)
3  let instance = MathDesktop()
4  registrar.addMethodCallDelegate(instance, channel: channel)
5  NSLog("初始插件 : example_plugin")
6}

据我观察并测试后, 原因是: 虽然迁移到了 swift, 但是相应的脚本或者说 flutter 的工具链没有迁移, 所以还是用的 oc 文件来注入, 但 oc 文件未被纳入到 flutter 项目中, 所以暂时还没法通过 flutter packages get 来自动引入原生的 plugin

所以需要手动修改example/macos/Flutter/GeneratedPluginRegistrant.swift文件,来注册插件

1import Foundation
2import FlutterMacOS
3import math_desktop
4
5func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
6    MathDesktop.register(with: registry.registrar(forPlugin: "math_desktop"))
7}

这样再次运行,就不会再报错了

可以得到正确的效果

20190613085127.png

简单总结

通过插件的编写, 简单总结一下:

  1. 插件的原生中引入的库不是 iOS 中的 Flutter, 而是 FlutterMacOS
  2. ViewController 的是 Cocoa 框架, 个人猜测不排除未来会变成 SwiftUI 的可能性
  3. 注册工具目前不太好用
  4. dart 端几乎无差,但是不排除会有一些特殊的手势或事件(鼠标滑过,键盘监听)

后记

仓库地址: 目前私有库,后续修改完成后放在 github 上

插件的简单使用就到这里

以上