Flutter入门基础

官方文档

  • Flutter官方开发文档地址: https://flutter.dev/docs
  • Flutter中文开发文档地址: https://flutterchina.club/docs/

预研内容主要分为几个部分:

  1. Flutter环境搭建(技术框架, 安装,编辑器)
  2. Flutter的UI及交互(布局,交互,手势,动画,路由导航)
  3. Flutter资源文件管理(图片资源,文件资源,文件的读写)
  4. Flutter数据存储
  5. Flutter的网络请求(网络HTTP,JSON序列化)
  6. Flutter与原生平台(平台特定代码交互)
  7. Flutter开发语言(Dart语言)
  8. Flutter其他

Flutter环境搭建

  • 技术框架
Flutter入门基础_第1张图片
15583329814371.jpg
Flutter入门基础_第2张图片
15583330609276.jpg

在Flutter中用Widget来描述界面,Widget只是View的“配置信息”,编写的时候利用Dart语言一些声明式特性来得到类似结构化标记语言的可读性。Widget根据布局形成一个层次结构。每个widget嵌入其中,并继承其父项的属性。没有单独的“应用程序”对象,相反,根widget扮演着这个角色。在Flutter中,一切皆为Widget,甚至包括css样式。

  • Flutter环境安装

mac下终端操作:
vim ~/.bash_profile 添加路径(没有.bash_profile的时候,需要通过vim命令创建)
export PUB_HOSTED_URL=https://pub.flutter-io.cn //国内用户需要设置
export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn //国内用户需要设置
export PATH=PATH_TO_FLUTTER_GIT_DIRECTORY/flutter/bin:$PATH
修改 ~/.zshrc ,在其中添加:source ~/.bash_profile (没有.zshrc的时候,需要通过vim命令创建)

  • 编辑器

Android Studio 安装

安装Flutter和Dart插件

在Flutter插件中,可使用以下模板:
前缀stless: 创建一个StatelessWidget的子类.
前缀stful: 创建一个StatefulWidget子类并且关联到一个State子类.
前缀stanim: 创建一个StatefulWidget子类, 并且它关联的State子类包括一个 AnimationController

Flutter的UI及交互

  • 布局

参考:https://flutterchina.club/tutorials/layout/

Text:该 widget 可让创建一个带格式的文本。

Row、 Column: 这些具有弹性空间的布局类Widget可让您在水平(Row)和垂直(Column)方向上创建灵活的布局

Stack: 取代线性布局 (译者语:和Android中的LinearLayout相似),Stack允许子 widget 堆叠

Container: Container 可让您创建矩形视觉元素。container 可以装饰为一个BoxDecoration

为了继承主题数据,widget需要位于MaterialApp内才能正常显示, 因此我们使用MaterialApp来运行该应用。 //Scaffold是Material中主要的布局组件.

GestureDetector widget并不具有显示效果,而是检测由用户做出的手势。 当用户点击Container时, GestureDetector会调用它的onTap回调, 在回调中,将消息打印到控制台。您可以使用GestureDetector来检测各种输入手势,包括点击、拖动和缩放。

StatefulWidgets是特殊的widget,它知道如何生成State对象,然后用它来保持状态

在StatefulWidget调用createState之后,框架将新的状态对象插入树中,然后调用状态对象的initState。 子类化State可以重写initState,以完成仅需要执行一次的工作。 例如,您可以重写initState以配置动画或订阅platform services。initState的实现中需要调用super.initState当一个状态对象不再需要时,框架调用状态对象的dispose。 您可以覆盖该dispose方法来执行清理工作。例如,您可以覆盖dispose取消定时器或取消订阅platform services。 dispose典型的实现是直接调用super.dispose。

stateless widget 没有内部状态. Icon、 IconButton, 和Text 都是无状态widget, 他们都是 StatelessWidget的子类。stateful widget 是动态的. 用户可以和其交互 (例如输入一个表单、 或者移动一个slider滑块),或者可以随时间改变 (也许是数据改变导致的UI更新)

可以使用key来控制框架将在widget重建时与哪些其他widget匹配。默认情况下,框架根据它们的runtimeType和它们的显示顺序来匹配。 使用key时,框架要求两个widget具有相同的key和runtimeType。

各种widgets的目录索引,有UI不熟悉的,可以在这里找到说明:https://flutterchina.club/widgets/
各类Widgets的地址: https://flutterchina.club/widgets/basics/
各种UI布局需要用到的控件介绍: https://flutterchina.club/widgets/material/
iOS 风格的控件集合 介绍: https://flutterchina.club/widgets/cupertino/

  • UI的一些注意事项

