5分钟学会Flutter开发

黄沙百战穿金甲,不破楼兰终不还。这篇文章主要讲述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/.../flutter export PATH=$PATH:$FLUTTER_HOME/bin export PATH=$PATH:$FLUTTER_HOME/bin/cache/dart-sdk/bin

1.3 刷新环境变量
source ~/.bash_profile source ~/.zshrc(如安装zsh插件)

1.4 开发工具以 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
5分钟学会Flutter开发

文章图片

*报错:Unable to find bundled java version on Flutter
cd /Applications/Android\\ Studio.app/Contents/jre ln -s ../jre jdk ln -s "/Library/Internet Plug-Ins/JavaAppletPlugin.plugin" jdk flutter 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版本)
5分钟学会Flutter开发

文章图片

3.3.2 点击按钮修改文本(Flutter、SwiftUI版本)
5分钟学会Flutter开发

文章图片

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

文章图片

四、基础架构
5分钟学会Flutter开发

文章图片

Flutter被设计为一个可扩展的分层系统。它可以被看作是各个独立的组件的系列合集,上层组件各自依赖下层组件。组件无法越权访问更底层的内容,并且框架层中的各个部分都是可选且可替代的。从下到上分为三层,依次为:Embedder、Engine、Framework。
4.1 EmbedderEmbedder是操作系统适配层,实现了渲染 Surface 设置,线程设置等。
4.2 EngineEngine层是 Flutter 的核心,它主要使用 C++ 编写,并提供了 Flutter 应用所需的原语。当需要绘制新一帧的内容时,引擎将负责对需要合成的场景进行栅格化。它提供了 Flutter 核心 API 的底层实现,包括图形(通过 Skia 链接:https://skia.org/)、文本布局、文件及网络 IO、辅助功能支持、插件架构和 Dart 运行环境及编译环境的工具链。
4.3 FrameworkFramework 层是一个用 Dart 实现的 UI SDK,包含了动画、图形绘制和手势识别等功能。开发者可以通过 Flutter 框架层与 Flutter 交互,该框架提供了以 Dart 语言编写的现代响应式框架。它包括由一系列层组成的一组丰富的平台,布局和基础库。从下层到上层,依次有:
1、基础的Foundation 类及一些基层之上的构建块服务,如 animation、 painting 和 gestures,它们可以提供上层常用的抽象。
2、渲染层用于提供操作布局的抽象。有了渲染层,你可以构建一棵可渲染对象的树。在你动态更新这些对象时,渲染树也会自动根据你的变更来更新布局。
3、widget层是一种组合的抽象。每一个渲染层中的渲染对象,都在 widgets 层中有一个对应的类。此外,widgets 层让你可以自由组合你需要复用的各种类。响应式编程模型就在该层级中被引入。
4、Material 和 Cupertino 库提供了全面的 widgets 层的原语组合,这套组合分别实现了 Material 和 iOS 设计规范。
五、视图渲染 5.1 WidgetFlutter中没有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分钟学会Flutter开发

文章图片

5.2.2 Widget会转化成RenderObject,但并不是所有的widget都会转成RenderObject。
非RenderObject转化:
//Text -> StatelessWidget -> Widgetclass 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 -> Widget class Column extends Flex { } class Flex extends MultiChildRenderObjectWidget { // ?法实现 RenderFlex createRenderObject(BuildContext context) { //返回RenderFlex return RenderFlex -> RenderBox -> RenderObject } } abstract class MultiChildRenderObjectWidget extends RenderObjectWidget { } abstract class RenderObjectWidget extends Widget { RenderObjectElement createElement(); RenderObject createRenderObject(BuildContext context); // 抽象?法-创建RenderObject void 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.获取FlutterViewController let 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: @escaping FlutterResult) in // 1.判断当前是否是nativeGetUDID guard call.method == "nativeGetUDID" else { result(FlutterMethodNotImplemented); // 报?个没有?法的错误 return; } call.arguments; //传递过来的参数 // 2.获取UDID let 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 { //?法找不到,回调notImplemented result.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引擎 , 为引擎起名为leo let flutterEngine:FlutterEngine = FlutterEngine(name: "leo"); // 启动flutter引擎,默认函数??为main flutterEngine.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.gradle setBinding(new Binding([gradle: this])) // new evaluate(new File( // new settingsDir.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.gradle dependencies { ... implementation project(\':flutter\') //增加flutter依赖 } // 3、AndroidManifest.xml配置 < activity android:name="io.flutter.embedding.android.FlutterActivity" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDi rection|fontScale|screenLayout|density|uiMode" android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize" />

import io.flutter.embedding.android.FlutterActivity; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); startActivity( FlutterActivity.createDefaultIntent(this) ); } }

七、案例讲解 - 计数器
// 导?类 import \'package:flutter/material.dart\'; //??函数,程序加载时调? void main() { runApp(MyApp()); //调?runApp?法,并初始化MyApp } class MyApp extends StatelessWidget { // This widget is the root of your application. @override Widget 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显示,所以此处继承StatefulWidget class 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会标记需要刷新UI setState(() { // 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显示 }); } @override Widget 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的孵化研发工作。是公司级战略产品,承担百度系产品矩阵短视频内容供给任务,重点支持百度搜索和信息流视频化,肩负百度内容生态视频化转型使命。仅用两年的时间就实现用户规模从零到亿级增长,日活数千万。拥有百亿级流量,亿级数据量,丰富新奇和全面的产品玩法,多类型的技术系统和领先的技术架构。
【5分钟学会Flutter开发】欢迎加入短视频研发部,社招,实习,校招都要哦
简历投递邮箱:geektalk@baidu.com (投递备注【短视频】)
推荐阅读:
|百度信誉认证中台架构解析
|图数据库在百度汉语中的应用
|从lowcode看下一代前端应用框架
---------- END ----------
百度Geek说
百度官方技术公众号上线啦!
技术干货 · 行业资讯 · 线上沙龙 · 行业大会
招聘信息 · 内推信息 · 技术书籍 · 百度周边
欢迎各位同学关注

    推荐阅读