写点什么

5 分钟学会 Flutter 开发

用户头像
百度Geek说
关注
发布于: 5 小时前
5分钟学会Flutter开发

导读:Flutter 是 Google 开源的构建用户界面(UI)工具包,帮助开发者通过一套代码库高效构建多平台应用,支持移动、Web、桌面和嵌入式平台。Flutter 使用 Dart 为开发语言,利用 Skia 绘图引擎,直接通过 CPU、GPU 进行绘制,不需要依赖任何原生的控件,相比 React Native(依赖中间者 JSCore 引擎)性能更高。


全文 3560 字,预计阅读时间 14 分钟。


目前 Flutter 混合栈技术成熟,基础建设完善,百度贴吧、网盘、地图、阅读、输入法等均已接入 Flutter,一套代码双端运行,约节省 50%人力。

一、环境配置:

1.1 下载 Flutter SDK

git clone https://github.com/flutter/flutter.git
复制代码


1.2 配置环境变量

编辑~/.bash_profile,将环境变量添加至末尾。(如终端安装了 zsh 插件,则添加环境变量至 ~/.zshrc)

# FLUTTER_HOME为下载的Flutter文件夹路径export FLUTTER_HOME=/Users/.../flutterexport PATH=$PATH:$FLUTTER_HOME/binexport PATH=$PATH:$FLUTTER_HOME/bin/cache/dart-sdk/bin
复制代码


1.3 刷新环境变量

source ~/.bash_profilesource ~/.zshrc(如安装zsh插件)
复制代码

1.4 开发工具

1. Xcode + Android Studio(推荐)

2. Visual Studio Code


以 Xcode + Android Studio 为例,配置 Android Studio 插件:


1.4.1 安装 Flutter,Dart 插件

Android Studio - Preferences - Plugins - Marketplace

1.4.2 安装最新 Android SDK Command

Android Studio - Preferences - SystemSettings - Android SDK - SDK Tools - 勾选 Android SDK Command-line Tools


1.4.3 运行 flutter doctor

*报错:Unable to find bundled Java version on Flutter

cd /Applications/Android\ Studio.app/Contents/jreln -s ../jre jdkln -s "/Library/Internet Plug-Ins/JavaAppletPlugin.plugin" jdkflutter doctor
复制代码

二、工程创建

2.1 创建 Flutter 项目

flutter create xxx
复制代码

2.2 创建 Flutter 模块(用于原生集成 Flutter)

create --template module xxx
复制代码

2.3 工程结构

bd_flutter  .dart_tool.............记录依赖库信息  .idea..................当前项目配置  android................Android工程目录  iOS....................iOS工程目录  lib....................Flutter代码目录  test...................单元测试目录  web....................Web工程目录  pubspec.yaml...........Pub第三方依赖配置文件,类似Cocoapods、Gradle
复制代码

三、编程范式

在 Flutter 中界面布局使用 Dart 语言声明式编程范式,更易于开发与阅读。

3.1 命令式编程

命令“机器”如何去做事情(注重 how) 。

3.2 声明式编程

告诉“机器”你想要的是什么(注重 what) 。

2009 年开始 Vue、React、SwiftUI、Flutter 以声明式编程为主,正逐步成为大前端的一种编程趋势。

3.3 我们举一个栗子,来帮我我们理解这两者的区别

3.3.1 点击按钮修改文本(OC、Java 版本)

3.3.2 点击按钮修改文本(Flutter、SwiftUI 版本)

在声明式编程中,首先代码是结构化的;其次,开发者无需关注 Label/Text 控件更新,引擎会自动根据 num 值的改变修改引用控件的值。

四、基础架构

Flutter 被设计为一个可扩展的分层系统。它可以被看作是各个独立的组件的系列合集,上层组件各自依赖下层组件。组件无法越权访问更底层的内容,并且框架层中的各个部分都是可选且可替代的。从下到上分为三层,依次为:Embedder、Engine、Framework。

4.1 Embedder

Embedder 是操作系统适配层,实现了渲染 Surface 设置,线程设置等。

