Skip to main content

Fllutter UI 上的内容

绝对布局组件用来按计算值进行布局.

Flutter 里面的许多 Widget 结构都是: 一个包裹 Widget, 一个提供数据的对象组合起来. 比如 Image + Provider, Icon + Data

布局时约束考虑就是自己的约束和父级的约束

布局 cheatsheet: https://medium.com/flutter-community/flutter-layout-cheat-sheet-5363348d037e 动画 cheatsheet: https://medium.com/flutter-community/flutter-animations-cheat-sheet-7f8cebfb850c https://docs.flutter.dev/development/ui/widgets/layout https://docs.flutter.dev/development/ui/layout https://docs.flutter.dev/resources/inside-flutter https://github.com/flutter/flutter https://docs.flutter.dev/development/ui/layout/constraints 动画: 值的变化 + 具体的动作(比如旋转) + 触发

  1. Layout 的详细内涵
  2. 官网文档和例子
  3. Widget 熟悉

记住: Constraints go down. Sizes go up. Parent sets position.

一个典型的布局流程如下:

  1. Widget 从 parent 获取自己的 constraints. constraints 只是 4 个值, 即 最大/最小 宽度/高度.
  2. Widget 遍历自己的孩子, 告诉自己的每个孩子约束是什么, 然后让孩子确定孩子自己的大小.
  3. Widget 根据尺寸定位自己的孩子(孩子的 x 和 y 值, x 按从左到右, y 按上向下).
  4. Widget 告诉自己的父自己的尺寸, 从而让它的父也可以根据尺寸定位这个 Widget.

在一个 Padding 包裹 Column, 且 Column 有两个孩子的 Widget 布局中, 父容器有最多 300 * 85 的可用空间, 孩子和父的沟通过程在实际例子中的情况如下:

1. Widget: 向 parent 请求自己的 constraints
1. Parent: 你的约束是 width(80~300), height(30~85)
1. Widget: 我有 5 px 的 padding, 因此我的孩子只有最多 width(290) 和 height(75) 的可用空间
1. Widget: 孩子 1, 你有 width(0~290) height(0~75) 的可用空间
1. 孩子 1: 可以, 我会占 width(290), height(20)
1. Widget: 收到, 由于我 Column 的布局特性, 第一个孩子在顶上放入, 可以给孩子 2 剩下的空间是 width(0~290), height(0~55).
1. Widget: 孩子2, 你的可用空间是 width(0~290), height(0~55).
1. 孩子 2: 可以, 我想占 width(140), height(30)
1. Widget: 收到, 因为设置的是主轴居中, 这样我第一个孩子位置 (x:5, y:5), 第二个孩子的位置 (x: 80, y: 25).
1. Widget: Hi parent, 我决定自己的尺寸是 width(300), height(60). (因为 Column 是匹配孩子大小的, 高度是 20 + 30 + 5 * 2, 宽度按最大孩子的宽度).

Flutter 布局限定:

  1. Widget 只能在父级给定约束条件下决定自己的尺寸. 因此自己的尺寸并不能脱离父级约束而自己直接决定采用多大.
  2. Widget 不能决定自己的位置, 位置是由 Parent 确定的.
  3. 由于 Parent 的尺寸和位置也是 Parent 的 Parent 给的, 因此要确定某个 Widget 的位置, 只有整棵树全盘考虑.
  4. 如果孩子需要需要的尺寸和父级给的约束不同, 但父级没有足够信息来确定它的位置, 孩子的尺寸设置可能被忽略. 因此在对齐时必须具体.(一个简单例子就是当设置孩子尺寸, 但父约束要求孩子占满自己的情况下, 设置的大小就会被忽略.)

