快速学习Flutter

写在前面

本文以官网为学习路线。辅助一些注意点。了解Flutter的整体环境

Flutter官网

快速学习flutter,首先需要知道flutter官网:

https://flutter.dev/

同时还有翻译版的flutter 中文网

https://flutterchina.club/

Flutter基本知识

由dart语言编写的跨平台UI框架。

Flutter 的特性

  • 跨平台
  • 高性能
  • 丰富的 UI 组件
  • 学习成本低
  • 开发效率高

Flutter Windows环境搭建

Flutter网站有安装:
https://flutter.dev/docs/get-started/install

英文不好可以看中文
Flutter中文网:
https://flutterchina.club/setup-windows/

  1. Flutter SDK
  2. Flutter IDE
    • Android Studio
    • VS Code
查看文档时有几点注意:
1.中国镜像地址

1.计算机 -> 属性 -> 高级系统设置 -> 环境变量,打开环境变量设置框。

2.在用户变量下,选择新建环境变量,添加如下的两个环境变量和值:

变量名:FLUTTER_STORAGE_BASE_URL

变量值:storage.flutter-io.cn

变量名:PUB_HOSTED_URL

变量值:pub.flutter-io.cn

2.搭建 Android 开发环境

1.打开 Android Studio
2.File > Settings > Plugins
3.点击 Browse repositories, 搜索 Flutter 并安装 重启 Android Studio。
4.Android Studio 重启后,点击 File > New ,如果看到了 New Flutter Project… ,说明 Flutter 插件已经安装完成。

3.Flutter SDK

https://flutter.dev/docs/development/tools/sdk/releases?tab=windows

最好下载稳定版本

Flutter SDK 的环境变量

1.计算机 -> 属性 -> 高级系统设置 -> 环境变量,打开环境变量设置框。
2.在用户变量下,选择 Path,点击编辑:

  • 如果已经存在 Path变量,则在原有的值后面先加 ;,然后将 Flutter SDK 的完整路径 E:\src\flutter\bin 添加上。
  • 如果没有 Path 变量,则新建一个名为 Path 的用户变量,然后将 Flutter SDK 的完整路径 E:\src\flutter\bin 添加上。

Flutter是如何运转的

Flutter重写了底层渲染逻辑和上层渲染开发。
在这里插入图片描述图片来自

Flutter 架构是采用的分层设计。从下到上依次为:Embedder(嵌入器)、Engine、Framework。
Embedder 是嵌入层,做好这一层的适配 Flutter 基本可以嵌入到任何平台上去; Engine 层主要包含 Skia、Dart 和 Text。Skia 是开源的二位图形库;Dart 部分主要包括 runtime、Garbage Collection、编译模式支持等;Text 是文本渲染。Framework 在最上层。我们的应用围绕 Framework 层来构建,因此也是本文要介绍的重点。

不是特别了解可以以后再看。
Flutter 实现原理及在马蜂窝的跨平台开发实践

Flutter知识体系

因为Flutter是跨平台开发。整体其实按照客户端路线即可构建整个知识体系。

在这里插入图片描述

Dart语言

Dart官网:
https://dart.dev/

Dart 语言优势

  • 面向对象
  • 支持 JIT(Just-in-time)(动态编译),也可以支持 AOT(Ahead Of Time)(静态编译)。热重载就是基于JIT编译。
  • 单线程模型:dart中没有线程,只有Isolate(隔离区)。Isolate之间不会共享内存。通过时间循环机制(Event looper)在时间队列(Event Queue)上传递消息。
  • 内存分配与垃圾回收: dart来及回收采用多声带算法。新生代在回收内存时采用“半空间机制”,触发垃圾你回收时,dart会将前半空间的活跃对象拷贝到备用空间,然后整体释放当前空间的所有内存。回收过程中,Dart 只需要操作少量的“活跃”对象,没有引用大量的死亡对象则被忽略。很适合Widget销毁重建场景。
如何区分一门语言究竟是AOT还是JIT?

