文章目录

  • 一、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为例:

  1. 下载Flutter SDK

可以在Flutter的官方网站上下载Flutter SDK。下载完成后,将其解压到您选择的目录中,例如在Windows上,您可以将其解压到 C:\src\flutter。

  1. 配置Flutter环境变量

    将Flutter SDK的路径添加到PATH环境变量中,以便在终端中可以使用flutter命令。例如,在Windows上,您可以将C:\src\flutter\bin添加到系统环境变量的PATH变量中。

  2. 下载开发工具并安装Flutter插件

    如果您使用的是Android Studio或IntelliJ IDEA,您需要安装Flutter插件以便在IDE中开发Flutter应用程序。打开IDE,进入插件设置,搜索“Flutter”,然后安装并重启IDE即可。

  3. 运行Flutter Doctor

    打开终端或命令提示符,并输入“flutter doctor”命令,以检查Flutter环境的配置情况。Flutter doctor会检查您的环境并给出建议以解决任何缺失的依赖项或配置问题。

  4. 配置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<String> 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<String> 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.dartclass Animal {String? name;Animal({this.name});Animal.fromJson(Map<String, dynamic> json) {name = json['name'];}Map<String, dynamic> toJson() {final Map<String, dynamic> data = <String, dynamic>{};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 {@overridevoid eat() {print("eat");}@overridevoid sleep() {print(" is sleeping");}@overridevoid 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是一种抽象和重用一系列操作和状态的方法。
    它类似于扩展类所获得的重用,但它与单继承兼容,因为它是线性的。

  1. 简单应用

最简单的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";
}
  1. 限定类型(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";
}
  1. 多个类型

在 ‘绘画’ 的基础上,我们增加一种新的能力 ‘唱歌’

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";
}
  1. 组合组块
  • 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<String> 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<String> 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<T> {FutureOr._() {throw new UnsupportedError("FutureOr can't be instantiated");}
}

想象一个这样的场景:

  • 调用登录接口;
  • 根据登录接口返回的token获取用户信息;
  • 缓存用户信息到本机。
Future<String> login(String name,String password){//登录
}Future<User> 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<String> getUserInfo() async {return 'aaa';
}//等价于:
Future<String> getUserInfo() async {return Future.value('aaa');
}

Completer

在Flutter中,Completer是一个类,用于实现异步操作的等待和结果处理。Completer允许您创建一个Future对象,并在该对象的值(或错误)可用时解决该对象。

Completer对象包含两个主要方法:complete()和completeError()。complete()方法将Future对象标记为已完成,并将其结果设置为指定的值,而completeError()方法将Future对象标记为已完成,并将其结果设置为指定的错误。使用Completer时,您可以通过Future对象的then()方法或await关键字来等待异步操作的结果。

以下是一个示例,演示如何使用Completer来处理异步操作的结果:

Completer<int> completer = Completer<int>();// 假设在两秒钟后异步返回一个值
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 <= 0) {return 0;} else if (n == 1) {return 1;} else {return fibonacci(n - 1) + fibonacci(n - 2);}
}Future<int> calculateFibonacci(int n) async {return await compute(fibonacci, n);
}int result = await calculateFibonacci(20);

Isolate.spawn()方法允许您创建新的Isolate,并将指定的函数作为Isolate的入口点来执行。例如,以下代码演示了如何使用Isolate.spawn()方法来创建一个新的Isolate,并在其中执行耗时的计算:

Future<void> calculate() async {ReceivePort receivePort = ReceivePort();await Isolate.spawn(calculateFibonacci, 20, onExit: receivePort.sendPort);int result = await receivePort.first;print('计算结果为:$result');
}void calculateFibonacci(int n) {// 计算斐波那契数列的第n项,并将结果发送到主Isolateint result = fibonacci(n);SendPort sendPort = IsolateNameServer.lookupPortByName('main_send_port');sendPort.send(result);
}

在上面的示例中,首先创建一个ReceivePort对象,然后使用Isolate.spawn()方法创建一个新的Isolate,并将calculateFibonacci函数作为入口点来执行。在calculateFibonacci函数中,计算斐波那契数列的第n项,并将结果发送到主Isolate。在calculate()函数中,使用ReceivePort来等待新Isolate的结果,并将其打印出来。