4.2 Engine

Engine 层是 Flutter 的核心,它主要使用 C++ 编写,并提供了 Flutter 应用所需的原语。当需要绘制新一帧的内容时,引擎将负责对需要合成的场景进行栅格化。它提供了 Flutter 核心 API 的底层实现,包括图形(通过 Skia 链接:https://skia.org/)、文本布局、文件及网络 IO、辅助功能支持、插件架构和 Dart 运行环境及编译环境的工具链。

4.3 Framework

Framework 层是一个用 Dart 实现的 UI SDK,包含了动画、图形绘制和手势识别等功能。开发者可以通过 Flutter 框架层与 Flutter 交互,该框架提供了以 Dart 语言编写的现代响应式框架。它包括由一系列层组成的一组丰富的平台,布局和基础库。从下层到上层,依次有:

1、基础的 Foundation 类及一些基层之上的构建块服务,如 animation、 painting 和 gestures,它们可以提供上层常用的抽象。

2、渲染层用于提供操作布局的抽象。有了渲染层,你可以构建一棵可渲染对象的树。在你动态更新这些对象时,渲染树也会自动根据你的变更来更新布局。

3、widget 层是一种组合的抽象。每一个渲染层中的渲染对象,都在 widgets 层中有一个对应的类。此外,widgets 层让你可以自由组合你需要复用的各种类。响应式编程模型就在该层级中被引入。

4、Material 和 Cupertino 库提供了全面的 widgets 层的原语组合,这套组合分别实现了 Material 和 iOS 设计规范。

五、视图渲染

5.1 Widget

Flutter 中没有 Controller、Activity 概念,只有一种控件 Widget(相当于 View),一切皆 Widget。

Widget 是 Flutter 功能的抽象描述,是视图的配置信息,同样也是数据的映射,是 Flutter 开发框架中最基本的概念。

两个比较重要的 Widget:StatelessWidget 和 StatefulWidget。

5.2 渲染过程

5.2.1 Flutter 引擎不会直接渲染 widget 树,因为 widget 是特别不稳定的,会频繁的调用 build 方法,widget 又相互依赖,一旦调用 build,后面的 widget 都会重新创建,直接去解析 widget 的话会非常消耗性能,布局需要重新计算。由此引出了 Element,RenderObject 的概念,Flutter 引擎解析的是 RenderObject 树,并非 widget。

5.2.2 Widget 会转化成 RenderObject,但并不是所有的 widget 都会转成 RenderObject。

非 RenderObject 转化:

//Text -> StatelessWidget -> Widget
class Text extends StatelessWidget {}abstract class StatelessWidget extends Widget {StatelessElement createElement() => StatelessElement(this);}abstract class Widget extends DiagnosticableTree {Element createElement(); // 创建element抽象方法}
复制代码

RenderObject 转化:


//Column -> Flex -> MultiChildRenderObjectWidget - > RenderObjectWidget -> Widgetclass Column extends Flex {}class Flex extends MultiChildRenderObjectWidget {// ⽅法实现RenderFlex createRenderObject(BuildContext context) {//返回RenderFlexreturn RenderFlex -> RenderBox -> RenderObject} }abstract class MultiChildRenderObjectWidget extends RenderObjectWidget {}abstract class RenderObjectWidget extends Widget {RenderObjectElement createElement();RenderObject createRenderObject(BuildContext context); // 抽象⽅法-创建RenderObjectvoid updateRenderObject(BuildContext context, covariant RenderObject renderObject) {}void didUnmountRenderObject(covariant RenderObject renderObject) { }}abstract class Widget extends DiagnosticableTree {Element createElement(); // 抽象⽅法-创建element}class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox,FlexParentData>, RenderBoxContainerDefaultsMixin<RenderBox,FlexParentData>, DebugOverflowIndicatorMixin {}abstract class RenderBox extends RenderObject {}
复制代码

六、混合开发

6.1 Flutter 调用原生方法

1.Platform channels

2.Pigeon

3.https://pub.dev/ 中搜索第三方开源包

以 Platform channels 为例:Flutter 调用原生获取 UDID

/*Flutter代码*/static const platform = const MethodChannel("leo.com/getudid");void getUDID() async {final result = await platform.invokeMethod("nativeGetUDID"); // 要调⽤的⽅法// final result = await platform.invokeMethod("nativeGetUDID",["flutter参数"]);setState(() {_udid = result; });}
复制代码


/*iOS代码*/// 1.获取FlutterViewControllerlet controller: FlutterViewController = window.rootViewController as!FlutterViewController;// 2.创建FlutterMethodChannel,跟controller⼆进制消息通信let channel = FlutterMethodChannel(name: "leo.com/getudid", binaryMessenger:controller.binaryMessenger);// 3.监听channel调⽤⽅法,当flutter调⽤nativeGetUDID的时候调⽤channel.setMethodCallHandler { (call: FlutterMethodCall, result: @escapingFlutterResult) in// 1.判断当前是否是nativeGetUDIDguard call.method == "nativeGetUDID" else {result(FlutterMethodNotImplemented); // 报⼀个没有⽅法的错误return; }call.arguments; //传递过来的参数// 2.获取UDIDlet udid = "xxxx-xxxx-xxxx-xxxx"result(udid) //回调值}
复制代码


/*Android代码*/private val CHANNEL = "leo.com/getudid"override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {GeneratedPluginRegistrant.registerWith(flutterEngine);// 1.创建MethodChannel对象val methodChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger,CHANNEL)// 2.添加调⽤⽅法的回调methodChannel.setMethodCallHandler {// Note: this method is invoked on the main thread.call, result ->// 2.1.如果调⽤的⽅法是nativeGetUDID,那么正常执⾏if (call.method == "nativeGetUDID") {// 2.1.1.调⽤另外⼀个⾃定义⽅法回去电量信息val udid = "xxxx-xxxx-xxxx-xxxx";result.success(udid) } else {//⽅法找不到,回调notImplementedresult.notImplemented() } }}
复制代码

6.2 原⽣集成 Flutter 创建 Flutter 模块

create --template module native_add_flutter
复制代码

6.2.1 iOS 集成 Flutter

// CocoaPods集成flutter_application_path = '../native_add_flutter'load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb’)
复制代码


// 初始化Flutter引擎 , 为引擎起名为leolet flutterEngine:FlutterEngine = FlutterEngine(name: "leo");// 启动flutter引擎,默认函数⼊⼝为mainflutterEngine.run();let flutterVC = FlutterViewController(engine: engine, nibName: nil, bundle: nil);flutterVC.modalPresentationStyle = .fullScreen;self.present(flutterVC, animated: true, completion: nil);
复制代码

6.2.2 Android 集成 Flutter

// 在gradle进⾏配置// 创建Android项⽬、添加相关的依赖// 1、修改Android项⽬settings.gradlesetBinding(new Binding([gradle: this])) // newevaluate(new File( // newsettingsDir.parentFile, // new'native_add_flutter/.android/include_flutter.groovy' // new))include ':native_add_flutter'project(':native_add_flutter').projectDir = new File('../native_add_flutter')// 2、配置Android项⽬的build.gradledependencies { ...implementation project(':flutter') //增加flutter依赖}// 3、AndroidManifest.xml配置<activity android:name="io.flutter.embedding.android.FlutterActivity"android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"android:hardwareAccelerated="true"android:windowSoftInputMode="adjustResize"/>
复制代码


import io.flutter.embedding.android.FlutterActivity;public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);startActivity(FlutterActivity.createDefaultIntent(this) ); }}
复制代码

七、案例讲解 - 计数器

flutter create flutterdemo

main.dart

// 导⼊类import 'package:flutter/material.dart';//⼊⼝函数,程序加载时调⽤void main() {runApp(MyApp()); //调⽤runApp⽅法,并初始化MyApp}class MyApp extends StatelessWidget {// This widget is the root of your application.@overrideWidget build(BuildContext context) { //初始化会调⽤build⽅法return MaterialApp( //Material为Google的⼀种UI⻛格,MaterialApp可为项⽬配置App标题、主题title: 'Flutter Demo',theme: ThemeData(// This is the theme of your application.//// Try running your application with "flutter run". You'll see the// application has a blue toolbar. Then, without quitting the app, try// changing the primarySwatch below to Colors.green and then invoke// "hot reload" (press "r" in the console where you ran "flutter run",// or simply save your changes to "hot reload" in a Flutter IDE).// Notice that the counter didn't reset back to zero; the application// is not restarted.primarySwatch: Colors.blue, ),home: MyHomePage(title: 'Flutter Demo Home Page'), //设置主⻚为MyHomePage ); }}//由于点击需要更改Text显示,所以此处继承StatefulWidgetclass MyHomePage extends StatefulWidget {MyHomePage({Key? key, required this.title}) : super(key: key);// This widget is the home page of your application. It is stateful, meaning// that it has a State object (defined below) that contains fields that affect// how it looks.// This class is the configuration for the state. It holds the values (in this// case the title) provided by the parent (in this case the App widget) and// used by the build method of the State. Fields in a Widget subclass are// always marked "final".final String title;@override_MyHomePageState createState() => _MyHomePageState();}class _MyHomePageState extends State<MyHomePage> {int _counter = 0;//下的按钮的点击事件void _incrementCounter() {// setState会标记需要刷新UIsetState(() {// This call to setState tells the Flutter framework that something has// changed in this State, which causes it to rerun the build method below// so that the display can reflect the updated values. If we changed// _counter without calling setState(), then the build method would not be// called again, and so nothing would appear to happen._counter++; //点击按钮时候,counter+1, 并⾃动更新UI显示 }); }@overrideWidget build(BuildContext context) {// This method is rerun every time setState is called, for instance as done// by the _incrementCounter method above.//// The Flutter framework has been optimized to make rerunning build methods// fast, so that you can just rebuild anything that needs updating rather// than having to individually change instances of widgets.return Scaffold(appBar: AppBar( //配置导航// Here we take the value from the MyHomePage object that was created by// the App.build method, and use it to set our appbar title.title: Text(widget.title), ),body: Center( //配置布局显示在中⼼// Center is a layout widget. It takes a single child and positions it// in the middle of the parent.child: Column( //⼀种竖向布局⽅式,相当于listview// Column is also a layout widget. It takes a list of children and// arranges them vertically. By default, it sizes itself to fit its// children horizontally, and tries to be as tall as its parent.//// Invoke "debug painting" (press "p" in the console, choose the// "Toggle Debug Paint" action from the Flutter Inspector in Android// Studio, or the "Toggle Debug Paint" command in Visual Studio Code)// to see the wireframe for each widget.//// Column has various properties to control how it sizes itself and// how it positions its children. Here we use mainAxisAlignment to// center the children vertically; the main axis here is the vertical// axis because Columns are vertical (the cross axis would be// horizontal).mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[ //返回多个widget数组,Text('You have pushed the button this many times:', ),Text( '$_counter',//显示_counter的值style: Theme.of(context).textTheme.headline4,//显示样式,使⽤主题的headline4显示 ), ], ), ),floatingActionButton: FloatingActionButton( //⼀个可点击的按钮,固定在右下⻆onPressed: _incrementCounter, //点击事件tooltip: 'Increment',child: Icon(Icons.add), //按钮显示为内部⾃带的add图⽚ ), // This trailing comma makes auto-formatting nicer for build methods.  ); }}
复制代码

八、附:近期重要更新

1.2 版本 2019/02/26

支持 java1.8

增加了 JavaScript 与 Dart 的通信通道

增加了对 Android App Bundles 的支持

通过减少调用构造函数和静态方法,提升 AOT(预编译)10%-20%的性能


1.5 版本 2019/05/07

添加了集成测试

提高热重载性能

决定删除动态更新计划

添加对 Linux 和 Windows 的 Flutter 运行支持


1.7 版本 2019/07/09

优化 Flutter tools 支持

支持 32 位和 64 位 Android bundles

开始支持 web 端和实验性支持桌面端


1.9 版本 2019/09/10

完善 Web 平台支持

桌面平台实验性支持

新增 24 种语言环境支持

支持 macOS Catalina 和 iOS 13

Android 增加对构建 AAR 的支持


1.12 版本 2019/12/11

支持 Android 10

支持 iOS13 暗黑模式

可以将 Flutter 模块集成到 Android 或 iOS 应用中

将 Web 支持从开发版转变为 beta 版;将 MacOS 支持纳入开发版本

推出新工具 DartPad(DartPad 是一个可以让你在任何现代化的浏览器中体验 Dart 编程语言线上工具)


1.17 版本 2020/05/06

减少 18.5%应用体积

提升了 20%-37%导航性能

降低了 40% iOS 动画 CPU/GPU 使用率

增加对谷歌字体的支持:fonts.google.com

完成对 Type Scale 部分的重构,符合 2018 Material 设计规范

提升了 iOS 50%渲染速度(iPhone5s+、iOS10+支持 Metal 渲染);不完全支持扔使用 OpenGL 渲染


1.20 版本 2020/08/05

增强了 UTF-8 解码

pubspec.yaml 插件不再支持旧格式

在 Visual Studio Code 中预览嵌入式 Dart DevTools

引入新的混编插件-Pigeon,可以在 Dart 方法中直接调用 Java/Objective-C/Kotlin/Swift 方法并传递非原始数据对象。


1.22 版本 2020/10/01

增加应用体积分析工具

提供了国际化和本地化工具,并实现了热重载支持

支持 Android 11;支持新的屏幕类型 (如挖孔屏和瀑布屏),以及同步 Android 11 动画效果

支持 iOS 14、Xcode 12 新图标以及对新 iOS 14 App Clips 功能的预览支持;默认模板版本从 8.0 升级到 9.0

可正式使用的 Google Maps 和 WebView 插件,将 Android 和 iOS 系统的原生界面组件托管在 Flutter 应用中


2.0 版本 2021/03/03

Web 支持从测试版转变为稳定版

除了 HTML 渲染,增加了 CanvasKit 渲染,桌面端浏览器会默认调用 CanvasKit 版本,移动端的浏览器会调用 HTML 版本。

混合开发多 flutter 实例(经测试 iOS 平台存在内存问题)

桌面平台的支持(beta)

Google Mobile Ads(Beta)

Dart 2.12 增加了空安全


2.2 版本 2021/05/18

更好的 iOS、Android、Web 跨平台支持

Dart 2.13 更新,引入 Type aliases

Flutter Web 提升稳定性

优化 iOS 端渲染动画帧时间、实现了增量 iOS 安装,缩短更新安装时间。

Android 中引入延迟组件,允许 Flutter 应用在运行时下载包含提前编译的代码模块,减少初始安装大小。


招聘信息:

短视频研发部,负责好看视频、全民小视频以及多款创新 APP 的孵化研发工作。是公司级战略产品,承担百度系产品矩阵短视频内容供给任务,重点支持百度搜索和信息流视频化,肩负百度内容生态视频化转型使命。仅用两年的时间就实现用户规模从零到亿级增长,日活数千万。拥有百亿级流量,亿级数据量,丰富新奇和全面的产品玩法,多类型的技术系统和领先的技术架构。

欢迎加入短视频研发部,社招,实习,校招都要哦

简历投递邮箱:geektalk@baidu.com (投递备注【短视频】)


推荐阅读:

百度信誉认证中台架构解析

图数据库在百度汉语中的应用

|从lowcode看下一代前端应用框架


---------- END ----------

百度 Geek 说

百度官方技术公众号上线啦!

技术干货 · 行业资讯 · 线上沙龙 · 行业大会

招聘信息 · 内推信息 · 技术书籍 · 百度周边

欢迎各位同学关注

发布于: 5 小时前阅读数: 13
用户头像

百度Geek说

关注

百度官方技术账号 2021.01.22 加入

关注我们,带你了解更多百度技术干货。

评论

发布
暂无评论
5分钟学会Flutter开发