(一)初识Flutter
1.Flutter是什么
Flutter is Google's UI toolkit for building beautiful, natively compiled applications for mobile, web, desktop, and embedded devices from a single codebase.
官方是这么描述的,简单来说,Flutter是一个UI SDK (Software Development Kit),可以进行移动端(iOS、Android),Web端,桌面的跨平台解决方案
2.Flutter的特点
美观
使用Flutter内置的Material Design 和Cupertino widget 、丰富的motion API ,平滑自然的滑动效果和平台感知,为用户带来全新的体验快速
Flutter引擎使用C++编写,包括高效的Skia 2D渲染引擎,Dart 运行时和文本渲染库高效
Hot Reload开放
开源
3.Flutter 绘制原理
- GPU将信号同步到UI线程
- UI线程用Dart构建图层树
- 图层树在GPU线程进行合成
- 合成后将视图数据提供给Skia引擎
- Skia引擎通过OpenGL 或者 Vulkan 将内容提供给GPU
4.渲染引擎 Skia
Skia(全称Skia Graphics Library
(SGL)
)就是Flutter向GPU提供数据的途径,是一个C++编写的开源图形库,能在低端设备如手机上呈现高质量的2D图形,最初由Skia公司开发,后被Google收购
- 应用于Android、Google Chrome、Chrome OS等等当中
- 目前, Skia已经是Android官方的图像渲染弓|擎了,因此Flutter Android
SDK无需内嵌Skia弓|擎就可以获得Skia支持 - 而对于iOS平台来说,由于Skia是跨平台的,因此它作为Flutter ioS渲染引
擎被嵌入到Flutter的iOS SDK中,替代了iOS闭源的Core Graphics/Core
Animation/Core Text ,这也正是Flutter iOS SDK打包的App包体积比
Android要大一些的原因 - 底层渲染能力统一了,上层开发接口和功能体验也就随即统一了,开发者再也不用操心平台相关的渲染特性了。也就是说, Skia保证了同一套代码调用在
Android和iOS平台上的渲染效果是完全一致的。
(二)环境搭建
1.安装Flutter SDK (MacOS为例)
去官网下载最新的安装包 Flutter SDK releases - Flutter
1.解压到想要安装的路径
cd ~/development
unzip ~/Downloads/flutter_macos_v0.5.1-beta.zip
3.添加flutter相关工具到path中:
export PATH=`pwd`/flutter/bin:$PATH
4.运行 flutter doctor
运行以下命令查看是否需要安装其它依赖项来完成安装:
flutter doctor
5.配置环境
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
6.开发工具选择
官方推荐 Android Studio 或者 VSCode
(三)Flutter初体验
1.创建Flutter项目
可以通过两种方式创建,1 终端 2.编辑器 ,以终端为例
flutter create myapp
$ cd myapp
上述命令创建一个Flutte让项目,项目名为myapp
,其中包含一个使用Material 组件的简单演示应用程序。
在项目目录中,您的应用程序的代码位于 lib/main.dart
.
2.运行应用程序
- 检查设备是否在运行。如果没有显示, 请参照 设置.
$ flutter devices
- 运行
flutter run
命令来运行应用程序:
$ flutter run
3.体验热重载
Flutter 可以通过 热重载(hot reload) 实现快速的开发周期,热重载就是无需重启应用程序就能实时加载修改后的代码,并且不会丢失状态(译者语:如果是一个web开发者,那么可以认为这和webpack的热重载是一样的)。简单的对代码进行更改,然后告诉IDE或命令行工具你需要重新加载(点击reload按钮),你就会在你的设备或模拟器上看到更改。
打开文件lib/main.dart
将字符串
'You have pushed the button this many times:' 更改为
'You have clicked the button this many times:'
不要按“停止”按钮; 让您的应用继续运行.
要查看您的更改,请调用 Save (cmd-s / ctrl-s), 或者点击 热重载按钮 (带有闪电图标的按钮).
(四) 认识 Dart
1.安装Dart
为什么还需要安装Dart呢?
事实上在安装Flutter SDK的时候,它已经内置了Dart了,我们完全可以直接使用Flutter去进行Dart的编写并且运行。
但是,如果你想单独学习Dart,并且运行自己的Dart代码,最好去安装一个Dart SDK。
官方网站:https://dart.dev/get-dart,下载完成后,根据路径配置环境变量即可。
以VSCode为例,去扩展添加Dart插件
2.hello Dart
在VSCode中新建一个helloWorld.dart文件
main(List args) {
print('Hello World');
}
执行快捷键 control + option + N
3.程序分析
一、Dart语言的入口也是main函数,并且必须显示的进行定义;
二、Dart的入口函数main是没有返回值的;
三、传递给main的命令行参数,是通过List
从字面值就可以理解List是Dart中的集合类型。
其中的每一个String都表示传递给main的一个参数;
四、定义字符串的时候,可以使用单引号或双引号;
五、每行语句必须使用分号结尾,很多语言并不需要分号,比如Swift、JavaScript;
(五)Dart 语法
1. 明确声明(Explicit)
明确声明变量的方式, 格式如下:
变量类型 变量名称 = 赋值;
String name = 'abc';
int age = 18;
double height = 1.88;
print('${name}, ${age}, ${height}'); // 拼接
注意事项: 定义的变量可以修改值, 但是不能赋值其他类型
2. 类型推导(Type Inference)
类型推导声明变量的方式, 格式如下:
var/dynamic/const/final 变量名称 = 赋值
2.1. var的使用
runtimeType用于获取变量当前的类型
var name = 'abc';
name = 'kobe';
print(name.runtimeType); // String
//var age = 18;
//age = 'why'; // 不可以将String赋值给一个int类型
2.2. dynamic的使用
dynamic name = 'abc';
print(name.runtimeType); // String
name = 18;
print(name.runtimeType); // int
2.3. final&const的使用
final和const都是用于定义常量的, 也就是定义之后值都不可以修改
final name = 'coderwhy';
name = 'kobe'; // 错误
const age = 18;
age = 20; // 错误
final和const有什么区别呢?
const在赋值时, 赋值的内容必须是在编译期间就确定下来的
final在赋值时, 可以动态获取, 比如赋值一个函数
String getName() {
return 'abc';
}
main(List args) {
const name = getName(); // 错误的做法, 因为要执行函数才能获取到值
final name = getName(); // 正确的做法
}
3.数据类型
3.1 数字类型
对于数值来说,我们也不用关心它是否有符号,以及数据的宽度和精度等问题。只要记着整数用int
,浮点数用double
就行了。Dart中的int
和double
可表示的范围并不是固定的,它取决于运行Dart的平台。
// 1.整数类型int
int age = 18;
int hexAge = 0x12;
print(age);
print(hexAge);
// 2.浮点类型double
double height = 1.88;
print(height);
字符串和数字间转化
// 1.字符串转数字
var one = int.parse('111');
var two = double.parse('12.22');
print('${one} ${one.runtimeType}'); // 111 int
print('${two} ${two.runtimeType}'); // 12.22 double
// 2.数字转字符串
var num1 = 123;
var num2 = 123.456;
var num1Str = num1.toString();
var num2Str = num2.toString();
var num2StrD = num2.toStringAsFixed(2); // 保留两位小数
print('${num1Str} ${num1Str.runtimeType}'); // 123 String
print('${num2Str} ${num2Str.runtimeType}'); // 123.456 String
print('${num2StrD} ${num2StrD.runtimeType}'); // 123.46 String
3.2 bool类型
Dart提供了一个bool的类型, 取值为true和false
var isFlag = true;
print('$isFlag ${isFlag.runtimeType}');
Dart中不能判断非0即真, 或者非空即真
不能使用if(非booleanvalue)或assert(非booleanvalue)之类的代码。
var message = 'Hello Dart';
// 错误的写法
if (message) {
print(message)
}
3.3 字符串类型
Dart字符串是UTF-16编码单元的序列。可以使用单引号或双引号创建一个字符串:
// 1.定义字符串的方式
var s1 = 'Hello World';
var s2 = "Hello Dart";
var s3 = 'Hello\'Fullter';
var s4 = "Hello'Fullter"
可以使用三个单引号或者双引号表示多行字符串:
// 2.表示多行字符串的方式
var message1 = '''
哈哈哈
呵呵呵
嘿嘿嘿''';
字符串和其他变量或表达式拼接: 使用${expression}
, 如果表达式是一个标识符, 那么{}可以省略
// 3.拼接其他变量
var name = 'ABC';
var age = 18;
var height = 1.88;
print('my name is ${name}, age is $age, height is $height');
3.4 集合类型
3.4.1 集合类型的定义
Dart内置了最常用的三种:List / Set / Map
。
// List定义
// 1.使用类型推导定义
var letters = ['a', 'b', 'c', 'd'];
print('$letters ${letters.runtimeType}');
// 2.明确指定类型
List numbers = [1, 2, 3, 4];
print('$numbers ${numbers.runtimeType}');
// Set的定义
// 1.使用类型推导定义
var lettersSet = {'a', 'b', 'c', 'd'};
print('$lettersSet ${lettersSet.runtimeType}');
// 2.明确指定类型
Set numbersSet = {1, 2, 3, 4};
print('$numbersSet ${numbersSet.runtimeType}');
Set和List最大的两个不同就是:Set是无序的,并且元素是不重复的。
// Map的定义
// 1.使用类型推导定义
var infoMap1 = {'name': 'why', 'age': 18};
print('$infoMap1 ${infoMap1.runtimeType}');
// 2.明确指定类型
Map infoMap2 = {'height': 1.88, 'address': '北京市'};
print('$infoMap2 ${infoMap2.runtimeType}');
3.4.2 集合的常见操作
// 获取集合的长度
print(letters.length);
print(lettersSet.length);
print(infoMap1.length);
// 添加/删除/包含元素
numbers.add(5);
numbersSet.add(5);
print('$numbers $numbersSet');
numbers.remove(1);
numbersSet.remove(1);
print('$numbers $numbersSet');
print(numbers.contains(2));
print(numbersSet.contains(2));
// List根据index删除元素
numbers.removeAt(3);
print('$numbers');
// Map的操作
// 1.根据key获取value
print(infoMap1['name']);
// 2.获取所有的entries
print('${infoMap1.entries} ${infoMap1.entries.runtimeType}'); // (MapEntry(name: why), MapEntry(age: 18)) MappedIterable>
// 3.获取所有的keys
print('${infoMap1.keys} ${infoMap1.keys.runtimeType}'); // (name, age) _CompactIterable
// 4.获取所有的values
print('${infoMap1.values} ${infoMap1.values.runtimeType}'); // (why, 18) _CompactIterable