Isolate是Flutter中处理并发性和异步操作的重要工具之一,特别是在需要执行耗时的计算或操作的情况下,使用Isolate可以使应用程序保持流畅并响应迅速。

那么这两个如何联合使用呢?

通常,Completer对象用于异步操作的等待和结果处理。当需要执行耗时的操作时,可以创建一个新的Isolate来执行该操作,并使用Completer来等待操作完成并获取结果。在Isolate中,可以使用SendPort来将结果发送回主Isolate,然后在主Isolate中使用Completer来获取结果。

以下是一个示例,演示如何使用Completer和Isolate一起实现异步操作和并发执行:

Future<int> calculateFibonacci(int n) async {Completer<int> completer = Completer<int>();ReceivePort receivePort = ReceivePort();await Isolate.spawn(calculate, [n, receivePort.sendPort]);receivePort.listen((result) {completer.complete(result);receivePort.close();});return completer.future;
}void calculate(List<dynamic> args) {int n = args[0];SendPort sendPort = args[1];// 计算斐波那契数列的第n项int result = fibonacci(n);// 将结果发送回主IsolatesendPort.send(result);
}int fibonacci(int n) {if (n <= 0) {return 0;} else if (n == 1) {return 1;} else {return fibonacci(n - 1) + fibonacci(n - 2);}
}int result = await calculateFibonacci(20);

在上面的示例中,首先创建一个Completer对象和一个ReceivePort对象。然后使用Isolate.spawn()方法创建一个新的Isolate,并将calculate函数作为入口点来执行。在calculate函数中,计算斐波那契数列的第n项,并将结果发送到主Isolate。在主Isolate中,通过监听ReceivePort来等待新Isolate的结果,并使用Completer来获取结果。

使用Completer和Isolate一起实现异步操作和并发执行可以提高应用程序的性能和响应速度,特别是在需要执行耗时的操作时。但是需要注意,使用Isolate也会带来一些额外的开销和复杂性,因此应根据实际需求进行权衡和选择。

4.Flutter项目结构和文件组织方式

在哪里归档图片资源以及如何处理不同分辨率?

虽然Android将resources和assets区别对待,但在Flutter中它们都会被作为assets处理,所有存在于Androidres/drawable文件夹中的资源都放在Flutter的assets文件夹中。与Android类似,iOS同样将images和assets作为不同的东西,而Flutter中只有assets。被放到iOS中Assets.xcassets文件夹下的资源在Flutter中被放到了assets文件夹中。在Flutter中assets可以是任意类型的文件,而不仅仅是图片。例如,你可以把json文件data.json放置到assets文件夹中。

在pubspec.yaml文件中声明assets

assets:-assets/data.json

然后在代码中我们可以通过AssetBundle来访问它:

import 'dart:async;
import package flutter services.dart;Future<String>loadAsset() async {retum await rootBundle.loadString("assets/data.json");
}

对于图片,Flutter像iOS一样,遵循了一个简单的基于像素密度的格式。Imageassets可能是1.0x 2.0x 3.0x或是其他的任何倍数。这个device PixelRatio表示了物理像素到单个逻辑像素的比率。
Android不同像素密度的图片和Flutter的像素比率的对应关系

  • ldpi 0.75x
  • mdpi 1.0x
  • hdpi 1.5x
  • xhdpi 2.0x
  • xxhdpi 3.0x
  • xxxhdpi 4.0x

Assets可以被放置到任何属性文件夹中,Flutter并没有预先定义的文件结构。我们需要在pubspec.yaml文件中声明assets的位置,然后 Flutter会把他们识别出来。举个例子

