Flutter基础入门
文章目录
- 一、Flutter基础知识
- 1.Flutter简介和发展历史
- 2.Flutter安装和配置
- 3.Dart语言基础知识
- Dart语言特性
- Dart基本语法
- 空安全 Null safety
- Dart异步原理
- 4.Flutter项目结构和文件组织方式
- 5.Flutter Widgets和布局基础
- 二、Flutter进阶知识
- 1.Flutter状态管理和数据传递
- 2.Flutter动画和过渡效果
- 隐式(全自动)动画
- AnimatedContainer
- TweenAnimationBuilder
- 显示(手动控制)动画
- 自定义显示动画:AnimatedBuidler
- 3.Flutter网络请求和数据解析
- 网络请求框架
- 数据解析
- 4.Flutter本地存储和数据缓存
- 5.Flutter渲染和性能优化
- 渲染流程
- 常见的内存溢出和内存泄漏的场景
- 优化检测工具
- Flutter布局优化
- 常规优化
- 深入光栅化优化
- Flutter内存优化
- 6.Flutter代码结构和代码质量把控
- 三、常用插件和第三方库
- 好用的状态管理
- 好用的网络请求
- 好用的图片加载
- 好用的音视频
- 好用的控件
- 好用的工具类
- 好用的动画
- 好用的数据存储
- 好用的事件传递
- 好用的三方sdk
- 好用的编译器插件
- 四、可能有用的小技巧
一、Flutter基础知识
1.Flutter简介和发展历史
Flutter是一款由Google开发的移动应用程序开发框架,可用于开发高性能、跨平台的移动应用程序。Flutter使用Dart编程语言,并提供了丰富的UI组件和工具,使得开发者可以快速构建漂亮、响应式的用户界面和高质量的应用程序。
(图片来源网络,侵删)Flutter的发展历史可以分为以下几个阶段:
-
2011年,Dart语言项目启动,旨在提供一种适用于Web开发的高性能编程语言。
-
2015年,Flutter项目启动,旨在使用Dart语言构建高性能、跨平台的应用程序。
-
2017年,Flutter的第一个Alpha版本发布,开发者可以开始尝试使用Flutter构建应用程序。
-
2018年,Flutter的第一个稳定版本发布,得到了广泛的认可和采用。
-
2019年,Flutter 1.5发布,增加了对iOS13和Android Q的支持,以及许多其他改进和 新功能。
(图片来源网络,侵删) -
2020年,Flutter 1.20发布,引入了新的Web支持和iOS14和Android 11的支持,以及其他改进和新功能。
-
2021年,Flutter2.0发布,引入了许多重要的新功能,包括全新的Web支持、桌面应用程序支持、Flutter for Windows等。
-
2023年,Flutter3.0发布,引入了许多重要的新功能,包括全新的Web支持、桌面应用程序支持、Flutter for Windows等。
截止到2023年3月,由于Flutter生态系统的不断发展,Flutter在Google Play Store和Apple App Store上已经有超过400,000款应用程序上线。同时,Flutter还正在扩展到其他领域,例如Web开发和桌面应用程序开发,这些领域的应用程序数量也在不断增长。由于Flutter是一个开源框架,许多公司和个人也在使用Flutter构建自己的应用程序,并且Flutter的生态系统也在不断扩大,因此Flutter上线的应用程序数量可能会随着时间的推移而继续增长。
2.Flutter安装和配置
以下是安装和配置Flutter的步骤,以Windows为例:
- 下载Flutter SDK
可以在Flutter的官方网站上下载Flutter SDK。下载完成后,将其解压到您选择的目录中,例如在Windows上,您可以将其解压到 C:\src\flutter。
(图片来源网络,侵删)-
配置Flutter环境变量
将Flutter SDK的路径添加到PATH环境变量中,以便在终端中可以使用flutter命令。例如,在Windows上,您可以将C:\src\flutter\bin添加到系统环境变量的PATH变量中。
-
下载开发工具并安装Flutter插件
如果您使用的是Android Studio或IntelliJ IDEA,您需要安装Flutter插件以便在IDE中开发Flutter应用程序。打开IDE,进入插件设置,搜索“Flutter”,然后安装并重启IDE即可。
-
运行Flutter Doctor
打开终端或命令提示符,并输入“flutter doctor”命令,以检查Flutter环境的配置情况。Flutter doctor会检查您的环境并给出建议以解决任何缺失的依赖项或配置问题。
-
配置Android开发环境
如果您要在Flutter中开发Android应用程序,您需要安装并配置Android开发环境。您可以在Android开发者网站上获取有关如何安装和配置Android开发环境的更多信息。
以上是在Windows上安装和配置Flutter的基本步骤。完成这些步骤后,您就可以开始使用Flutter构建应用程序了。
3.Dart语言基础知识
Dart语言特性
-
Dart 的特性
Dart 是少数同时支持 JIT(Just In Time,即时编译)和 AOT(Ahead of Time,运行前编译)的语言之一。语言在运行之前通常都需要编译,JIT 和 AOT 则是最常见的两种编译模式。
-
JIT 在运行时即时编译,在开发周期中使用,可以动态下发和执行代码,开发测试效率高,但运行速度和执行性能则会因为运行时即时编译受到影响。
-
AOT 即提前编译,可以生成被直接执行的二进制代码,运行速度快、执行性能表现好,但每次执行前都需要提前编译,开发测试效率低。
总结来讲,在开发期使用 JIT 编译,可以缩短产品的开发周期。Flutter 最受欢迎的功能之一热重载,正是基于此特性。而在发布期使用 AOT,就不需要像 React Native 那样在跨平台 JavaScript 代码和原生 Android、iOS 代码之间建立低效的方法调用映射关系。所以说,Dart 具有运行速度快、执行性能好的特点。
-
内存分配与垃圾回收
Dart VM 的内存分配策略比较简单,创建对象时只需要在堆上移动指针,内存增长始终是线性的,省去了查找可用内存的过程。
Dart 的垃圾回收,则是采用了多生代算法。新生代在回收内存时采用“半空间”机制,触发垃圾回收时,Dart 会将当前半空间中的“活跃”对象拷贝到备用空间,然后整体释放当前空间的所有内存。回收过程中,Dart 只需要操作少量的“活跃”对象,没有引用的大量“死亡”对象则被忽略,这样的回收机制很适合 Flutter 框架中大量 Widget 销毁重建的场景。
-
单线程模型
Dart 中并没有线程,只有 Isolate(隔离区)。Isolates 之间不会共享内存,就像几个运行在不同进程中的 worker,它们通过事件循环(Event Looper)在事件队列(Event Queue)上传递消息通信。所以如果想要在 Dart 中实现并发是可以通过 Isolate 实现的。Isolate 的这种类似于线程但不共享内存,独立运行的 worker的机制,就可以让 Dart 实现无锁的快速分配。
-
无需单独的声明式布局语言
Dart 声明式编程布局易于阅读和可视化,使得 Flutter 并不需要类似 JSX 或 XML 的声明式布局语言。所有的布局都使用同一种格式,也使得 Flutter 很容易提供高级工具使布局更简单,就突出一个上手简单。
Dart基本语法
- 主函数(入口函数)
void main(List arguments) { print('Hello world! ${arguments}'); }
- 函数
// 1,函数创建 // 2,函数传值 // 3. 可选参数 printName(String name, int age, [String sex = '女']) { print("name is ${name}, age is ${age}, sex is ${sex}") } // 4. 命名函数 printInfo({name, age=4, sex}) { print("name is ${name}, age is ${age}, sex is ${sex}") } void main(List arguments) { printName('果果', 18); printInfo({name: '果果', age: 4, sex: '女'}); }
-
基本类型
Dart支持的基础数据类型和其他语言基本一样
// 基本类型 // int int age = 20; // double double count = 10.0; // String String name="果果"; // bool bool flag = true; // List List list = [1,2,3,4,5,6]; // Set Set set = new Set(); set.addAll(list); // Map Map user = {'name': 'bajie', 'age': 18}; // 常量 const final /* * 区别: * const 必须先赋初值 * final 可以后面赋值一次 */ // 变量 var // 类型可推导 => var user = {'name': '果果', 'age': 4}; print("${user['name']}")
- 面向对象
// 创建一个类 // lib/animal.dart class Animal { String? name; Animal({this.name}); Animal.fromJson(Map json) { name = json['name']; } Map toJson() { final Map data = {}; data['name'] = name; return data; } void eat() { print('$name is eating!'); } } // main.dart void main() { Animal a = Animal(name: '果果'); a.eat(); } 输出结果:果果 is eating
- 类的继承
// 继承一个类 // lib/cat.dart class Cat extends Animal { } void main() { Cat cat = Cat(); cat.name = "colala"; cat.eat(); } 输出结果:colala is eating
多态
// lib/cat.dart class Cat { // 重写一个方法 drink() { print('$name is drinking!'); } } void main() { Cat cat = Cat(); cat.name = "colala"; cat.eat(); cat.drink(); } 输出结果:colala is eating 输出结果:colala is drinking
- 类的实现
Dart语言中没有接口(interface)的关键字,但是有实现(implements)关键字,Dart中可以将类(是否为抽象无关)当做隐式接口直接使用,当需要使用接口时,可以声明类来代替。
// 抽象类作为接口 abstract class Sleep{ void sleep(); } // 普通类作为接口 class Play{ void play(){} } class Cat extends Animal implements Sleep, Play { @override void eat() { print("eat"); } @override void sleep() { print(" is sleeping"); } @override void play() { print("is beating colala"); } } void main() { var cat = = Cat(); cat.name = "果果"; cat.eat(); cat.sleep(); cat.play(); } 输出结果:果果 is eating 输出结果:果果 is sleeping 输出结果:果果 is beating colala
-
类的混入
mixin一般用于描述一种具有某种功能的组块,而某一对象可以拥有多个不同功能的组块。
mixin用于修饰类,和abstract类似,该类可以拥有成员变量、普通方法、抽象方法,但是不可以实例化。
mixins不是一种在经典意义上获得多重继承的方法。
mixins是一种抽象和重用一系列操作和状态的方法。
它类似于扩展类所获得的重用,但它与单继承兼容,因为它是线性的。
- 简单应用
最简单的mixin由mixin & with关键字组成。
‘教师‘ 一种能力是 ‘绘画’
void main() { Teacher().draw(); } mixin DrawFunc { String content = '..'; String what(); void draw() { print('I can draw ${what()}'); } } class Teacher with DrawFunc { String what() => "car"; }
- 限定类型(mixin…on)
当我们在mixin上使用了on关键字,那么mixin只能在那个类的子类上使用,而mixin可以调用那个类的方法。
限定 ‘绘画’ 这种能力只能够用在 ‘人类’ 上面
void main() { Teacher().draw(); } class Person {} mixin DrawFunc on Person { String content = '..'; String what(); void draw() { print('I can draw ${what()}'); } } class Teacher extends Person with DrawFunc { String what() => "car"; }
- 多个类型
在 ‘绘画’ 的基础上,我们增加一种新的能力 ‘唱歌’
void main() { Teacher().draw(); Teacher().sing(); } class Person {} mixin DrawFunc on Person { String content = '..'; String what(); void draw() { print('I can draw ${what()}'); } } mixin SingFunc on Person { void sing() { print('I can sing'); } } class Teacher extends Person with DrawFunc, SingFunc { String what() => "car"; }
- 组合组块
- mixin:定义了组块。
- on:限定了使用mixin组块的宿主必须要继承于某个特定的类;在mixin中可以访问到该特定类的成员和方法。
- with:负责组合组块,而with后面,这一点需要注意,例如下面这样:
void main() { Teacher().draw(); Teacher().sing(); Teacher().dance(); } class Person {} mixin DrawFunc on Person { String content = '..'; String what(); void draw() { print('I can draw ${what()}'); } } mixin SingFunc on Person { void sing() { print('I can sing'); } } abstract class DanceFunc { void dance() { print('I can dance'); } } class Teacher extends Person with DrawFunc, SingFunc, DanceFunc { String what() => "car"; }
总结就是,mixin可以理解为一个个的功能组块,哪些宿主需要哪些功能就with到上去。
on关键字一方面是为了限制组块的应用场景,也可以为多个组块提供公共的基础功能。
空安全 Null safety
- 空安全的目的是让开发人员对代码中的 Null 可见且可控,并且确保它不会传递至某些位置从而引发崩溃。相关关键字有以下这些:
- ?
- !
- late
如果可以为空值的变量(null), 在类型声明处加上 ?。
String? name = null;
在您已经明确一个非空变量一定会在使用前初始化,而 Dart 分析器仍然无法明确的情况下,您可以在变量的类型前加上late。
void main() { late TextEditingController textEditingController; init() { textEditingController = TextEditingController(); } }
当您正在调用一个可空的变量或者表达式时,请确保您自己处理了空值。例如:您可以使用 if 条件句、?? 操作符 或是 ?. 操作符来处理可能为空的值。
// 使用 ?? 操作符来避免将非空变量赋予空值 Cat cat = Cat(); String name = cat.name ?? "果果";
如果您能确定一条可空的表达式不为空,您可以在其后添加 ! 让 Dart 处理为非空。
Cat cat = Cat(); cat.name = "果果"; String name = cat.name!;
一旦您开始使用空安全,当操作对象可能为空时,您将不再能使用成员访问符(.),取而代之的是可空版本的?.
Cat? cat; String? name = cat?.name;
Dart异步原理
Dart 是一门单线程编程语言。异步 IO + 事件循环,Dart异步主要可以通过以下关键字来实现:
- Future
- async
- await
Future 对象封装了Dart 的异步操作,它有未完成(uncompleted)和已完成(completed)两种状态。completed 状态也有两种:一种是代表操作成功,返回结果;另一种代表操作失败,返回错误。
Future fetchUserName() { //想象这是个耗时的获取用户名称操作 return Future(() => '果果'); } void main() { fetchUserName().then((result){print(result)}) print('after fetch user name ...'); }
通过.then来回调成功结果,main会先于Future里面的操作,输出结果:
flutter: after fetch user name ... flutter: 果果
那如果我们想要先打印名称咋办,换一下掉用方式
Future fetchUserName() { //想象这是个耗时的获取用户名称操作 return Future(() => '果果'); } void main() { fetchUserName().then((result){ print(result); print('after fetch user name ...'); } ) }
输出结果:
flutter: 果果 flutter: after fetch user name ...
Future 同名构造器是 factory Future(FutureOr computation()),它的函数参数返回值为 FutureOr 类型,我们发现还有很多 Future 中的方法比如Future.then、Future.microtask 的参数类型也是 FutureOr,这个对象其实是个特殊的类型,它没有类成员,不能实例化,也不可以继承,只是一个语法糖。
abstract class FutureOr { FutureOr._() { throw new UnsupportedError("FutureOr can't be instantiated"); } }
想象一个这样的场景:
- 调用登录接口;
- 根据登录接口返回的token获取用户信息;
- 缓存用户信息到本机。
Future login(String name,String password){ //登录 } Future fetchUserInfo(String token){ //获取用户信息 } Future saveUserInfo(User user){ // 保存用户信息 }
用 Future 可以这样写:
login('name','password') .then((token) => fetchUserInfo(token)) .then((user) => saveUserInfo(user));
但是这种看着有点别扭,所以我们可以换成关键字处理,换成async和await则可以这样:
void doLogin() async { String token = await login('name','password'); //await 必须在 async 函数体内 User user = await fetchUserInfo(token); await saveUserInfo(user); }
需要注意的是如果声明了 async 的函数,返回值是必须是 Future 对象。即便你在 async 函数里面直接返回 T 类型数据,编译器会自动帮你包装成 Future 类型的对象,如果是 void 函数,则返回 Future 对象。在遇到 await 的时候,又会把 Futrue 类型拆包,又会原来的数据类型暴露出来,所以请注意,await 所在的函数必须添加 async 关键词。
await 的代码发生异常,捕获方式跟同步调用函数一样:
void doLogin() async { try { var token = await login('name','password'); var user = await fetchUserInfo(token); await saveUserInfo(user); } catch (err) { print('Caught error: $err'); } }
语法糖
Future getUserInfo() async { return 'aaa'; } //等价于: Future getUserInfo() async { return Future.value('aaa'); }
Completer
在Flutter中,Completer是一个类,用于实现异步操作的等待和结果处理。Completer允许您创建一个Future对象,并在该对象的值(或错误)可用时解决该对象。
Completer对象包含两个主要方法:complete()和completeError()。complete()方法将Future对象标记为已完成,并将其结果设置为指定的值,而completeError()方法将Future对象标记为已完成,并将其结果设置为指定的错误。使用Completer时,您可以通过Future对象的then()方法或await关键字来等待异步操作的结果。
以下是一个示例,演示如何使用Completer来处理异步操作的结果:
Completer completer = Completer(); // 假设在两秒钟后异步返回一个值 Future.delayed(Duration(seconds: 2), () { completer.complete(42); }); // 使用then()方法等待异步操作的结果 completer.future.then((value) { print('异步操作的结果为:$value'); });
在上面的示例中,创建了一个Completer对象,并通过Future.delayed()方法模拟一个异步操作,该操作在两秒钟后返回一个值。然后,使用then()方法等待异步操作的结果,并在结果可用时打印该结果。
Completer是Flutter中处理异步操作的重要工具之一,特别是在需要处理多个异步操作的情况下,Completer可以更好地控制异步操作的执行顺序和结果处理。
Isolate
在Flutter中,Isolate是一种轻量级的、独立的执行线程,它与主线程(也称为UI线程)并行运行,并可以执行CPU密集型任务,而不会阻塞UI线程。Flutter中的每个Isolate都有自己的内存空间,可以独立于其他Isolate进行操作。
Isolate可以在Flutter应用程序中实现并发性,可以将一些耗时的任务(如网络请求、文件操作、复杂计算等)分配给Isolate,从而使UI线程不会被阻塞,从而提高应用程序的性能和响应速度。
Flutter中可以通过使用compute()函数或Isolate.spawn()方法来创建和使用Isolate。compute()函数是Flutter提供的一个方便的API,它允许您在后台Isolate中执行耗时的计算,并在完成后返回结果。例如,以下代码演示了如何使用compute()函数来计算斐波那契数列的第20项:
int fibonacci(int n) { if (n
- 空安全的目的是让开发人员对代码中的 Null 可见且可控,并且确保它不会传递至某些位置从而引发崩溃。相关关键字有以下这些:
-
- 类的实现
- 类的继承
- 面向对象
-
- 函数
- 主函数(入口函数)
-
-
还没有评论,来说两句吧...