通常来说,看代码在执行前是否需要编译即刻,如果需要编译属于AOT;如果不需要,属于JIT.

  • AOT代表:C/C++ 执行前编译机器码
  • JIT代表:JavaScript .Python 等脚本语言。

为什么Flutter会选择 Dart ?

Dart基础语法

官网中:
https://dart.dev/guides/language/language-tour

中文网:
http://dart.goodev.org/guides/language/language-tour

Dart语法:

Dart语法如果会java,javascript。上手会很快。

关键字56个
  • 声明变量并赋值的示例:
var name = 'Bob';
重点是Final and const
  • const 是编译时常量,在编译的时候就初始化了,但是 final 变量是当类创建的时候才初始化。
  • 有String 字符串 还可以写成 var(声明变量而不指定类型。)
数据类型
  • numbers
  • strings
//基本
String abc= 'abcdefg';
// 单引号嵌套双引号
String abcs= '$singleString a "bbb" ${doubleString}';
  • booleans
  • lists (也被称之为 arrays)
//创建一个int类型的list
 List list = [1, 2, 3];
// 使用List的构造函数
var vegetables = new List();
// 添加list
fruits.add('kiwis');

// 添加多个
fruits.addAll(['grapes', 'bananas']);

// 获取list的长度
assert(fruits.length == 5);

// 移出一个条目
var appleIndex = fruits.indexOf('apples');
fruits.removeAt(appleIndex);
assert(fruits.length == 4);

// 移除所有
fruits.clear();
assert(fruits.length == 0);
  • maps
定义 map 对象
// 经常使用字符串作为key。
var hawaiianBeaches = {
  'Oahu'      : ['Waikiki', 'Kailua', 'Waimanalo'],
  'Big Island': ['Wailea Bay', 'Pololu Beach'],
  'Kauai'     : ['Hanalei', 'Poipu']
};
// 构造函数也可以
var searchTerms = new Map();
//键值对
var nobleGases = new Map<int, String>();
//添加
var nobleGases = {54: 'xenon'};
//移除
nobleGases.remove(54);
Functions(方法)
  • 函数可以被定义为变量,甚至可以被定为参数传递给另一个函数。
  • 函数可以不用写返回值:
// 声明返回值
  bool isNoble(int atomicNumber) {
  return _nobleGases[atomicNumber] != null;
}

// 不声明返回值
  isNoble(atomicNumber) {
  return _nobleGases[atomicNumber] != null;
}

//对于只有一个表达式的方法,你可以选择 使用缩写语法来定义:
// => expr 语法是 { return expr; } 形式的缩写
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;
  • dart不支持重载,以可选参数来代替。Optional parameters(可选参数)
    • 默认构造函数的写法:
    Exapmle(this.x, this.y);
    
    • 创建对象可以不需要new。
     if (i.isOdd) return Divider(); 
    
    这句可以写成
     if (i.isOdd) return new Divider(); 
    
流程控制语句

和其他语言基本类似。

Typedefs

在 Dart 语言中,方法也是对象。 使用 typedef, 或者 function-type alias 来为方法类型命名, 然后可以使用命名的方法。 当把方法类型赋值给一个变量的时候,typedef 保留类型信息。

typedef int Compare(Object a, Object b);
class SortedCollection {
  Compare compare;
  SortedCollection(this.compare);
}

 // Initial, broken implementation.
 int sort(Object a, Object b) => 0;

main() {
  SortedCollection coll = new SortedCollection(sort);
  assert(coll.compare is Function);
  assert(coll.compare is Compare);
}
异常

和java基本相似

try {
  breedMoreLlamas();
} catch(e) {
  print('Error: $e');  // Handle the exception first.
} finally {
  cleanLlamaStalls();  // Then clean up.
}

和java基本相似,也有构造方法,成员变量,有抽象类,可以实现

class Point {
  num x;
  num y;

  Point(num x, num y) {
    // There's a better way to do this, stay tuned.
    this.x = x;
    this.y = y;
  }
}

以上代码不免有点java语言的感觉,构造方法可以简化,简化后就有dart语言的风格了。

class Point {
  num x;
  num y;
 Point(this.x, this.y);
}

