本系列文章共有三篇,计划分别从 Flutter、Dart、工具三个方面介绍,快速形成对 Flutter 开发的一个综合概念。
本文主要介绍 Flutter 相关知识。
Flutter 语言使用 Dart,开发 IDE 使用 VSCode、Android Studio 或 IntelliJ IDEA,个人推荐使用 VSCode。
Flutter 概览
Flutter 是由 Google 开发的开源移动应用开发框架,用于快速构建高性能、高保真度的跨平台移动应用。Flutter 支持同一套代码在 iOS、Android、Web、桌面应用等多个平台上运行,使用自带的 Skia 图形引擎,可以直接绘制 UI 组件,这使得 Flutter 应用在性能方面表现出色,滚动流畅,响应迅速(目前正在使用 Impeller 替换 Skia)。
Flutter 架构
看一眼有个印象即可。
参考资料
三棵树
Flutter 的渲染机制由三棵树组成:Widget Tree、Element Tree、Render Tree(渲染树)。
- 所有 Widget 组成 Widget Tree。
- 通过调用 Widget 的
createElement()方法,创建 Element Tree,Wdiget Tree 与 Element Tree 是一一对应的。 - 每个 Element 调用
createRenderObject()形成 Render Tree,Render Tree 负责渲染,注意 Render Tree 与 Elemeng Tree 并不是一一对应的,而是为最后渲染做准备。
Widget
在 Flutter 中,widget 是一个非常重要的概念,是描述 UI 的基本元素。可以用 iOS 中的 UIView 来类比理解 widget 的概念,但是 widget 并不是 UIView。widget 只是对布局的一种描述,最终还要经过三棵树生成 element 和 renderObject 才会被绘制到屏幕上,而 UIView 就是最终渲染的 view。
这里可以给出一个 widget 的定义:widget 是 Flutter 中描述一 UI 元素的配置信息,需要经过三棵树生成最终的 renderObject 才会被渲染到屏幕上。可以用 UIView 理解,但是 UIView 更像 renderObject。
在 Flutter 中,通过 widget 嵌套 widget 的方式来构建 UI 和进行事件处理,所以万物都是 Widget。Widget 的基类是Widget,是一个抽象类一般不会直接用。
在 flutter 中,widget 分为两类:有状态的 widget 和无状态的 widget,即StatefulWidget和StatelessWidget,两个都直接继承自Widget类。不需要改变的元素使用StatelessWidget,设定好后元素不会被改变;需要改变的元素需要使用 StatefulWidget,通过改变内部 state 来修改自身 UI,触发重新渲染。
StatelessWidget,无状态 widget,设定好后不能再修改。StatefulWidget,有状态的 widget,内部会对应一个 State 类,通过修改 State 可以改变自身 widget,然后更新 UI。
参考:
- Widget 简介 - Flutter 实战第二版
StatelessWidget
StatelessWidget 是无状态组件,通过build方法来构建 UI。
build,通过 build 方法嵌套其他 widget 来构建 UI,在构建过程中会递归的构建其他嵌套的 widget。context,build方法有一个context参数,它是BuildContext类的一个实例,表示当前 widget 在 widget 树中的上下文,每一个 widget 都会对应一个 context 对象(因为每一个 widget 都是 widget 树上的一个节点)。实际上,context是当前 widget 在 widget 树中位置中执行” 相关操作 “的一个句柄 (handle),比如它提供了从当前 widget 开始向上遍历 widget 树以及按照 widget 类型查找父级 widget 的方法。
// StatelessWidget 定义
abstract class StatelessWidget extends Widget {
const StatelessWidget({ super.key });
@override
StatelessElement createElement() => StatelessElement(this);
// 核心是 build 方法
@protected
Widget build(BuildContext context);
}
// 示例,Echo widget,核心是build方法构建UI
class Echo extends StatelessWidget {
const Echo({
Key? key,
required this.text,
this.backgroundColor = Colors.grey, //默认为灰色
}):super(key:key);
final String text;
final Color backgroundColor;
@override
Widget build(BuildContext context) {
return Center(
child: Container(
color: backgroundColor,
child: Text(text),
),
);
}
}
// 在子树中获取父级widget的一个示例:
class ContextRoute extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Context测试"),
),
body: Container(
child: Builder(builder: (context) {
// 在 widget 树中向上查找最近的父级`Scaffold` widget
Scaffold scaffold = context.findAncestorWidgetOfExactType<Scaffold>();
// 直接返回 AppBar的title, 此处实际上是Text("Context测试")
return (scaffold.appBar as AppBar).title;
}),
),
);
}
}
StatefulWidget
StatefulWidget 的核心就是有状态,可以修改重绘 UI。
- StatefulWidget 重写了 createElement 方法,返回的是
StatefulElement,StatefulElement可能会多次调用createState()来创建对象。 createState(),创建状态,修改 UI 必须通过修改状态来实现,一个StatefulElement对应一个State实例。
abstract class StatefulWidget extends Widget {
const StatefulWidget({ Key key }) : super(key: key);
@override
StatefulElement createElement() => StatefulElement(this);
@protected
State createState();
}
State
一个 StatefulWidget 类会对应一个 State 类,State 表示与其对应的 StatefulWidget 要维护的状态。
State 中的状态信息:
- 在 widget 构建时可以被同步读取。
- 在 widget 生命周期中可以被改变,当 State 被改变时,可以手动调用其
setState()方法通知 Flutter 框架状态发生改变,Flutter 框架在收到消息后,会重新调用其build方法重新构建 widget 树,从而达到更新 UI 的目的。
State 中有两个常用的信息:
widget,它表示与该 State 实例关联的 widget 实例,由 Flutter 框架动态设置。注意,这种关联并非永久的,因为在应用生命周期中,UI 树上的某一个节点的 widget 实例在重新构建时可能会变化,但 State 实例只会在第一次插入到树中时被创建,当在重新构建时,如果 widget 被修改了,Flutter 框架会动态设置 State. widget 为新的 widget 实例。context。StatefulWidget 对应的 BuildContext,作用同 StatelessWidget 的 BuildContext。
State 生命周期:
这块详细参考:State-Flutter 实战第二版
参考资料
- Widget 简介 - Flutter 实战第二版
布局
Debugger 输出
- debugPrint
- log
// debugPrint
import 'package:flutter/foundation.dart';、
debugPrint('movieTitle: $movieTitle');
// log
import 'dart:developer'; //(auto import will do this even)
//example for api logging
log("${response?.statusCode} : ${response?.request?.path}",
name: "Response", error: response.data);
Flutter 命令
// 检测flutter配置,输出相关依赖的安装情况
flutter doctor
// flutter 工程安装依赖,注意针对flutter工程,需要使用flutter命令(dart pub get是针对dart工程)
flutter pub get
// flutter 工程添加依赖
flutter pub add css_colors
// flutter 移除依赖
flutter pub remove css_colors
// 运行flutter工程
flutter run
// Other
// 升级Flutter SDK
flutter upgrade
// 创建Flutter App
flutter create test_drive // 创建Flutter工程模板
flutter devices // 选择对应的平台
flutter run // 运行Flutter工程
// 后续注意使用 fvm flutter 代替 flutter
# Use
> fvm flutter {command}
# Instead of
> flutter {command}
// 后续使用 fvm dart 代替 dart
# Use
> fvm dart {command}
# Instead of
> dart {command}
创建 Flutter 工程
具体参考:开发体验初探
通过 VSCode 创建 Flutter App:
- 打开 VSCode 命令窗口,输入 Flutter,选择
Flutter: New Project - 通过命令行创建 :flutter create -t app
Flutter 工程结构目录
工程结构
创建工程后,默认会显示以下文件
.
├── .dart_tool # 记录了一些dart工具库所在的位置和信息
├── .idea # android studio 是基于idea开发的,.idea 记录了项目的一些文件的变更记录
├── android # Android项目文件夹
├── build # 编译或运行后产物
├── ios # iOS项目文件夹
├── lib # lib文件夹存放我们的dart语言代码
├── linux # Linux项目目录
├── macos # mac项目目录
├── test # 测试代码
├── web # web项目目录
├── windows # win项目目录
├── .gitignore # git忽略配置文件
├── .metadata # 一个对当前workspace的配置记录
├── analysis_options.yaml # flutter lint配置文件,定义代码静态分析相关的配置项和规则
├── flutter_application_1.iml # 工程文件的本地路径配置,在Android Studio或IntelliJ下起作用
├── pubspec.lock # 项目依赖的lock文件
├── pubspec.yaml # 依赖配置文件,类似package.json文件
└── README.md # README
其中比较关注以下几个文件夹:
- lib,存放工程的核心代码
- pubspec.yaml,依赖配置管理
另外,一般额外创建两个目录:assets和fonts,用于存放图片和字体,注意需要在pubspec.yaml文件中声明。
# 示例
# The following section is specific to Flutter.
flutter:
# To add assets to your application, add an assets section, like this:
assets:
- assets/images/
fonts:
- family: Raleway
fonts:
- asset: fonts/Raleway-Regular.ttf
- asset: fonts/Raleway-Italic.ttf
style: italic
- family: RobotoMono
fonts:
- asset: fonts/RobotoMono-Regular.ttf
- asset: fonts/RobotoMono-Bold.ttf
weight: 700
lib 目录结构
不要一直寻求最佳实践,最佳实践只是最小省力原则,要 go above and beyond。
lib 目录是存放了我们工程代码,结合前端工程,目前得出一个最佳目录结构。
lib
├── main.dart
├── api # 接口请求
├── components # 公共组件
├── l10n # 国际化,Flutter中使用 flutter_localizations 库作为国际化,因此叫l10n
├── router # 路由
├── store # 状态管理
├── models # Json文件对应的Dart Model类会在此目录下
├── types # 公共类型定义
├── utils # 工具函数、通用方法、网络接口
└── pages # 页面
参考:
- A Comprehensive Guide to Creating a Scalable Folder Structure for Flutter Apps
- Flutter APP 代码结构 - Flutter 实战第二版
- 三步入门之 Flutter 一看工程目录结构设计
pubspec.yaml 文件
参考资料
常用三方库
功能类
- dio,网络库
- shared_preferences,存储库
- flutter_ume,字节开源的 debug 库
- flutter_ume_plus,ume 目前看不再维护,需要使用该库
- event_bus_plus,Flutter 的 EventBus,订阅发布机制,用于不同模块之间通信
- device_info_plus,获取设备信息,品牌 / 系统 / 屏幕等等
- fl_chart,Flutter 图表库,效果示例
- skeletonizer,骨架屏
- bot_toast,toast 库
- cached_network_image,网络图片缓存
- flutter_animate,强大的动画库
- flame,flutter 游戏框架,使用该框架开发手机游戏
- go_router,flutter 官方出的路由框架
- permission_handler,封装了 iOS/Anroid 权限管理库
- easy_refresh,上拉下拉加载刷新库
- fluttertoast,Toast 库
- pretty_qr_code,二维码生成库
- url_launcher,支持 scheme url 跳转第三方应用
- path_provider,获取系统文件路径
- bruno,贝壳维护的一套 Flutter UI 组件库
- FAIR,58 维护的一套动态化框架,
工具类
学习 Demo
- Flutter Gallery - 官方,Flutter 官方示例工程
- Flutter Samples - 官方,官方示例工程页面,集成了一些项目参考
- flutter_flexible,一个集成了常用的第三方工程
FlutterUnit
FlutterUnit 是一个非常强大的 Flutter 学习利器,提供全面的 Flutter 学习指南,支持 iPhone/Andriod/Mac/PC 全平台,可以快速查看组件 API 说明和示例。可以说是学习 Flutter 的必备学习软件。
Flutter 源码中的可用的组件一共 350 个左右,纷繁复杂,也没有明确的分类标准 FlutterUnit 对
大大小小,常用不常用的组件能收的尽量收录。并根据个人感觉进行评星目前收录组件306个,每个都有至少一个演示展现和代码展示。
FlutterUnit 提供以下功能:
- 300 + 组件的收录
- 组件详情说明,提供组件的属性和说明演示
- 可操作,操作交互类组件提供可操作性
- 提供组件的关联切换
Material Design
是一种用户界面设计语言,开始被 Google 引入用于安卓的 UI 设计风格。现在提供一系列 UI 组件,Flutter 核心的 UI 库。
参考资料:
- Material Design - 官网
- Material Icons - 官网
参考资料
- Flutter 官网
- UIKit 开发者的 Flutter 指南 - 官方
- Flutter for SwiftUI Developers - 官方
- Android 开发者的 Flutter 指南 - 官方
- Web 开发者的 Flutter 指南 - 官方
- React Native 开发者的 Flutter 指南 - 官方
- 《Flutter 实战 · 第二版》
- awesome-flutter
- awesome-flutter-cn
- 在中国网络环境下使用 Flutter
- Flutter 架构概览
📝 转载声明
- 原作者:京东云开发者
- 原文链接:深入浅出 RabbitMQ:核心概念与实战应用
- 版权声明:本文内容来源于网络,版权归原作者所有,转载请联系原作者授权。