Go Flutter Desktop (一) 初探

文章目录

Flutter 在去年的时候就有一个第三方的桌面引擎, 是用 golang 开发的

Github 地址是:https://github.com/go-flutter-desktop/go-flutter

目前在 mac,linux,windows 均可用, 作为一个 mac 用户, 除了 retina 下字显得有点小, 感觉没有单独适配外, 总体感觉是优于官方的 desktop 引擎的

另外我是真实的 golang 脑残粉, 我觉得 golang 这东西真的太好了, 用 golang, 准不会错

开发环境

需要的开发环境, 因为我是 MacOS, 我以 macOS 为例,其他的请参考对应的系统

  1. Xcode 命令行体系, 这个东西包含很多开发套件(Git), 无论你是否用 XCode 开发,都建议装一个...
  2. Flutter 环境和配套工具, 这个跑不掉,作为 flutter 开发者...
  3. go 语言环境(使用 brew 安装), 1.12+, IDE 用 Jetbrains 家的 goland (你用 VSCode 的话看你自己的情况)

环境安装

对 flutter 桌面版本感兴趣的一定接触过 flutter 开发, 我就默认你有 flutter 全套开发环境

go 语言环境安装

$ brew install go

如果你的 go 比较老, 请升级,使用$ brew upgrade go

配置 GOPATH 环境变量

$ vi ~/.bash_profile

1export GOPATH=~/code/go # 这个是必须配置的, 等号后的部分根据你的情况修改, 简单来说里面放的是你自己的代码,不是go的SDK,不是go的SDK,不是go的SDK, 具体的话是你 go 语言的三方库源码/自己写的go代码/中间产物/应用程序所在的目录
2PATH=$PATH:$GOPATH/bin  # 这个是选配, 但是强烈建议配置,不然以后的go工具链需要全路径引用

你下载的 go 相关的东西会被装在这个文件夹里

go-flutter 的环境

需要使用一个叫做 hover 的工具, 这个工具是由 go 编写的, 编译打包运行都使用这个工具

$ go get -u github.com/go-flutter-desktop/hover, 这样这个工具会被安装到$GOPATH/bin目录下

20190704163802.png

当你可以直接在命令行输入 hover 可以出现如下情况时就说明可用了

 1➜  ~ hover -h
 2Hover helps developers to release Flutter applications on desktop.
 3
 4Usage:
 5  hover [flags]
 6  hover [command]
 7
 8Available Commands:
 9  build       Build a desktop release
10  help        Help about any command
11  init        Initialize a flutter project to use go-flutter
12  run         Build and start a desktop release, with hot-reload support
13
14Flags:
15  -h, --help   help for hover
16
17Use "hover [command] --help" for more information about a command.
18➜  ~

安装 hover 出现问题的话可以参考

运行 example

官方提供了几个 example:

1cd /tmp
2git clone https://github.com/go-flutter-desktop/examples.git flutter-examples
3cd flutter-examples/pointer_demo
4flutter pub get
5hover run

通过以上几个步骤就可以把项目跑起来了

20190704165918.png

是一个关于鼠标移入移出监听的 demo

将原项目改为 desktop

官方说明文档是这样的, 不想看英文的, 可以跳过官方文档直接看我的中文说明

20190705150032.png

这里要注意, 因为插件系统的原因, 如果不是纯 dart 插件, 则插件内容不能用

我模拟一下这个过程, 创建一个+++的 helloworld 工程, 你如果是要改造已有项目, 则应该 cd 到你的 flutter 的根目录进行 $ hover init 项目url的操作, 这里根据官方说, url 无所谓, 后面可改

1flutter create flutter_example_1
2cd flutter_example_1
3flutter pub get
4hover init github.com/Caijinglong/flutter-go-example # 初始化 desktop 工程

这时候运行项目$ hover run会有一个提示: Target file "lib/main_desktop.dart" not found.

我们查询可知, 可能是考虑到兼容性的问题, go 引擎的项目使用 main_desktop.dart 作为入口, 我们创建一个文件

main_desktop.dart:

1import 'package:flutter/foundation.dart';
2import 'package:flutter/widgets.dart';
3
4import 'main.dart';
5
6void main() {
7  debugDefaultTargetPlatformOverride = TargetPlatform.fuchsia;
8  runApp(MyApp());
9}

$ hover run

20190704171705.png

这样项目就跑起来了

测试基础的项目

测试下常用的几项:

  1. 事件响应
  2. ListView
  3. 图片
  4. 输入框情况

事件响应

点击事件是可行的,数字可加, 说明鼠标事件能响应, 其他的长按双击等等都是 flutter 实现的, 理论上就不需要测试了

最开始的官方 demo 中有鼠标移入移出事件

ListView 滚动

 1import 'package:flutter/material.dart';
 2
 3class ListViewPage extends StatefulWidget {
 4  @override
 5  _ListViewPageState createState() => _ListViewPageState();
 6}
 7
 8class _ListViewPageState extends State<ListViewPage> {
 9  @override
10  Widget build(BuildContext context) {
11    return Scaffold(
12      appBar: AppBar(),
13      body: ListView.builder(
14        itemBuilder: _buildItem,
15      ),
16    );
17  }
18
19  Widget _buildItem(BuildContext context, int index) {
20    return ListTile(
21      title: Text(index.toString()),
22    );
23  }
24}