细节:

  1. 约束有两类: loose 和 tight, tight 优先级大于 loose
  2. 约束值可以是具体, 也可以是无限.
  3. 如果孩子要的大小比父能给的多, 则父只能允许孩子在可用范围内要到最多...
  4. UnconstrainedBox 是个特例, 它给孩子说我可以给你任意大小, 孩子要得多的话会出现 overflow. 如果孩子要无限, 则不能渲染任何东西并且有错误日志打印.
  5. Container 匹配孩子大小, 且传入孩子的约束最大是它的父所能给的最大大小
  6. LimitedBox 只有在父给的是无限时才会起作用
  7. Screen(就是根容器) 对孩子施加的约束是 tight 的约束, 让孩子和自己一样大.
  8. FittedBox 只能限制已有限制的孩子, 如果孩子没有限制则无法缩放展示.
  9. Row/Column 不会对其孩子引入任何额外的约束.

Weekly Widget

列表地址: https://www.youtube.com/playlist?list=PLjxrf2q8roU23XGwz3Km7sQZFTdB996iG

  1. 导航: NavigationRail: index=4
  2. 桌面端按钮自定义: FocusableActionDetector 6
  3. UserDefaults: shared_preferences 7
  4. google_fonts: 可以在本地缓存使用的字体 8
  5. RepaintBoundary: 将孩子限制在自己的图层中渲染, 不会全部重绘, 提升性能: 9
  6. StatefulBuilder: 限制仅该区域的重建 10
  7. ScaffoldMessenger: 弹窗的跨导航保持 11
  8. DropdownButton: 下拉按钮 12
  9. Badges: 帮助实现 APP 内部的 Barge 展示, 比如有好多消息 13
  10. Baseline: Flutter 中的 Widget 通过它的顶部和高度来确定位置, 如果想要关心从底部开始布局的时候, 使用这个. 14
  11. get_it: 依赖注入 15
  12. path_provider: 用于访问设备的文件系统 16
  13. Freezed: 不可变数据建模 17
  14. GestureDetector: 不是按钮, 但还是想让某个 Widget 可交互, 比如可以在 Stack 中拖动孩子进行交互 18
  15. CachedNetworkImage: 缓存网络图片的包 19
  16. Theme: 可以在任意层级包裹并提供自定义的 ThemeData 20
  17. TabPageSelector: 几个 Tab 切换, 配合 TabBarWidget, 使用 TabController 控制这个 selector 21
  18. Flow: 可以对展开的流式布局进行编排, 还可以添加动画 22
  19. font_awesome_flutter: Font Awesome 的图标库 23
  20. RefreshIndicator: 列表下拉刷新的那种 24
  21. HeroMode: 用来设置 Hero 动画的开关 26
  22. Collection: 提供集合类的许多算法的包 27
  23. MouseRegion: 判断鼠标是否在某个区域内, 在桌面实现时比较有用, 还可以改变鼠标的样式, 监听进入退出 29
  24. animated_text_kit: 提供许多的文本动画的包, 比如 typewritter 动画等 30
  25. Connectivity: 监听网络状态的包, 比如什么网络, 有没有网络等 32
  26. Scrollbar: 正常情况下 scrollView 没有滚动条, 要展示的话, 需要添加这个. 还可以横竖滚动条 33
  27. ExpansionPanel: 对 Widget 进行折叠和展开的组件, 在列表或展示详细信息时非常有用 34
  28. RotatedBox: 可以对界面组件进行旋转, 而且和直接变换不同, 因为它可以遵守之前整体的布局方式, 就好像那个部分没有旋转一样, 另外还可以用来动画 35
  29. flutter_slidable 包: 可以在列表上展示那种划过去有多个按钮的样式 36
  30. Animations 包: 提供非常有用的动画, 需要熟悉!! 37
  31. PhysicalModel: 如果有自定义阴影的需求, 这个非常好用! 38
  32. ImageFiltered: 虽然叫 ImageFiltered, 但实际上它是对渲染出来的任意图层进行加工, 只需要包裹 Widget 即可!! 提供比如 blur 等效果, 非常有用! 如果想对它下面的背景进行比如 blur 加工, 则可以使用 BackdropFilter. 只是 backdrop 的性能不是太高... 39
  33. Device_info 包: 提供设备的详细信息, 在事件记录时非常有用, 实际已升级为了 device_info_plus! 40 https://pub.dev/packages/device_info_plus 40
  34. SwitchListTile: 列表中的带开关按钮的行, 实际还有类似的比如 RatioListTile, CheckboxListTile, xxxTile, 都是一个道理 42
  35. GridView: 网格滑动视图 43
  36. url_launcher: 启动 URL, 打开网页, 邮件等 47
  37. Package:async 包, 提供异步编程的许多支持功能 48
  38. Padding: 基础组件 51
  39. AnimatedWidget: 其他的 Widget 可以继承它, 用来实现自定义动画 52
  40. ClipOval: 可以将孩子包裹起来, 提供圆角等, 还可以提供任意图形的自定义 clipper. 53
  41. CupertinoActivityIndicator: apple 风格的菊花图标. 54
  42. IgnorePointer: 非常有用的一个组件, 可以将孩子区域设置为用户是否可交互, 但不会影响到它的外观, 类似 AbsorbPointer. 55
  43. Divider: 基础组件, 在布局时提供一个横隔 56
  44. CircularProgressIndicator 和 LinearProgressIndicator: 提供进度展示支持 57
  45. ClipPath: Clip 孩子, 任意形状 58
  46. Builder: 基础组件 59
  47. NotificationListener: 基础组件, 提供各类事件通知的监听 60
  48. ShaderMask: 提供对孩子的渲染 Mask, 比如对文字提供渐变展示等 61
  49. ListWheelScrollView: 可以实现比如日期设置的那种中间一个变大的效果, 轮式的选择列表 62
  50. SnackBar: 基础组件, 提供简单的消息通知, 比如"设备已连接" 这类的, 在展示的时候需要一个 Scaffold 提供上下文环境, 如果要在导航的前后都能展示, 则可以结合 ScaffoldMessenger 使用 63
  51. Drawer: 侧边可隐藏的菜单 64
  52. DefaultTabController & TabBar: 提供分 Tab 的展示, 内容可以通过 TabView 提供, 配合 TabController 实现转换 65
  53. Image: 基础组件, 展示图片, 还可以提供加载过程的数据, 这样可以添加加载进度, 支持若干图片格式, 包括 webp, gif, 如果 svg, 则需要使用 svg 的包 66
  54. TweenAnimationBuilder: 插值动画 67
  55. CupertinoActionSheet: 苹果风格的一个选项列表 68
  56. ToggleButtons: 类似编辑器上的一系列开关, 比如点击加粗的按钮 69
  57. ColorFiltered: 对 Widget 颜色进行过滤, 比如可以对图片设置不同颜色, 还有许多的 Blend 模式可选 70
  58. DraggableScrollableSheet: 可以将一个 Widget 滑动到屏幕内, 到达屏幕内后, 可以持续滚动, 类似电信营业厅客户端的下面那个可以拖上来的界面 71
  59. AnimatedCrossFade: 可以在两个不同 Widget 间实现 Fade 动画转换, 比如将未连接状态动画变化为已连接 72
  60. AlertDialog: 实现类似 SheetWindow 那种的弹窗, 有 苹果和 Material 风格可选, 内容可以自定义, 风格可以自定义, 还可以自定义用户点击外部是否隐藏弹窗的能力 73
  61. Slider, RangeSlider, and CupertinoSlider: 通过滚动条的形式设置某个值, 另外还可以用来展示步骤, 这个在 step 1 -> step4 这类的情况非常有用 74
  62. DataTable: 数据表, 比较适合展示设备信息等的列表 75
  63. SelectableText: 可选的文本 76
  64. Container: 基础组件, 比如设置背景图/背景色, 大小约束, 孩子在容器中的位置等 77
  65. ListView: 滑动列表组件 79
  66. FractionallySizedBox: 包裹某个组件, 可以设置自己的size约束是父级的百分比, 在 Row 或 Column 中, 需要结合 Flexible 使用 80
  67. AnimatedOpacity: 可以动画改变某个组件的透明度, 这样它在一系列的组件中就可以突出展示(同时降低其他组件的透明度), 且隐式动画. 如果要详细控制, 可以使用 FadeTransition 81
  68. Stack: 在内部可以对多个孩子进行层叠布局, 可以设置自己的约束模式(匹配孩子还是匹配父), 配合 Positioned 来对每个孩子的位置进行设置 82
  69. ConstrainedBox: 基础组件, 可以对包裹的孩子设置 max/min 的 width/height, 比如一个图片, 可以约束它在窗口扩大缩小时候的最大大小 83
  70. IndexedStack: 可以同时保持多个孩子的状态, 每次只展示某个孩子在界面上, 这样用户就可以切换不同的孩子来看(类似非整个屏幕的 tab 切换), 配合 Slider 的一系列, 可以实现比如 step1 到 4 的这样, 或用在引导页面上 85
  71. AnimatedPadding: 动画 Padding, 可以根据值进行动画 86
  72. AnimatedPositioned: 用在 Stack 中(和 Positioned 类似), 可以让这个孩子根据值进行动画, 比如 Slider 实际就是这样实现的 87
  73. AnimatedSwitcher: 在两个不同组件间使用自定义动画进行切换, 默认的是 Fade 动画, 但可以自定义, 比如魔术效果切换, 如果两个 Widget 类型相同, 需要设置 key 来区分 88
  74. ReorderableListView: 可以拖动来改变 list 的行 89
  75. RichText: 类似 AttributedString 的那种文本组件, 可以设置文字风格, 段落... 90
  76. Placeholder: 基础组件, 特别适合在创建 UI 草图的时候, 使用它进行占位, 然后进行详细实现. 默认它会占满父空间, 如果放到没有宽度/高度约束的父内部, 还可以提供一个默认的大小 91
  77. LimitedBox: 基础组件, 在父本身约束为非绑的情况下, 给那些匹配父约束的孩子设置约束, 如果父有约束, 这个组件不会生效. 它在 ListView 中非常有用, 比如在 ListView 中展示 Container 时(Container 是匹配父约束的设置, 然后 ListView 自己又没有高度约束), 这个时候使用本组件就可以限制 Container 的尺寸 92
  78. AspectRatio: 基础组件, 在需要关心宽高比而非宽高具体数值的情况下非常有用, 提供的比例是 宽 / 高. 93
    • 还有一个情况是 AspectRatio 的父级没有某个约束(比如 Expanded), 但 AspectRatio 本身是匹配孩子的, 此时不确定 AspectRatio 应放到哪个位置, 可以在中间加一个 Align, 这样父还是填满父的父空间, 而 AspectRatio 自己就可以根据设置定位到具体的放置位置.
  79. AnimatedIcon: 基础组件, Flutter 提供的一系列动画图标 94
  80. InheritedWidget: 基础组件, 可以说是现代状态管理框架的基础. 它自己可以在树中任意位置插入, 这样子树中任意位置都可以拿到它, 孩子可以观察它并对应进行更新(它内部有 updateShouldNotify 方法), 配合 Notifier 就可以实现状态更新后的通知. 95
  81. Spacer: 基础组件, 用在 Row 或 Column 中, 提供额外的 Space 从而可以进行更好的自定义分配. 96
  82. MediaQuery: 基础组件, 查询屏幕相关的信息, 比如宽度, 用户字体大小系统设置等, 即用户 UI 设置. 97
  83. Flexible: 在 Row 或 Column 中自定义孩子的宽高占比, 比如设置三个孩子是 1 : 3 : 1. 还可以设置孩子是匹配父大小还是孩子匹配自己或自己孩子大小. 98
  84. AnimatedList: 提供 LIst 的动画能力, 比如插入删除更新表行. 99
  85. Draggable: 提供非常便捷的拖动交互能力, 比如把一个 Widget 拖到另外一个里面. 100
  86. ValueListenableBuilder: 注入后可以观察这个值的改变 101
  87. SizedBox: 基础组件, 提供 Widget 的具体尺寸, 比如固定大小的按钮, 并且如果有填满 Parent 的需求, 可以设置为 infinite, 或使用 SizedBox.expand 便捷生成 102
  88. Dismissible: 提供 Widget 的滑动移除能力 103
  89. AnimatedBuilder: 提供直接构造动画 Widget 的能力, 这里面说了如何写动画 104
  90. Positioned: 配合在 Stack 中指定孩子的位置 106
  91. Align: 在任意 Parent Widget 中指定孩子的位置, 也可以在 Stack 中, 类似 Positioned 的能力 107
  92. BackdropFilter: 提供 Widget 的渲染改变能力, 比较适合一些特殊效果的实现 108
  93. Transform: 提供将 Widget 进行三维转换的能力, 109
  94. AbsorbPointer: 同样是一个提供被包裹的 Widget 不被点击的能力, 类似 IgnorePointer. 110
  95. LayoutBuilder: 提供一个 build 回调, 在孩子进行前获取 constraints, 从而在 builder 中决定孩子的具体处理 111
  96. FittedBox: 基础组件, 提供让孩子在容器中以指定方式放入的能力 112
  97. Tooltip: 提供任意 Widget 的 Tooltip 113
  98. CustomPaint: 基础组件, 提供自定义绘制的能力. 实现时传入自定义的 Painter 类(继承 CustomPainter 重写里面的方法)作为绘制代码的实现. 114
  99. Hero: 实现 Hero 动画. 即提供在两个 Route 之间进行 Hero 动画的能力, 两个 route 内有一个共通的 Widget 作为 Hero 115
  100. ClipRRect: 提供 Widget 圆角能力, 同时有一些属性可以自定义, Flutter 还提供了 ClipPath, ClipOval 等... 116
  101. InheritedModel: 类似 selector 的能力, 配合 InheritedWidget, 当孩子用 InheritedModel 包裹时, 可以观察仅某个属性改变才更新 117
  102. StreamBuilder: 可以和一个 Stream 配合使用, 当 stream 有新 Event 后触发更新 118
  103. FadeInImage: 在图片加载时提供 PlaceHolder, 而且可以展示进度. 类似 SDWebImage 提供的能力 119
  104. SliverList & SliverGrid: 提供滚动组合的能力, Sliver 只是可滚动区域的部分描述. 它们两个一起可以实现复杂的滚动效果. 120
  105. SliverAppBar: 提供针对 AppBar 的滚动展示能力 121
  106. Table: 提供数据表的展示能力, 不需要滚动的时候 122
  107. PageView: 提供 Page 展示能力, 可以在多个 Page 间切换, 只需要配合 PageController 使用 123
  108. FloatingActionButton: 浮动的按钮 124
  109. FadeTransition: 简单的 Fade 动画 125
  110. FutureBuilder: 基础组件, 可以在 Future 满足后重建孩子. 126
  111. Opacity: 提供设置 Widget 的透明度能力, 动画版本是 AnimatedOpacity, 比如在 Stack 中层叠两个孩子, 上层有透明度这样. 实现 welcome 的时候可以参考 127
  112. AnimatedContainer: 基础组件, 隐式动画容器 128
  113. Wrap: 流式布局, 在孩子空间不足时自动换行(换列) 129
  114. Expanded: 配合 Row 或 Column 使用, 让孩子可以占满可用空间, 或设置 flex 属性让孩子占多少百分比(是 Flexible 的再封装) 130
  115. SafeArea: 提供安全区, 避免被弄. 在桌面实现时, 用在弹窗中, 可以避开系统的一些区域. 131