Flutter基础入门相关推荐

  1. Flutter 项目开发指导 从基础入门到精通使用目录

    Flutter 从入门 到精通系列文章 本文章为 Flutter 开发中的经验积累分享.教程分享.开发笔记分享目录,持续维护中. 题记 -- 执剑天涯,从你的点滴积累开始,所及之处,必精益求精. Fl ...

  2. Flutter基础(三)Dart快速入门

    本文首发于公众号「刘望舒」 关联系列 ReactNative入门系列 React Native组件 Flutter基础系列 前言 Dart是Flutter SDK指定的语言,因此要学习Flutter, ...

  3. Flutter基础—开发环境与入门

    Flutter开发环境的前提要求如下: Windows 7以上版本(64位).Mac或Linux操作系统(64位) 磁盘空间:400 MB(不包括Android Studio的磁盘空间). 已经安装及 ...

  4. Flutter从入门到进阶

    Flutter从入门到进阶 <Flutter简介> Dart入门 <Dart入门-基础类型与正则> <Dart入门-集合类型> <Dart入门-函数与运算符& ...

  5. Flutter基础—你好,Flutter!

    什么是Flutter Flutter是一个移动应用程序的软件开发工具包(SDK),用一个代码库构建高性能.高保真的iOS和Android应用程序.目标是使开发人员能够为Android和iOS提供自然的 ...

  6. Flutter基础(四)开发Flutter应用前需要掌握的Basic Widget

    本文首发于公众号「刘望舒」 关联系列 ReactNative入门系列 React Native组件 Flutter基础系列 前言 学完了Dart语言,接下来就可以学习Widget了,Flutter的U ...

  7. Flutter基础布局组件及实现

    https://www.cnblogs.com/lxlx1798/p/11084904.html 一,概述 Flutter中拥有30多种预定义的布局widget,常用的有Container.Paddi ...

  8. 【Flutter从入门到实战】⑪、豆瓣案例-1、星星评分Widget、虚线Widget、TabbarWidget、BottomNavigationBarItem的封装、初始化配置抽取

    Flutter从入门到实战 一共分为23个系列 ①(Flutter.Dart环境搭建篇) 共3个内容 已更新 ②(Dart语法1 篇) 共4个内容 已更新 ③(Dart语法2 篇) 共2个内容 已更新 ...

  9. 新书推荐 | Flutter技术入门与实战(第2版)

    新书推荐 <Flutter技术入门与实战(第2版)> 长按二维码 了解及购买 从实战角度出发,手把手教会Flutter,案例丰富,实操性强. 编辑推荐 本书在上一版的基础上,根据Flutt ...

最新文章

  1. ubuntu 14.04 mysql 5.7_ubuntu14.04 升级mysql到5.7版本
  2. 【转】一个Java程序员应该掌握的10项技能
  3. linux 命令行编辑常用快捷键
  4. html表单中的数据提交后清空,怎么实现小程序中表单提交后自动清空内容
  5. OpenCV‘s Kalman filter卡尔曼滤波器的实例(附完整代码)
  6. Linux网络协议栈(二)——套接字缓存(socket buffer)
  7. 为什么这么多烂代码?
  8. 产品经理教你玩转阿里云负载均衡SLB系列(一):快速入门--什么是负载均衡
  9. 牛客国庆集训派对Day3: G. Stones(博弈+SG)
  10. 支付宝接口调试经验总结
  11. galleryview-3.0b3使用小记
  12. 写出Oracle分页语句,Oracle分页语句
  13. 小米电视换鸿蒙,DIY 篇一:迟来的小米电视主机改造,完美增加红外接收(红外遥控)...
  14. python爬取美女图片_Python 爬取美女图片
  15. android uikit框架详解,UIKit框架(2)框架简介
  16. crystal xcelsius 的使用
  17. 大健康产业B2B2C多用户电商平台开发,开启数字化消费新蓝海
  18. 合并写(write combining)
  19. 【软件应用】word数学公式插件mathtype安装
  20. 运维入门之CPU平均负载及问题排查

热门文章

  1. npm学习:安装、更新以及管理npm版本
  2. OSCP-Wombo(Redis 4.x-5.x)
  3. 如何利用js实现萤火虫效果
  4. CSS中 块级元素、行内元素、行内块元素区别
  5. Golang --Merkle tree
  6. 手把手教你搭建自己的Java Web(Android)项目(SpringMVC + Mybatis服务端,Html5 Web端, Android客户端实现)
  7. regexbuddy 教程
  8. html对话框特效,基于html5手机移动端对话框特效
  9. 深入理解计算机系统读书笔记(第二章 信息的表示和处理)
  10. mysql主从同步(3)-percona-toolkit工具(数据一致性监测、延迟监控)使用梳理