Kapture 2019-07-04 at 17.26.43.gif

没有移动端的惯性, 可以响应鼠标滚轮上下

改成横向的

1  Widget build(BuildContext context) {
2    return Scaffold(
3      appBar: AppBar(),
4      body: ListView.builder(
5        itemBuilder: _buildItem,
6        scrollDirection: Axis.horizontal,
7      ),
8    );
9  }

横向同样没惯性, shift+滚动可以横向

图片

网络图片

 1import 'package:flutter/material.dart';
 2
 3class ImagePage extends StatefulWidget {
 4  @override
 5  _ImagePageState createState() => _ImagePageState();
 6}
 7
 8class _ImagePageState extends State<ImagePage> {
 9  @override
10  Widget build(BuildContext context) {
11    return Scaffold(
12      appBar: AppBar(),
13      body: ListView(
14        children: <Widget>[
15          Container(
16            width: 500,
17            height: 500,
18            child: Image.network(
19                "https://raw.githubusercontent.com/kikt-blog/image/master/img/20190704171705.png"),
20          ),
21        ],
22      ),
23    );
24  }
25}

网络图片可行

20190704173327.png

文件

File 的图片, 直接使用本地图片就可以了, 我因为是 mac,所以是这样的,windows 可能是c:\\XXXX\\XXX\\X.jpg

1Container(
2  width: 500,
3  height: 500,
4  child: Image.file(File("/Users/cai/Desktop/auto-angle.jpg")),
5),

20190705092501.png

内存

memory , 这里需要模拟一下

读取上面的文件,然后转为 Uint8List

1Container(
2  width: 500,
3  height: 500,
4  child: Image.memory(
5    Uint8List.fromList(
6      File("/Users/cai/Desktop/auto-angle.jpg").readAsBytesSync(),
7    ),
8  ),
9),

资产

asset: 这种方式的加载我印象中去年这个引擎需要使用约定式文件夹, 与 flutter-web 的方式类似
而现在不需要这种方式了, 直接与 flutter 官方的方式一致,只需要在 pubspec.yaml 中配置即可

1flutter:
2  # The following line ensures that the Material Icons font is
3  # included with your application, so that you can use the icons in
4  # the material Icons class.
5  uses-material-design: true
6
7  # To add assets to your application, add an assets section, like this:
8  assets:
9    - assets/

20190705102154.png

1Container(
2  width: 500,
3  height: 500,
4  child: Image.asset(R.ASSETS_HAVE_EXIF_JPG),
5),
1/// generate by resouce_generator library, shouldn't edit.
2class R {
3  /// ![preview](file:///private/tmp/flutter-go-example/./assets/have-exif.jpg)
4  static const String ASSETS_HAVE_EXIF_JPG = "assets/have-exif.jpg";
5}

这里插入一句, 图片会根据 exif 信息旋转至正确的方向

20190705102408.png

但是图片多了后 ListView 的滚动性能似乎变差了

输入框

使用 Material 体系的 TextField 作为测试, Cupertino 和 Widget 体系的输入框请自行测试吧

有如下几个测试方向(有其他的需求可说, 我会加入)

  • 输入响应
  • 显示行为
  • 系统快捷键
  • 鼠标行为

输入响应

这个很好理解, 就是用键盘能否输入字符... 因为 flutter 上的官方的 plugin 就没法输入(我都是道听途说)

 1import 'package:flutter/material.dart';
 2
 3class InputPage extends StatefulWidget {
 4  @override
 5  _InputPageState createState() => _InputPageState();
 6}
 7
 8class _InputPageState extends State<InputPage> {
 9  @override
10  Widget build(BuildContext context) {
11    return Scaffold(
12      appBar: AppBar(title: Text('input')),
13      body: Container(
14        child: TextField(),
15      ),
16    );
17  }
18}

20190705102958.png

输入英文还算正常

试试中文:

20190705103106.png

文字位置正常,输入框没跟随

显示行为

单行没问题, 试试多行, 直接回车不行 我们需要将 TextField 设置为多行, 我这里分别设置 10/50 行

20190705103327.png

50 行的话,一页放不下,滚动也算正常

Kapture 2019-07-05 at 10.34.23.gif

但是这里有一个问题, 中英文混合的情况下, 水滴不正常

20190705103537.png

开头和结尾都是英文则没问题, 都是中文同理

20190705103646.png

20190705103635.png

系统快捷键+鼠标行为

常用的几个快捷键(复制,粘贴,全选)都是 OK 的, 基本行为和正常的输入框完全吻合,这里要给好评, 比官方桌面引擎好用多了, 其他系统的请自行测试

Kapture 2019-07-05 at 10.37.44.gif

鼠标行为顺便一起测试了,基本符合正常的操作习惯

插件

