10分钟入坑Flutter指南

748 阅读3分钟

Flutter

Flutter 由 Flutter Engine 和 Dart 实现,Flutter Engine 负责线程管理,Dart VM 状态管理和 Dart 代码加载等工作。而 Dart 代码所实现的 Framework 则是业务接触到的主要 API,诸如 Widget 等概念就是在 Dart 层面 Framework的内容。

下图flutter的框架结构诠释了渲染以及实现的方式

Flutter 从一开始就是为纯 Flutter 应用设计的,纯 Flutter 应用整个运行生命周期都只有一个 FlutterView 和 Root Isolate,依靠 Flutter Framework 自身 Route 支持在 FlutterView 内部完成界面跳转

  • 一个 FlutterView 对应一个 FlutterEngine 实例;

  • 一个 FlutterEngine 实例对应一个 Dart Isolate 实例;

  • 同一个进程只有且仅有一个 Dart VM 虚拟机;

  • 一个 Dart VM 上会存在多个 Dart Isolate 实例,Isolate 是 dart 代码的执行环境;

Flutter 和其他跨平台方案的本质区别:

  • React Native 之类的框架,只是通过 JavaScript 虚拟机扩展调用系统组件,由 Android 和 iOS 系统进行组件的渲染;
    • 上层是js语言,通过中间层 bridge,再由Android 和 iOS 系统进行组件的渲染
    • 对接不同平台会有些差异,无法统一
  • Flutter 则是自己完成了组件渲染的闭环
    • 不同平台不需要写iOS 或者 Android 不同的组件渲染接口
    • 少了中间层bridge的开销,性能会更快

初始化-配置

ios 开发模式为例

  • Visual Studio Code

  • macOs 10.13 以上

  • Xcdoe

    • 配置完成后,输入命令 open -a Simulator 打开 iOS 模拟器
  • flutter sdk 安装包

    • 编辑 ~/.bash_profile 文件,export PATH="$PATH:pwd/flutter/bin"将 flutter 命令的执行路径追加到环境变量 PATH 中
  • create the app

flutter命令

cd flutter_app
export PATH="$PATH:`pwd`/flutter/bin"
echo $PATH
which flutter
open -a Simulator
flutter create my_app
cd my_app
flutter run

小试牛刀 hello word

widget

Flutter 中都是 Widget,包括应用、视图、视图控制器、布局等在内的概念,都建立在 Widget 之上, build 方法中,我们通常通过对基础 Widget 进行相应的 UI 配置,或是组合各类基础 Widget 的方式进行 UI 的定制化。比如在 MyApp 中,我通过 MaterialApp 这个 Flutter App 框架设置了应用首页,即 MyHomePage。当然,[MaterialApp] (api.flutter.dev/flutter/mat…)也是一个 Widget,可以参考API设计不同的风格.

StatefulWidget有状态组件 这类Widget(比如 Image、Checkbox)的展示,除了父 Widget 初始化时传入的静态配置之外,还需要处理用户的交互(比如,用户点击按钮)或其内部数据的变化(比如,网络数据回包),并体现在 UI 上。

StatelessWidget无状态组件比如 Text、Container、Row、Column 等)在创建时,通过父widget的初始化获得配置参数之外不依赖于任何其他信息)

hello word

MyApp 为 Flutter 应用的运行实例,通过在 main 函数中调用 runApp 函数实现程序的入口。而应用的首页则为 MyHomePage,一个拥有 _MyHomePageState 状态的 StatefulWidget。_MyHomePageState 通过调用 build 方法,以相应的数据配置完成了包括导航栏、文本及按钮的页面视图的创建。

官网demo解读如下:

手势 - 手势密码

  • 指针事件
  • 手势识别

手指接触屏幕 PointerDownEvent、手指在屏幕上移动 PointerMoveEvent、手指抬起 PointerUpEvent,以及触摸取消 PointerCancelEvent

手势语义操作的 Gesture,如点击 onTap、双击 onDoubleTap、长按 onLongPress、拖拽 onPanUpdate、缩放 onScaleUpdate 等

Flutter 提供了 Listener Widget,可以监听其子 Widget 的原始指针事件, 以及 Arena 用来识别究竟哪个手势可以响应用户事件。

main.dart 文件如下

import 'package:flutter/material.dart';
import 'package:flutter/gestures.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}