需要注意的是:
没有public private等关键字。在变量前面加入“_”,表示私有。

父子类构造函数顺序:

子类构造函数调用父类非命名,无参构造函数。先父后子。
1.初始化列表
2.父类的无参构造函数
3当前类的无参构造函数

通过named constructors可以使父类有多个构造函数,但是子类是不能继承父类的构造函数的。如果使用父类的构造函数,子类需要实现父类的构造函数。

多个构造函数

可以通过Named constructors使类有多个构造函数:
使用:

class Point {
  num x, y;
  Point(this.x, this.y);
  // Named constructor
  Point.origin() {
    x = 0;
    y = 0;
  }
}

通过named constructors可以使父类有多个构造函数,但是子类是不能继承父类的构造函数的。如果使用父类的构造函数,子类需要实现父类的构造函数。

继承和接口的差别

从下面代码中,我们也可以看到一些dart语言的语法风格。

class Point {
  num x = 0, y = 0;
  void printInfo() => print('($x,$y)');
}

//Vector 继承自 Point
class Vector extends Point{
  num z = 0;
  @override
  void printInfo() => print('($x,$y,$z)'); // 覆写了 printInfo 实现
}

//Coordinate 是对 Point 的接口实现
class Coordinate implements Point {
  num x = 0, y = 0; // 成员变量需要重新声明
  void printInfo() => print('($x,$y)'); // 成员函数需要重新声明实现
}

var xxx = Vector(); 
xxx
  ..x = 1
  ..y = 2
  ..z = 3; // 级联运算符,等同于 xxx.x=1; xxx.y=2;xxx.z=3;
xxx.printInfo(); // 输出 (1,2,3)

var yyy = Coordinate();
yyy
  ..x = 1
  ..y = 2; // 级联运算符,等同于 yyy.x=1; yyy.y=2;
yyy.printInfo(); // 输出 (1,2)
print (yyy is Point); //true
print(yyy is Coordinate); //true

子类 Coordinate 采用接口实现的方式,仅仅是获取到了父类 Point 的一个“空壳子”,只能从语义层面当成接口 Point 来用,但并不能复用 Point 的原有实现。

类继承,接口实现和混入(minxin)的理解

父类继承:和java类似,继承了父类的实例变量和各种方法。但是不能用一个普通方法重写getter。
抽象类:抽象类不能实例化,会报出AbstractClassInstantiationError错误。
接口:成员变量,成员函数需要重新声明实现。和java不一样的是,没有接口声明,可以通过抽象类来描述接口。
mixin:使一个类有多个父类。例如:在Flutter中常见的我们需要继承state。如果需要页面保持状态,我们还需要AutomaticKeepAliveClientMixin来保持页面状态。这时就需要通过with来使用mixin.

泛型

和java类似

T first<T>(List<T> ts) {
  // ...Do some initial work or error checking, then...
  T tmp ?= ts[0];
  // ...Do some additional checking or processing...
  return tmp;
}
Asynchrony support(异步支持)

和java不同的是,java使用多线程,dart提供的是异步支持,通过async和await

checkVersion() async {
  var version = await lookUpVersion();
  if (version == expectedVersion) {
    // Do something.
  } else {
    // Do something else.
  }
}

Flutter示例

让我们来看一下dart语法在Flutter中

//导包
import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';

//程序的起始入口
//箭头符号  支持lambada表达式
void main() => runApp(MyApp());

//extends 可以继承
//StatelessWidget  是无状态的
//StatefulWidget是有状态的。有状态的就是数据频繁变动的,我们可以用setstate()方法来进行刷新UI

class MyApp extends StatelessWidget {
//通过build来进行构建
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Startup Name Generator',
      home: RandomWords(),
    );
  }
}

class RandomWordsState extends State<RandomWords> {
  //Dart 没有 public、 protected、 和 private 关键字
  //变量定义可以为final 
  //_suggestions :如果一个标识符以 (_) 开头,则该标识符 在库内是私有的。
  //数组 []
  final _suggestions = <WordPair>[];
  final _biggerFont = const TextStyle(fontSize: 18.0);