这个版本的插件和官方的不一样, 需要用 golang 去写, 而不是各自平台的, 当然如果各自平台有特殊的 api, 也需要使用 golang 去调用

总体有如下几个步骤:

  1. 创建插件
  2. 编写代码(go+dart)
  3. 引入插件

官方文档在此:

创建并编写插件

go 端

打开 goland, 或者其他的什么编辑器

具体的 golang 知识没法展开讲解,

可以理解为在 gopath 的 src 目录下创建一个包, 大部分情况下模仿别人, 建议放在 github.com 目录下

$ mkdir -p src/github.com/caijinglong/go-flutter-plugin/version

创建一个目录, 这个就是我插件的文件夹

version.go:

 1package version
 2
 3import (
 4    "github.com/go-flutter-desktop/go-flutter"
 5    "github.com/go-flutter-desktop/go-flutter/plugin"
 6)
 7
 8const (
 9    channelName = "top.kikt/go/version"
10    getVersion  = "getVersion"
11)
12
13type VersionPlugin struct{}
14
15var _ flutter.Plugin = &VersionPlugin{}
16
17func (VersionPlugin) InitPlugin(messenger plugin.BinaryMessenger) error {
18    channel := plugin.NewMethodChannel(messenger, channelName, plugin.StandardMethodCodec{})
19    channel.HandleFunc(getVersion, getVersionFunc)
20    return nil;
21}
22
23func getVersionFunc(arguments interface{}) (reply interface{}, err error) {
24    return "0.0.1", nil
25}

这个文件就是我们的插件, 必须要有的是结构体声明, 初始化插件的方法

下面那个getVersionFunc就是我们处理的方法, 这里可以使用 golang 编程,返回你需要通过 golang 获取的数据或任何东西, 我这里返回了一个简单的字符串

dart 端

1import 'package:flutter/services.dart';
2
3class GetVersionPlugin {
4  static const _channel = const MethodChannel("top.kikt/go/version");
5
6  static Future<String> get version async => _channel.invokeMethod("getVersion");
7}

发布插件

为了让我们的 flutter 应用可以找到这个插件, 需要一些配置

go-flutter 使用的是 go module 的方案管理的 go 包

我们需要如下几步

1cd $GOPATH/src/github.com/caijinglong/go-flutter-plugin/version
2export GO111MODULE=on
3go mod init github.com/caijinglong/go-flutter-plugin/version
4go mod tidy

目前我们的目录结构是这样的

1/Users/cai/code/go/src/github.com/caijinglong/go-flutter-plugin/version
2├── go.mod
3├── go.sum
4└── version.go

要想发布, 其实得发布到 github 上, 这样别人才能访问, 我们目前不这么做, 仅本地使用

引入插件

这里需要修改 desktop/cmd 目录下的 options.go 文件

 1package main
 2
 3import (
 4    "github.com/caijinglong/go-flutter-plugin/version"
 5    "github.com/go-flutter-desktop/go-flutter"
 6)
 7
 8var options = []flutter.Option{
 9    flutter.WindowInitialDimensions(800, 1280),
10    flutter.AddPlugin(version.VersionPlugin{}),
11}

这时候重新运行下项目会报一个错

1build github.com/Caijinglong/flutter-go-example/desktop/cmd: cannot load github.com/caijinglong/go-flutter-plugin/version: cannot find module providing package github.com/caijinglong/go-flutter-plugin/version

这个是因为插件没有发布到 github 所致, 我们先在本地测试下, 需要按照官方文档修改一下

 1module github.com/Caijinglong/flutter-go-example/desktop
 2
 3go 1.12
 4
 5require (
 6  github.com/go-flutter-desktop/go-flutter v0.24.1
 7  github.com/pkg/errors v0.8.1
 8  .com/stretchr/objx v0.2.0 // indirect
 9)
10
11replace github.com/caijinglong/go-flutter-plugin/version => /Users/cai/code/go/src/github.com/caijinglong/go-flutter-plugin/version // 添加这行

接着就可以运行了

运行

20190705111417.png

代码如下:

 1import 'package:flutter/material.dart';
 2
 3import 'get_version_plugin.dart';
 4
 5class PluginPage extends StatefulWidget {
 6  @override
 7  _PluginPageState createState() => _PluginPageState();
 8}
 9
10class _PluginPageState extends State<PluginPage> {
11  @override
12  Widget build(BuildContext context) {
13    return Scaffold(
14      appBar: AppBar(),
15      body: FutureBuilder<String>(
16        future: GetVersionPlugin.version,
17        builder: (c, snapshot) {
18          if (!snapshot.hasData) {
19            return Container();
20          }
21          return Text(snapshot.data);
22        },
23      ),
24    );
25  }
26}

后记

本章简单使用了一下 go-flutter 项目, 仓库地址:

这里需要注意下, 由于 go 插件的原因,直接 clone 是跑不起来的, 你需要配置 go 以后, 把 go 插件复制到$GOPATH/src/github.com/caijinglong/go-flutter-plugin/version目录内

后面有时间补测下打包产物

以上