Exploring_Flutter_in_action icon indicating copy to clipboard operation
Exploring_Flutter_in_action copied to clipboard

从头到尾撸一遍Flutter的一切...

封面

Flutter学(cai)习(keng)之路(Exploring Flutter in action)


大纲

  • 创世宣言
    • 背景
      • 何为Flutter? Flutter能做何?
      • Exploring Flutter in action的由来
      • Exploring Flutter in action想做什么
  • 运行前准备
  • 主要涵盖的控件和部分常见场景
  • App部分界面实景预览
  • 整套效果的App下载链接
    • for Android

创世宣言

背景

何为Flutter? Flutter能做何?

  1. Flutter是一个由Google开发的开源移动应用软件开发工具包,用于为Android、iOS、 Windows、Mac、Linux、Google Fuchsia跨平台开发应用。作为一个UI框架,可以快速的基于上述操作系统构建高质量的原生用户界面。
  2. Flutter的主要组成部分包括:
    • Dart平台
    • Flutter引擎
    • 基础库
    • 定制化设计风格的组件
  3. Flutter相比其他跨平台技术的优点:
    • 自绘UI + 原生渲染,调用系统API渲染,UI运行体验与原生应用相差无几,性能好且开发效率高
    • Google提供了符合各个操作系统UI风格的控件,且可以自由组合构建自己想要的界面
    • 多平台移植成本很低,无需为某个特定的操作系统添加额外的适配代码
    • 开发生态日趋完善,可以在pub.dev找到近乎所有的类原生开发包

Exploring Flutter in action的由来

  • Flutter 1.0版本于北京时间2018年12月5日发布以来,对于国内的很多开发者,直接阅读Google官方英文文档相对来说比较困难,而等待国内公开出版的Flutter相关书籍又显得太慢
  • 官网上的示例代码稍显零碎,不太直观
  • 想要运行起来零距离"触摸"下各种widget,体验下各种属性,往往得几经折腾
  • 对于追一门偏向于实践且比较新的技术,往往抄起键盘来一边写一边看是相对比较快速的上手方法

Exploring Flutter in action想做什么

  • 基于上述现状,本项目旨在提供一个App形式的demo,将各路常用的控件"揣"在一起供大家把玩,部分场景甚至可以直接移植使用
  • 相应的关键部位,在代码当中有相应的注释
  • 力图一个App搞定所有Flutter常见控件的使用
  • 不定期更新控件使用方式和pub依赖版本

运行前准备