class MyHomePage extends StatelessWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: 3,
      child: Scaffold(
        body: TabBarView(
          physics: NeverScrollableScrollPhysics(),
          children: [
            ListenerWidget(),  // Flutter 提供了 Listener Widget,可以监听其子 Widget 的原始指针事件
            DragWidget(),  // 拖拽
            DoubleGestureWidget(), // 双击手势
          ],
        ),
        bottomNavigationBar: TabBar(
          tabs: [
            Tab(icon: Icon(Icons.home),text: "指针事件",),
            Tab(icon: Icon(Icons.rss_feed),text: "手势",),
            Tab(icon: Icon(Icons.perm_identity),text: "手势冲突",)
          ],
          unselectedLabelColor: Colors.blueGrey,
          labelColor: Colors.blue,
          indicatorSize: TabBarIndicatorSize.label,
          indicatorColor: Colors.red,
        ),
      ),
    );
  }
}


class ListenerWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Listener(
        child: Container(
          color: Colors.red,
          width: 300,
          height: 300,
        ),
        onPointerDown: (event) => print("down $event"),
        onPointerMove:  (event) => print("move $event"),
        onPointerUp:  (event) => print("up $event"),
      )
    );
  }
}


class DragWidget extends StatefulWidget {
  @override
  _DragState createState() => new _DragState();
}

class _DragState extends State<DragWidget> {
  double _top = 0.0; //距顶部的偏移
  double _left = 0.0;//距左边的偏移

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("demo"),),
      body: Stack(
        children: <Widget>[
          Positioned(
            top: _top,
            left: _left,
            child: GestureDetector(   // 手势识别
              child: Container(color: Colors.red,width: 50,height: 50),
              onTap: ()=>print("Tap"),
              onDoubleTap: ()=>print("Double Tap"),
              onLongPress: ()=>print("Long Press"),
              onPanUpdate: (e) {
                setState(() {   // 触发状态更新
                  _left += e.delta.dx;
                  _top += e.delta.dy;
                });
              },
            ),
          )
        ],
      )
    );
  }
}

class DoubleGestureWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      body: RawGestureDetector(
        gestures: {
          MultipleTapGestureRecognizer: GestureRecognizerFactoryWithHandlers<
              MultipleTapGestureRecognizer>(
                () => MultipleTapGestureRecognizer(),
                (MultipleTapGestureRecognizer instance) {
              instance.onTap = () => print('parent tapped ');  // 父视图的点击回调
            },
          )
        },
        child: Container(
          color: Colors.pinkAccent,
          child: Center(
            child: GestureDetector(
                onTap: () => print('Child tapped'), // 子视图的点击回调
                child: Container(
                  color: Colors.blueAccent,
                  width: 200.0,
                  height: 200.0,
                )),
          ),
        ),
      )
    );
  }

}


class MultipleTapGestureRecognizer extends TapGestureRecognizer {
  @override
  void rejectGesture(int pointer) {
    acceptGesture(pointer);
  }
}

相关的ios 手势密码开发

  • 手势密码中的圆点

  • 手势绘制 View

  • 展示ui

  • GesturesViewController:这个 controller 用于展示 UI,你可以替换成自己 controller,

  • GesturesView:用于圆点按钮的初始化和布局,

  • PointView:圆点手势按钮。

plugin 调用native的功能- camera

  • pubspec.yaml添加所需的依赖项。
    • flutter
    • sdk: flutter
    • camera:">=0.5.7"
    • path_provider:^0.5.0
dependencies:
flutter:
  sdk: flutter
   # The following adds the Cupertino Icons font to your application.
   # Use with the CupertinoIcons class for iOS style icons.
   cupertino_icons: ^0.1.2
  camera: "0.5.7"
  # path_provider: "/Users/Documents/ppt/pic/"
  # path: "/Users/Documents/ppt/pic/"
  #  path: "../"
  path_provider: ^0.5.0
  
  
  //
  flutter pub get

flutter pub get error

ios 配置 info.plist


	<key>NSCameraUsageDescription</key>
  <string>Can I use the camera please?</string>
  <key>NSMicrophoneUsageDescription</key>
  <string>Can I use the mic please?</string>

  • 获取可用摄像机的列表。
  • 创建并初始化CameraController。
  • CameraPreview 显示摄像机的源。
  • CameraController相机控制器拍照。
  • 使用Image widget显示图片。

社区问题

  • 新项目可以直接尝试
  • 旧项目flutter如何于native共存,数据状态的管理(阿里,字节调动)
  • 性能
    • 虽然 Flutter 内部通过 Element 层可以最大程度地降低对真实渲染视图的修改,提高渲染效率,而不是销毁整个 RenderObject 树重建。但,大量 Widget 对象的销毁重建是无法避免的。如果某个子 Widget 的重建涉及到一些耗时操作,那页面的渲染性能将会急剧下降。
    • 合理使用StatefulWidget有状态组件 StatelessWidget无状态组件

参考

flutter camera camera