  Widget _buildSuggestions() {
    return ListView.builder(
        padding: const EdgeInsets.all(16.0),
        itemBuilder: (context, i) {
        
          if (i.isOdd) return Divider(); 

          final index = i ~/ 2;
          if (index >= _suggestions.length) {
            _suggestions.addAll(generateWordPairs().take(10)); 
          }
          return _buildRow(_suggestions[index]);
        });
  }
 //一个简单的函数
  Widget _buildRow(WordPair pair) {
    return ListTile(
      title: Text(
        pair.asPascalCase,
        style: _biggerFont,
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Startup Name Generator'),
      ),
      body: _buildSuggestions(),
    );
  }

}

class RandomWords extends StatefulWidget {
  @override
  RandomWordsState createState() => RandomWordsState();
}

MaterialApp 与 Scaffold

  • Flutter中有两种风格安卓对应Material Design,MaterialApp ,iOS对应Cupertino.
  • MaterialApp 经常是 Flutter Widget 树里的第一个元素,就是 Root Widget。
  • Scaffold 实现了 Material Design 的基本布局结构,例如 AppBar、Drawer、SnackBar 等,所以为了使用这些布局,也必须要使用 Scaffold,所以一个 Flutter App 的 基本结构就是:Root Widget 是 MaterialApp ,然后 MaterialApp 的 子Widget 就是 Scaffold,然后我们在 Scaffolfd 的 子Widget 里写UI。
Widgets
  • widget树深度优先遍历,将widget由父到子构建。
  • 通过build来构建UI
    官网中看完Get Started会看到
    Learn more
    https://flutter.dev/docs/get-started/learn-more
    先看看widgets
    我们使用的都是组件封装提供好的。
    Widget 组合的结构是树,所以就有父子关系。
    我们通常用MaterialApp 配合Scaffold 一起使用。
小拓展:widgets中的key:

key这块仅仅是一个小的知识点。如果不是特别理解,入门的时候可以不看。

官网:
https://api.flutter.dev/flutter/widgets/Widget/key.html

控件一个小部件如何替换树中的另一个小部件。

key分类:

  • Local Key
  • Global Key

官网中 youtube视频 ,可以点击字幕选项,这样听起来可能会更好。
https://www.youtube.com/watch?v=kn0EOS-ZiIc

Flutter中key的作用
https://www.jianshu.com/p/57debb89a24f?tdsourcetag=s_pctim_aiomsg
Flutter中Widget之key原理探索
https://www.jianshu.com/p/e9f48141218d?tdsourcetag=s_pctim_aiomsg

Widget 的分类
  • StatefulWidget
    • 可以变化的 Widget,通过setState刷新
  • StatelessWidget
    • 不可以变化的 Widget
基本布局构建

常用的widgets有

  • 文本框
    • text
    • RichText(富文本)
  • 图片
    • Image
  • 输入框(TextField)
  • 对话框(showDialog)

flutter中有几种布局:

我们需要注意的是,大多数widget都是有padding,居中等自带属性。
如果一开始不熟悉布局,我们可以在每个控件之上加上矩形边框(BoxDecoration)来查看每个布局的大小熟悉之后将其去除

盒子模型

三种:

  • 尽可能大的。例如,Center和ListView使用的框。
  • 那些试图和他们的孩子一样大的孩子。例如, Transform 和 Opacity。
  • 那些试图成为特定尺寸的。例如,图像(image)和文本框(Text)

container也是尽可能大,但是给宽度,就遵循这个值。

路由跳转

界面之间跳转通过路由
第一个页面跳转到第二个页面。例如:

Navigator.push(
  context, MaterialPageRoute(builder: (context) => SecondPage()));

返回:

Navigator.pop(context);
flutter第三方插件

pub.dartlang.org/
搜索第三方插件

例如搜索webview
image
里面会有相应的用法

image

在github中可以看到flutter中官方的插件

https://github.com/flutter/plugins/tree/master/packages
flutter_webview_plugin 插件是先于官方出的。

Flutter 的状态管理