主要涵盖的控件和部分常见场景

  • [x] 路由界面
  • [x] 状态传递
    • [x] 子Widget树获取父级StatefulWidget的State对象
  • [x] 基础控件
    • [x] 随机字符串(package:english_words/english_words.dart)
    • [x] 文本控件,字体样式(Text/TextStyle/Text.rich/TextSpan)
    • [x] 按钮系列(RaisedButton/FlatButton/OutlineButton/IconButton/FlatButton.icon/shape)
    • [x] 图片系列(ImageProvider/Image/Image.asset/Image.network)
    • [x] 单选开关和复选框Switch/Checkbox
    • [x] 输入框和表单(TextField/Form/TextFormField/FormState)
    • [x] 登录表单(TextField/Form/TextFormField/FormState)
    • [x] 各种样式的进度条(LinearProgressIndicator/CircularProgressIndicator)
  • [x] 布局控件
    • [x] 线性布局(Row/Column)
    • [x] 弹性布局(Flex/Expanded/Spacer)
    • [x] 流式布局(Wrap/Flow)
    • [x] 层叠布局(Stack/Positioned)
    • [x] 对齐与相对定位(Align/Alignment/FractionalOffset/Center)
  • [x] 容器控件
    • [x] 填充(Padding/EdgeInsets)
    • [x] 尺寸限制(ConstrainedBox/BoxConstraints/SizedBox/UnconstrainedBox)
    • [x] 装饰(DecoratedBox)
    • [x] 变换(Transform/Matrix4(作用于绘制阶段)/RotatedBox(作用于布局阶段))
    • [x] 容器(Container(多种装饰和填充等组件的组合)/Padding/Margin)
    • [x] 裁减(Clip/CustomClipper(裁减动作的作用时期与Transform相同,都作用于绘制阶段))
    • [x] 通用类导航主界面(Scaffold/AppBar/TabBar/TabBarView/Drawer/FloatingActionButton)
  • [x] 列表控件
    • [x] 单child滚动控件(SingleChildScrollView/Scrollbar)
    • [x] 有限列表项情况下使用ListView(ListView)
    • [x] 众多列表项情况下使用ListView(ListView.builder)
    • [x] 带分割线的列表项情况下使用ListView(ListView.separated)
    • [x] 下拉刷新上拉加载更多(初始化加载数据、结束时的标记、根据index判断底部是绘制结束的Widget还是正在加载时的Widget、Widget的正常显示)
    • [x] 有限GridView(GridView + SliverGridDelegateWithFixedCrossAxisCount)
    • [x] 有限GridView.count(效果完全等价于GridView + SliverGridDelegateWithFixedCrossAxisCount)
    • [x] 有限GridView(GridView + SliverGridDelegateWithMaxCrossAxisExtent)
    • [x] 有限GridView.extent(效果完全等价于GridView + SliverGridDelegateWithMaxCrossAxisExtent)
    • [x] 无限GridView加载(GridView.builder)
    • [x] 滚动监听(ScrollController/ScrollPosition)
  • [x] 触摸反馈
    • [x] 事件处理
      • [x] 原始指针(触摸事件)(撸一个触摸板)(Listener)
    • [x] 事件冒泡
      • [x] 不同事件冒泡行为之比较(HitTestBehavior.deferToChild/HitTestBehavior.opaque/HitTestBehavior.translucent/IgnorePointer)
    • [x] 手势识别
      • [x] 点击/双击/长按/拖动/滑动(GestureDetector)
      • [x] 缩放(GestureDetector)
      • [x] GestureRecognizer(当所修饰的对象不为widget且具有recognizer节点时可用)
  • [x] 事件总线
    • [x] 简易EventBus(Dart实现)
    • [x] EventBus界面演示
  • [x] 通知
    • [x] 通知事件名称(NotificationListener.onNotification)
    • [x] 自定义通知(覆写Notification + NotificationListener<T>)
    • [x] 通知冒泡(onNotification回调中的return value)
  • [x] 存储路径访问和文件操作
    • [x] 存储路径访问(访问缓存/访问包路径/访问SD卡)(PathProvider)
  • [x] 网络编程
  • [x] 功能控件及数据状态管理(InheritedWidget)
    • [x] 导航返回键和实体返回键拦截(再按一次确认退出)(WillPopScope)
    • [x] 控件跨级传递数据(InheritedWidget/dependOnInheritedWidgetOfExactType/getElementForInheritedWidgetOfExactType/updateShouldNotify/didChangeDependencies)
    • [x] 跨控件状态管理(手动实现Provider)
    • [x] 异步更新UI(FutureBuilder/StreamBuilder)
  • [x] Dialog
    • [x] AlertDialog
    • [x] SimpleDialog
    • [x] ListDialog
    • [x] 对话框状态管理(StatefulBuilder实现)
    • [x] 对话框状态管理(markNeedsBuild实现)
    • [x] 底部sheet(showModalBottomSheet)
    • [x] 对话框之LoadingDialog
    • [x] 对话框之DatePicker
    • [x] 对话框之IOS风格DatePicker
  • [x] 应用主题切换(Theme/ThemeData(内部通过InheritedWidget实现))
    • [x] 单个界面主题切换
    • [x] 全局界面主题切换
  • [x] 与原生互调和相互集成(MethodChannel.invokeMethod)
    • [x] Flutter调用Android Native方法
    • [x] Flutter界面跳转至Android Native界面
  • [x] WebView Flutter(webview_flutter)
  • [x] 开发者通用设置(MaterialApp)
    • [x] 是否显示界面布局网格(debugShowMaterialGrid)
    • [x] 是否打开性能监控,覆盖在屏幕最上面(showPerformanceOverlay)
    • [x] 是否打开栅格缓存图像的检查板(checkerboardRasterCacheImages)
    • [x] 是否打开显示到屏幕外位图的图层的检查面板(checkerboardOffscreenLayers)
    • [x] 是否打开覆盖图,显示框架报告的可访问性信息,显示边框(showSemanticsDebugger)
    • [x] 是否显示右上角的Debug标签(debugShowCheckedModeBanner)
  • [x] 切换操作系统平台(Android/iOS)
    • [x] 切换为Android应用: debugDefaultTargetPlatformOverride = TargetPlatform.android
    • [x] 切换为iOS应用: debugDefaultTargetPlatformOverride = TargetPlatform.iOS
  • [ ] 动画
  • [ ] 自定义控件
  • [ ] 国际化

App部分界面实景预览

  • 内含大量gif图,loading可能会比较耗时,依自身网速而定

整套效果的App下载链接

for Android

OS平台应用包 QRCode
Android APK包下载(内测密码:123456) Android APK包下载

for iOS

OS平台应用包 QRCode
iOS 包下载( 虚位以待 )

for Web

  • web地址: 虚位以待

