flutter

[TOC]

0x00 Flutter

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

1. Flutter系统架构

2. Platform Channels

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

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. 创建应用

创建flutter应用 创建flutter应用 创建flutter应用 创建flutter应用

2. 项目结构

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

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

Flutter应用项目结构

3. 调试与运行

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

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

4. 热加载

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

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

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

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

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

1. 应用程序结构![](/flutter//flutter/

  • Debug (slow mode模式)

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

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

  • Release模式

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

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. 效果

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使其生效。

3. dart 1 vs dart 2

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

4. dart2对apk大小的影响

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

0x03 Flutter’s modes

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

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 参考引用