类MyAppBar和MyScaffold中使用了Container、Row、Column、Text、IconButton、Icon、BoxDecoration、Center、Expanded等常用Widget

Theme.of(context)将查找Widget树并返回树中最近的Theme。如果我们的Widget之上有一个单独的Theme定义,则返回该值。如果不是,则返回App主题。 事实上,FloatingActionButton真是通过这种方式找到accentColor的!

ListView的构造函数需要一次创建所有项目,但ListView.builder的构造函数不需要,它将在列表项滚动到屏幕上时创建该列表项。

new ListView.builder(
  itemCount: items.length,
  itemBuilder: (context, index) {
    return new ListTile(
      title: new Text('${items[index]}'),
    );
  },
);

滑动删除有直接可用的Widget;

将响应转换为自定义Dart对象;

class Post {
  final int userId;
  final int id;
  final String title;
  final String body;

  Post({this.userId, this.id, this.title, this.body});

  factory Post.fromJson(Map json) {
    return new Post(
      userId: json['userId'],
      id: json['id'],
      title: json['title'],
      body: json['body'],
    );
  }
}

http package提供了一种方便的方法来为请求添加headers。您也可以使用dart:iopackage来添加。

Flutter提供各种按钮和类似的交互式widget。这些widget中的大多数实现了Material Design 指南, 它们定义了一组具有质感的UI组件。可以使用GestureDetector来给任何自定义widget添加交互性。 可以在管理状态和Flutter Gallery中找到GestureDetector的示例。

如果你要构建一个 CustomButton ,并在构造器中传入它的 label?那就组合 RaisedButton 和 label,而不是扩展 RaisedButton。

Isolates 是分离的运行线程,并且不和主线程的内存堆共享内存。这意味着你不能访问主线程中的变量,或者使用 setState() 来更新 UI。正如它们的名字一样,Isolates 不能共享内存。

在 Flutter 中,最简单的方法是使用 ListView widget。它表现得既和 iOS 中的 ScrollView 一致,也能和 TableView 一致,因为你可以给它的 widget 做垂直排布:

  • 交互

参考:https://flutterchina.club/tutorials/interactive/

  • 手势

要从widget层监听手势,使用 GestureDetector.

  • 动画

参考动画的说明: https://flutterchina.club/animations/
在 Flutter 中,使用 AnimationController 。这是一个可以暂停、寻找、停止、反转动画的 Animation 类型。它需要一个 Ticker 当 vsync 发生时来发送信号,并且在每帧运行时创建一个介于 0 和 1 之间的线性插值(interpolation)。你可以创建一个或多个的 Animation 并附加给一个 controller。

  • 路由