Flutter 在设计声明式 UI 上借鉴了不少 React 的设计思想。
状态管理简单理解就是一个页面数据改变之后,其他页面也会改变相同的数据。

全局状态的例子:
用户的数据信息
App 的已读、未读状态数据

常用的框架:

  • Scoped model
  • BLoC
  • Redux
网络

我们可以使用基本http进行网络请求。
还可以使用dio第三方库进行网络请求。

dio使用

yaml 文件配置
yaml相当于Android中的gradle,用来配置项目相关脚本。

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
  json_annotation: ^1.2.0
  dio: ^1.0.6

dev_dependencies:
  build_runner: ^1.0.0
  json_serializable: ^1.5.0

为了便利使用 json_serializable库

https://caijinglong.github.io/json2dart/index_ch.html

选1.xx 文件名写上

注意

part 'Email.g.dart';

类名如果大写的时候 需要修改,默认是小写的


flutter packages pub run build_runner watch

监听之后 会自动生成。
Flutter dio结合json_serializable请求数据并解析Demo

本地数据

官方shared_preferences
pub网址
https://pub.dev/packages/shared_preferences#-installing-tab-

基本shared_preferences工具类将会是这样。

class LocalStorage {
	//保存
  static save(String key, value) async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    prefs.setString(key, value);
  }
	//获取
  static get(String key) async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    return prefs.get(key);
  }
//移除
  static remove(String key) async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    prefs.remove(key);
  }
}
Flutter 与 Native 通信

Flutter毕竟是UI框架,有的时候避免不了使用原生的一些功能或者代码。
这个时候UI和Native之间就需要通信。例如我们的Flutter需要推送功能,我们就需要自己封装一个插件代码。

PlatformChannel

在这里插入图片描述
PlatformChannel 用于 Flutter 与 Native(Android、iOS) 之间的消息传递。

PlatformChannel 是双向的
MethodChannel
EventChannel
BasicMessageChannel

官网中介绍了一种写法:
https://flutter.dev/docs/development/platform-integration/platform-channels

github中有相应的demo。

官方的video_player 源码中用到了event channel和methodChannel。
详细请看 flutter/plugins github
BasicMessageChannel大多数情况下是不用的。

Flutter 性能监控

https://flutterchina.club/debugging/

Flutter打包
Android

build.gradle android下

android {
    signingConfigs {
        release {
            keyAlias keystoreProperties['keyAlias']
            keyPassword keystoreProperties['keyPassword']
            storeFile file(keystoreProperties['storeFile'])
            storePassword keystoreProperties['storePassword']
        }
    }

    buildTypes {
        release {
            signingConfig signingConfigs.release
        }
    }
    ....
    }

在android\app同级目录下建立local.properties文件并且填入

storePassword=你的
keyPassword=你的
keyAlias=你的
storeFile=哪个盘的jks 如: D:/myproject/lalala.jks
ios打包和正常一样
持续交付

Travis CI 是在线托管的持续交付服务,网页上点几下就好,非常方便。
Travis CI官网

Flutter 深入原理

美团
https://tech.meituan.com/2018/08/09/waimai-flutter-practice.html

Flutter混合遇到的问题

如果是新建立一个项目,可以重新建立一个Flutter App。而在大多数已经有的项目中,部分引入flutter会遇到很多的问题。

闲鱼对这部分探索是最多的。闲鱼技术掘金

混合Flutter主要问题:
混合栈管理,减少包大小,flutter音视频,内存, Flutter工具,Flutter webview,业务架构

混合栈管理:flutter_boost 闲鱼的一个框架
减少包大小:带有flutter引擎,资源目录,以及代码实现。
flutter音视频:

有两种方法:

一platfomeview

如果有原生的音视频代码,迁移到flutter上面,platfomeview是一个很好的选择。
在这里插入图片描述

二 TextureWidget

platfomeview在内存上有一定的劣势。
google video_player的demo推荐的方式就是TextureWidget
解码的纹理特征给TextureWidget,通过textureWidget调用Opengl来进行底层渲染,通过textureid来传递。
在这里插入图片描述

内存
flutter工具
flutter webview
业务架构

fish_redux

  • 6
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值