Flutter

[TOC]

0x00 Flutter

flutter是google开发的移动端UI框架,支持android和ios。该框架使用dart语言进行开发,在skia的基础上开发了一套公共组件达到android与ios共用代码的目的。

1. Flutter系统架构

flutter_arch.png

2. Platform Channels

flutter使用methodChannel/flutterMethodChannel来访问系统原生api。


platform_channels.png

0x01 使用Flutter开发应用程序

1. 下载配置Flutter SDK

  • 由于众所周知的原因,我们首先需要进行如下配置,让flutter通过国内镜像下载sdk等依赖。
export PUB_HOSTED_URL=https://pub.flutter-io.cn
export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn
  • 安装sdk
$ git clone -b beta https://github.com/flutter/flutter.git
$ export PATH=`pwd`/flutter/bin:$PATH
  • 使用flutter doctor检测本机环境,然后根据提示安装依赖软件。
$ flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel beta, v0.1.5, on Mac OS X 10.11.6 15G19009, locale zh-Hans)
[✓] Android toolchain - develop for Android devices (Android SDK 27.0.3)
[!] iOS toolchain - develop for iOS devices (Xcode 7.2.1)
    ✗ Flutter requires a minimum Xcode version of 9.0.0.
      Download the latest version or update via the Mac App Store.
    ✗ ios-deploy not installed. To install:
        brew install ios-deploy
    ✗ CocoaPods not installed.
        CocoaPods is used to retrieve the iOS platform side's plugin code that responds to your plugin usage on the Dart side.
        Without resolving iOS dependencies with CocoaPods, plugins will not work on iOS.
        For more info, see https://flutter.io/platform-plugins
      To install:
        brew install cocoapods
        pod setup
[✓] Android Studio (version 3.0)
[✓] Android Studio (version 2.3)
[!] IntelliJ IDEA Community Edition (version 2017.2.6)
    ✗ Flutter plugin not installed; this adds Flutter specific functionality.
[!] VS Code (version 1.21.0)
[!] Connected devices
    ! No devices available

! Doctor found issues in 4 categories.

2. 使用Android Studio开发Demo

1. 创建应用

create_flutter_app_1.png
create_flutter_app_2.png
create_flutter_app_3.png
create_flutter_app_4.png

2. 项目结构

使用android studio创建出来的项目目录结构大致如下:

  • ios目录包含了ios的全部代码可以直接使用xcode(需要9.0+版本)进行开发。
  • android目录包含了android的全部代码,直接使用android studio开发即可。
  • lib目录中包含了两端通用的dart代码,在打包生成应用时,全部的dart代码会被编译为本地代码(如在android端,会被直接编译为so文件)。
app_struct.png

3. 调试与运行

  • 选择android/ios设备,然后点击运行按钮就可以将应用运行到android/ios设备上。


    app_select_device_and_run.png
  • 点击调试安妮运行app,就可以调试dart代码,调试界面与java完全一样。


    app_debug.png

4. 热加载

flutter中热加载的概念和android开发中的Instant run类似,同样也是点击⚡️按钮启用热加载功能。但是其拥有以下优点:

  1. 点击保存按钮或者保存快捷键,也会触发热加载功能(⚡️按钮功能相同)。
  2. 热加载会保留先前的状态
  3. 快!快!快! 代码增量编译在秒级别,单行代码改变反应到应用程序UI改变耗时约1.5秒左右。

所以大家就可以像写web页面一样,可以边写->边保存->边查看效果,开发效率大大加快有没有,再也不用等等等有没有。。。

热加载前 代码变动 热加载后
可以看到应用程序的计数器的值是2.
app_hot_reload_before.png
修改字符显示,添加“吼吼吼吼”到文本中,然后保存触发热加载。
app_hot_reload_with_state.png
可以看到文本字符发生改变,但是计数器的值未发生变化,仍然是2.
app_hot_reload_after.png

5. 应用程序结构与兼容性

1. 应用程序结构

  • Debug (slow mode模式)

首先我们打开项目根目录中的build文件夹,该文件包含了编译app的全部生成文件,其结构与android应用程序一致(注意:build目录在android studio中不显示,可以通过terminal打开或查看)。

apk_location.png

接下来,我们可以通过android studio直接打开该apk,可以发现仅仅只有一个页面的flutter应用大小已经达到了25MB左右,分析其结构(见下图),其包含了全部abi类型的so文件,导致apk整体比较大,排除掉x86x86_64平台的so文件之后,apk整体大小约11MB左右。

apk_struct_with_dart-1.png

  • Release模式

使用release模式时,apk大小约8.1MB,大小比较正常。

apk_struct_mode_release.png

2. 应用程序兼容性

  • andorid最低支持到 api 16;
  • ios最低支持到ios 8.0;

3. 开发Toast模块

这里我们直接使用android studio进行开发,如果大家需要直接使用flutter进行创建的话,可以直接参考platform-channels进行开发。