路由的介绍:https://docs.flutter.io/flutter/widgets/Navigator-class.html
在页面之间跳转,你有几个选择:
具体指定一个由路由名构成的 Map。(MaterialApp
直接跳转到一个路由。(WidgetApp)
Navigator 类不仅用来处理 Flutter 中的路由,还被用来获取你刚 push 到栈中的路由返回的结果。通过 await等待路由返回的结果来达到这点。

Map coordinates = await Navigator.of(context).pushNamed('/location');
Navigator.of(context).pop({"lat":43.821757,"long":-79.226392});

Flutter资源文件管理

  • 图片资源

iOS 把 images 和 assets 作为不同的东西,而 Flutter 中只有 assets。被放到 iOS 中 Images.xcasset 文件夹下的资源在 Flutter 中被放到了 assets 文件夹中。assets 可以是任意类型的文件,而不仅仅是图片。
例如,你可以把 json 文件放置到 my-assets 文件夹中。在 pubspec.yaml 文件中声明 assets:`assets:

  • my-assets/data.json然后在代码中使用 AssetBundle 来访问它:Future loadAsset() async {
    return await rootBundle.loadString('my-assets/data.json');
    }`

images/my_icon.png // Base: 1.0x image
images/2.0x/my_icon.png // 2.0x image
images/3.0x/my_icon.png // 3.0x image

  • 文件的读写

PathProvider 插件提供了一种平台透明的方式来访问设备文件系统上的常用位置。该类当前支持访问两个文件系统位置:

Flutter数据存储

在 Flutter 中,可以使用 Shared Preferences plugin 来达到相似的功能。它包裹了 UserDefaluts 以及 Android 上等价的 SharedPreferences 的功能。

在 iOS 中,你通过 CoreData 来存储结构化的数据。这是一个 SQL 数据库的上层封装,让查询和关联模型变得更加简单。在 Flutter 中,使用 SQFlite 插件来实现这个功能。

Flutter的网络请求

  • 网络HTTP

使用dio 来发起网络请求,它是一个强大易用的dart http请求库,支持Restful API、FormData、拦截器、请求取消、Cookie管理、文件上传/下载
参见具体说明: https://flutterchina.club/networking/

  • JSON序列化

使用dart:convert库可以简单解码和编码JSON;要对简单的JSON进行编码,请将简单值(字符串,布尔值或数字字面量)或包含简单值的Map,list等传给encode方法:

Flutter与原生平台

  • 平台特定代码交互

交互的方式: https://flutterchina.club/platform-channels/

通过MethodChannel实现。在Flutter当中定义平台管道,定义平台需要捕获的方法名称->Appdelegate当中注册定义的管道->管道方法调用的时候,实现平台方法的调用(达到Flutter调用平台方法的目的)

  • Flutter页面路由 与 原生页面之间跳转实现:

Flutter 的代码并不直接在平台之下运行,相反,Dart 代码构建的 Flutter 应用在设备上以原生的方式运行,却“侧步躲开了”平台提供的 SDK。这意味着,例如,你在 Dart 中发起一个网络请求,它就直接在 Dart 的上下文中运行。你并不会用上平常在 iOS 或 Android 上使用的原生 API。你的 Flutter 程序仍然被原生平台的 ViewController 管理作一个 view,但是你并不会直接访问 ViewController 自身,或是原生框架。

我怎么访问 GPS 传感器?使用 location 社区插件。

我怎么访问摄像头?image_picker 在访问摄像头时非常常用。

Flutter开发语言

  • Dart语言一些语法特性:

所有没有初始化的变量值都是 null。
注意: 只有当名字冲突的时候才使用 this。否则的话, Dart 代码风格样式推荐忽略 this。
注意: 如果在构造函数的初始化列表中使用 super(),需要把它放到最后。 详情参考 Dart 最佳实践。
在构造函数体执行之前除了可以调用超类构造函数之外,还可以 初始化实例参数。 使用逗号分隔初始化表达式。

class Point {
  num x;
  num y;

  Point(this.x, this.y);

  // Initializer list sets instance variables before
  // the constructor body runs.
  Point.fromJson(Map jsonMap)
      : x = jsonMap['x'],
        y = jsonMap['y'] {
    print('In Point.fromJson(): ($x, $y)');
  }
}

有时候一个构造函数会调动类中的其他构造函数。 一个重定向构造函数是没有代码的,在构造函数声明后,使用 冒号调用其他构造函数。 Point.alongXAxis(num x) : this(x, 0);

如果一个构造函数并不总是返回一个新的对象,则使用 factory 来定义 这个构造函数。例如,一个工厂构造函数 可能从缓存中获取一个实例并返回,或者 返回一个子类型的实例。

class Logger {
  final String name;
  bool mute = false;

  // _cache is library-private, thanks to the _ in front
  // of its name.
  static final Map _cache =
      {};

  factory Logger(String name) {
    if (_cache.containsKey(name)) {
      return _cache[name];
    } else {
      final logger = new Logger._internal(name);
      _cache[name] = logger;
      return logger;
    }
  }

  Logger._internal(this.name);

  void log(String msg) {
    if (!mute) {
      print(msg);
    }
  }
}

Abstract methods(抽象函数): 实例函数、 getter、和 setter 函数可以为抽象函数, 抽象函数是只定义函数接口但是没有实现的函数,由子类来 实现该函数。如果用分号来替代函数体则这个函数就是抽象函数。

abstract class Doer {
  // ...Define instance variables and methods...

  void doSomething(); // Define an abstract method.
}

class EffectiveDoer extends Doer {
  void doSomething() {
    // ...Provide an implementation, so the method is not abstract here...
  }
}

操作符可以被覆写。 例如,如果你定义了一个 Vector 类, 你可以定义一个 + 函数来实现两个向量相加。

如果你使用 noSuchMethod() 函数来实现每个可能的 getter 、setter、 以及其他类型的函数,你可以使用 @proxy 注解来避免警告信息:

枚举类型通常称之为 enumerations 或者 enums, 是一种特殊的类,用来表现一个固定 数目的常量。枚举的 values 常量可以返回 所有的枚举值。

Mixins 是一种在多类继承中重用 一个类代码的方法。使用 with 关键字后面为一个或者多个 mixin 名字来使用 mixin

class Musician extends Performer with Musical {
  // ...
}

class Maestro extends Person
    with Musical, Aggressive, Demented {
  Maestro(String maestroName) {
    name = maestroName;
    canConduct = true;
  }
}

如果你查看 List 类型的 API 文档, 则可以看到 实际的类型定义为 List。 这个 <…> 声明 list 是一个 泛型 (或者 参数化) 类型。 通常情况下,使用一个字母来代表类型参数, 例如 E, T, S, K, 和 V 等。T 是一个备用类型。这是一个类型占位符, 在开发者调用该接口的时候会指定具体类型。

如果你导入的两个库具有冲突的标识符, 则你可以使用库的前缀来区分。 例如,如果 library1 和 library2 都有一个名字为 Element 的类, 你可以这样使用:

import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;
// ...
Element element1 = new Element();           // Uses Element from lib1.
lib2.Element element2 = new lib2.Element(); // Uses Element from lib2.

Dart 有一些语言特性来支持 异步编程。 最常见的特性是 async 方法和 await 表达式。要使用 await,其方法必须带有 async 关键字:

在一个方法上添加 async 关键字,则这个方法返回值为 Future。 例如,下面是一个返回字符串 的同步方法: String lookUpVersionSync() => '1.0.0';如果使用 async 关键字,则该方法 返回一个 Future,并且 认为该函数是一个耗时的操作。Future lookUpVersion() async => '1.0.0';

在 await expression 中, expression 的返回值通常是一个 Future; 如果返回的值不是 Future,则 Dart 会自动把该值放到 Future 中返回。 Future 对象代表返回一个对象的承诺(promise)。 await expression 执行的结果为这个返回的对象。 await expression 会阻塞住,直到需要的对象返回为止。如果 await 无法正常使用,确保是在一个 async 方法中。 例如要在 main() 方法中使用 await, 则 main() 方法的函数体必须标记为 async:

异步 for 循环具有如下的形式:使用 break 或者 return 语句可以 停止接收 stream 的数据, 这样就跳出了 for 循环并且 从 stream 上取消注册了。

await for (variable declaration in expression) {
  // Executes each time the stream emits a value.
}

所有的 Dart 代码在 isolates 中运行而不是线程。 每个 isolate 都有自己的堆内存,并且确保每个 isolate 的状态都不能被其他 isolate 访问。

第三方Flutter框架Demo 及文章参考

  • 基于Google Flutter的开源中国客户端
  • 山寨掘金
  • Flutter beta3 避坑指南1

其他

  • Flutter安装包大小的问题?

在安卓的安装包当中会根据工程的不同增加不同的大小。本地打的包和发布的包大小也可能不一样。

部分团队经验:在Release模式下,安卓端的安装包大小增加约为3~4M ,iOS端的安装包大小增加约为13~14M,实际增加的大小跟业务端代码和资源大小有关系;

其他团队的经验,引入 Flutter 之前,涨乐财富通的安装包为 94MB,引入之后大小为 100MB,发现增大了 6MB,这其中主要是引入了 Flutter 的 SDK,增加的大小在可以接受的范围。

参考: Flutter Android/iOS包大小分析

  • Flutter的组件化集成(将Flutter代码集成进现有的工程)?

对现有工程有侵入。不能完全按照官方的指导来集成。需要把Flutter编译产物放入主工程。

对混合栈的管理,参考闲鱼团队的开源方案。

参考:使用 Flutter 之后,我们的 CPU 占用率降了 50%

  • 预研结果
  1. 采用Flutter框架,能够满足日常80%以上的基础UI展示和常见需求。如果涉及复杂动画或者特定平台特性的调用,也可以使用Flutter的管道特性,和平台进行交互实现。

  2. Flutter的性能虽然不如官方宣称60FPS,但是在流畅性上也接近原生,而且主要能够在安卓和iOS平台上实现统一且支持部分定制,尽管安装包和内存上会有部分提升空间。





开发过程中遇到的实际问题及解决:

  1. 按钮做倒计时功能
  2. 导航的时候,如果用Navigator.push处理,新的路由页面如果用MaterailPageRoute创建,那么 main.dart中 run 的需要是一个MaterailApp
  3. 在编辑的时候,有的时候编辑器输入颜色的时候,会fetching Doc ,然后AS就会请求文档地址,网络阻塞造成卡死。 根据查找的资料,在mac下需要找到 ~/Library/preference/Android studio x.x下面的jav.table.xml这样的文件,替换里面JAVDOC节点下的文档来源地址url,或者有解决方式是直接删除掉这个文件。 试了一下删除,效果不是很好,但是有所改善。可以参考这个回答: Android Studio hangs at fetching documentation
  4. ListView自定义显示Item。 建立数据源 -> 自定义Adapter,处理Item显示的内容 -> list的内容设置给Adapter -> Adapter设置给ListView;

你可能感兴趣的:(Flutter入门基础)