坑の顺位: ↓

  1. Waiting for another flutter command to release the startup lock...
  • 因为已经有一个Flutter命令正在运行,删除SDK中的flutter/bin/cache/lockfile文件重新运行即可
  1. pubspec.yaml修改后点击packages get后长时间无反应,提示: Running "flutter pub get" in flutter_app
  1. XXX called with a context that does not contain a Scaffold...
  1. Failed assertion: line 25 pos 15: 'child != null': is not true...
  • 某个控件树下没有对应的设置child节点
  1. 修改应用名称和应用logo无效...
  • 只修改main.dart文件中MaterialApp节点下的title是不行的,title不全等价于App在手机上的应用名。对应的还需要修改相应平台的名称: Android/iOS。
  • 原生平台的应用名称修改分别在android/app/src/main/AndroidManifest.xml和ios/Runner/Info.plist下
  • 同理,修改应用logo也遵循此原则
  1. The method 'showSnackBar' was called on null...
  • 调用showSnackBar方法显示SnackBar时,往往是在与一个StatefulWidget对应的State类中调用
  • 在State对应的子类的body节点中,需要new一个Builder并传递一个上下文,通过上下文find到父类中对应的ScaffoldState对象,进而调用showSnackBar显示SnackBar
  • 否则,会出现没有对应的上下文对象所导致的NPL
  1. Positioned宽度width和高度height的计算...
  • width的计算会基于left和right进行, 当指定left和width后,right会自动算出(left+width),如果同时指定width/left/right属性则会报错
  • height的计算会基于top和bottom进行,当指定top和height后,bottom会自动算出(top+height),如果同时指定height/top/bottom属性则会报错
  1. 在事件冒泡过程中
  • 深坑: 当给底部Container控件设置color后,任何behavior(opaque/translucent/deferToChild)将会失效
  1. Flutter如何更改App包名?
1.) src/profile/AndroidManifest.xml
2.) src/debug/AndroidManifest.xml
3.) src/main/AdroidManifest.xml
4.) build.gradle .
    defaultConfig {
    applicationId
5.) MainActivity.java on "package"
  1. Flutter更改versionCode及versionName
  • pubspec.yaml文件中默认有version: 1.0.0+1,其中+之前的"1.0.0"对应versionName,+后边的"1"对应versionCode.
  1. 打release包
  1. Flutter command not found
  • 打开终端
  • open .bash_profile
  • export PATH=/Users/yangyi/developer/flutter/bin:$PATH
  • 上述PATH后的部分为你的Flutter SDK路径
  • 保存
  1. 设置当前操作系统平台
  • AppBar设置actions节点后,debugDefaultTargetPlatformOverride = TargetPlatform.iOS切换iOS效果不起作用
  1. 迁移FlutterActivity以兼容WebView和MethodChannel
  • io.flutter.app.FlutterActivity类已过时,官方推荐需要迁移到io.flutter.embedding.android.FlutterActivity
  • 但是迁移至io.flutter.embedding.android.FlutterActivity后会找不到FlutterView,需要用flutterEngine.getDartExecutor()代替
  • flutterEngine在configureFlutterEngine回调中可以拿到,通过flutterEngine.getDartExecutor()获取最终传入MethodChannel的值

最后我再叨叨两句

  • 本人学(cai)习(keng)中的一些小体会(主观居多︿( ̄︶ ̄)︿)
    • 先说Dart
      • Dart语言是基于Java、JavaScript的部分语法,还糅合了Kotlin的部分语法糖的一门语言,本身是融合的产物,估计Google的初衷是想"博采众长"
      • Dart语言本身比Java更加简练,也加入了一些Java中没有的语法糖。比如async和await,本质其实还是个Future
      • 对于已经习惯了Rx系列链式编程的人来说,Dart天然自带。如果觉得链式书写的"链"太长"影响市容"的话,还可以采用类似Kotlin中协程的写法非常舒服的书写异步任务,基本上在Dart语言中不会出现callback hell
      • 对于掌握上述任何一种语言的人来说,近乎可以以极小成本上手Dart
    • 再说Flutter
      • Flutter本质上其实算一个UI框架,一套代码到处跑。UI框架+原生引擎渲染
      • 一套优秀的Flutter代码,理论上可以在Android/iOS/Web/Fuchsia各种环境和操作系统上运行,并且保证UI界面的高度一致性
      • Flutter的数据结构体系,本质上与其他技术栈对于UI层控件的处理类似,都是一颗多叉树,创建并渲染UI的过程本质上就是建树和走树的过程
      • 在写Flutter的过程中,有时候甚至觉得自己不是在"写代码",更像是在一棵"树"上画画
      • 对于有任何一端移动开发经验尤其是Android开发经验的同学来说,Flutter的上手门槛是0

巨人的肩膀: Flutter官方示例Flutter中文网