1. 使用dart编写公共的toast模块

import 'package:flutter/services.dart';

// 下划线开头的变量只在当前package中可见。
const _toast = const MethodChannel('com.coofee.flutterdemoapp/sdk/toast');

const int _LENGTH_SHORT = 0;

const int _LENGTH_LONG = 1;

void show(String text, int duration) async {
  try {
    await _toast.invokeMethod("show", {'text': text, 'duration': duration});
  } on Exception catch (e) {
    print(e);
  } on Error catch (e) {
    print(e);
  }
}

void showShort(String text) {
  show(text, _LENGTH_SHORT);
}

void showLong(String text) {
  show(text, _LENGTH_LONG);
}

2. 编写android端的代码并注册

package com.coofee.flutterdemoapp;

import android.os.Bundle;
import android.widget.Toast;

import io.flutter.app.FlutterActivity;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugins.GeneratedPluginRegistrant;

public class MainActivity extends FlutterActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        GeneratedPluginRegistrant.registerWith(this);

        new MethodChannel(getFlutterView(), "com.coofee.flutterdemoapp/sdk/toast")
                .setMethodCallHandler(new MethodChannel.MethodCallHandler() {
                    @Override
                    public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) {
                        if ("show".equals(methodCall.method)) {
                            String text = methodCall.argument("text");
                            int duration = methodCall.argument("duration");
                            Toast.makeText(MainActivity.this, text, duration).show();
                        }
                    }
                });
    }
}

3. 编写ios端代码并注册

ios端的代码与android端类似,但是需要使用FlutterMethodChannel进行处理,其他操作与android端一致,打开AppDelegate.m文件,添加如下代码:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  [GeneratedPluginRegistrant registerWithRegistry:self];

  FlutterViewController* controller = (FlutterViewController*)self.window.rootViewController;

  FlutterMethodChannel* toastChannel = [FlutterMethodChannel
                                            methodChannelWithName:@"com.coofee.flutterdemoapp/sdk/toast"
                                            binaryMessenger:controller];

  [toastChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
      if ([@"show" isEqualToString:call.method]) {
          // 展示toast;
          NSLog(@"显示toast....")
      }
  }];

  // Override point for customization after application launch.
  return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

4. 在flutter中调用toast模块

import 'sdk/toast.dart';

void _incrementCounter() {
  showShort('你点击了$_counter次');

  setState(() {
    // This call to setState tells the Flutter framework that something has
    // changed in this State, which causes it to rerun the build method below
    // so that the display can reflect the updated values. If we changed
    // _counter without calling setState(), then the build method would not be
    // called again, and so nothing would appear to happen.
    _counter++;
  });
}

5. 效果

toast_show.png

0x02 使用Dart 2

1. 升级flutter sdk

使用dart 2时,必须保证,Flutter SDK版本必须大于等于以下版本:

  • Beta channel: build 0.1.4 from 2018-02-19, or later
  • Dev channel: build from 2018-02-22, or later
  • Master channel: build from 2018-02-20, or later

在Terminal中执行flutter --version命令,查看flutter的版本:

→ flutter --version
Flutter 0.1.5 • channel beta • https://github.com/flutter/flutter.git
Framework • revision 3ea4d06340 (3 weeks ago) • 2018-02-22 11:12:39 -0800
Engine • revision ead227f118
Tools • Dart 2.0.0-dev.28.0.flutter-0b4f01f759

在Terminal中执行flutter upgrade可以升级flutter。

2. 在android studio中启用dart 2

在android studio中启用dart2后,需要重启android studio使其生效。

dart-2.png

3. dart 1 vs dart 2

从下图可以看出dart2相对于dart1来说,省略了关键字new,使得声明式布局的可读性更进一步。

dart1_vs_dart2.png

4. dart2对apk大小的影响

从下图可以看出来,使用dart2时生成的apk比dart1打5MB左右(slow mode模式)。

apk_dart1_vs_dart2.png

0x03 Flutter's modes

从下图我们可以看到,flutter mode显示在在app的右上角。

mode_slow.png

flutter的应用程序有以下4中模式,分别使用不同的命令生成,且不同模式下生成的应用大小不一(如:release模式会去掉x86相关的so文件)

mode 命令
debug flutter run debug模式下的产物,且应用的右上角会显示slow mode字样,支持debug。
release flutter run --release UI上面不显示模式;禁止debug,且删除了debug相关的信息;关闭全部的断言检测,减小包大小,使其达到最佳性能。
profile flutter run --profile 调试性能,不支持模拟器。
test flutter test 和debug模式类似,不支持headless和桌面平台。

0x04 参考引用

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,192评论 6 511
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,858评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,517评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,148评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,162评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,905评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,537评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,439评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,956评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,083评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,218评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,899评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,565评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,093评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,201评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,539评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,215评论 2 358

推荐阅读更多精彩内容