尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Flutter for OpenHarmony:Scaffold与Container搭建三段式布局

Flutter for OpenHarmony:Scaffold与Container搭建三段式布局 最近在折腾 Flutter for OpenHarmony 的时候我发现很多刚接触这套组合的朋友问的第一个问题往往不是动画怎么做、性能怎么调而是最基础的这个页面的骨架到底怎么搭。从 Android、iOS 或者纯 Web 前端转过来的开发者看到 OpenHarmony 工程里那一坨 Flutter 代码第一反应基本都是——Scaffold 到底是干什么的为什么好好的页面不直接画非要套一层又一层的 Container这篇文章就从一个最常见的需求入手做一个三段式布局顶栏、内容区、底栏从 Scaffold 的骨架搭建一路拆到 Container 的尺寸与装饰细节把 Flutter 页面从 0 到 1 的完整构建过程过一遍。适合刚把 Flutter 环境跑通、正准备在 OpenHarmony 设备上写页面的初学者也适合已经有一两个页面经验、想系统补一补布局原理的朋友。1. 先拆需求三段式布局并不是简单的上中下1.1 一个任务清单页面长什么样我拿「任务清单」来举例这是 App 开发里最典型的页面之一顶部有一个标题栏写当前页面名称中间是主体内容区通常是一块可滚动的列表底部是操作栏放新建任务、统计信息这类高频动作。把这个结构抽象出来就是三个横向条带叠加在一起这就是手机端最常见的“三段式”页面骨架。你别觉得它简单几乎所有能上架的 App首页拆到最后都能归到这个模式上。电商首页的顶部搜索栏、中部的金刚区和商品瀑布流、底部 Tab 栏也都是这个思路的变体。所以把三段式布局吃透等于掌握了一大类页面的构建方法。在 Flutter 里做这个正常思路是用 Scaffold 把三段的位置占好再用 Container 去填充每一段的具体内容。为什么是 Scaffold 加 Container 这个组合而不是全部手写或者全部用高级组件这背后其实有一个职责划分的问题。1.2 Scaffold 和 Container 各自负责什么Scaffold 是 Material 设计体系里的页面骨架它负责给你把页面的大结构摆好顶上有没有 AppBar、底下有没有 bottomNavigationBar、右下角有没有 FAB、侧边有没有抽屉这些槽位在 Scaffold 里都是现成的位置。它还有一个很实用的能力自动感知系统状态栏高度、底部手势条区域、屏幕刘海这些环境信息然后帮你处理安全区。你在 Scaffold 里写 appBar它自动顶开系统状态栏你写 bottomNavigationBar它也会把这个区域贴到屏幕底部不让你自己去量设备参数。Container 就不一样了它是个“盒子”负责页面内部某一小块内容的尺寸、颜色、圆角、边距、对齐方式这些视觉与布局属性。你可以把它理解成装修时的木工板Scaffold 把房间的毛坯墙砌好了剩下每个区域怎么装饰是铺地板、贴瓷砖还是刷漆都是 Container 的事。1.3 为什么先用 Scaffold再谈 Container很多初学者习惯自己包一层 SafeArea 加 Column 硬写三段也不是不行但那相当于把毛坯墙敲掉自己重新砌。你要手动处理状态栏高度、底部手势条、键盘弹出时的避让做了很多没有价值的重复劳动。先用 Scaffold 定骨架的好处是很多环境问题它已经替你处理了代码结构也清晰——第 1 段是顶栏第 2 段是 body第 3 段是底部导航别人接手你的代码一眼就能看懂。而 Container 的价值在模块内部体现它让每一段的具体内容有了统一的“盒子”外壳方便设置背景、内边距、阴影和圆角。所以正确的姿势是外层用 Scaffold 划分区域内层用 Container 塑造视觉。先定骨架再填血肉这就是从 Scaffold 到 Container 的核心思路。2. 环境准备让 Flutter 在 OpenHarmony 上跑起来2.1 工具链和系统版本怎么选OpenHarmony 上的 Flutter 开发和普通 Flutter 开发不太一样官方主线的 Flutter SDK 目前并不直接支持 ohos 平台。实际开发用的是 OpenHarmony SIG 维护的 flutter_flutter 分支它额外支持生成ohos平台工程。你需要的工具链大概是这样DevEco StudioOpenHarmony/HarmonyOS 官方的 IDE用来做签名配置、查看设备日志、管理 OpenHarmony SDK。个人开发用社区版就行。OpenHarmony SDK在 DevEco Studio 的 SDK Manager 里下载API 版本建议选你目标设备能支持的较高版本API 12 以上目前比较常见。flutter_flutter 分支从 openharmony-sig 仓库拉取不同版本对应的 Flutter 版本差异很大。网上能搜到的指导教程有的用 dev 分支有的用某个特定 release 分支照抄之前一定先看仓库 README。hdc 工具OpenHarmony 的设备连接调试工具类似于 Android 的 adbDevEco Studio 里自带装了 IDE 就有。这里有个容易踩的坑不要看到 Flutter 官方版本号已经到 3.2x、3.3x 了就顺手拿来给 OpenHarmony 用。官方主线和 OHOS 分支是两套东西版本号对齐情况每个阶段都不一样。我自己的习惯是锁定一个已经验证过的分支组合用 fvm 管理避免今天能跑明天全红的局面。2.2 创建带 ohos 平台的 Flutter 工程环境装好后先用flutter doctor看看基础项是否通过。然后拉分支、配置 PATH大概是这么个顺序# 拉取 OpenHarmony SIG 的 flutter_flutter 分支 git clone https://gitee.com/openharmony-sig/flutter_flutter.git cd flutter_flutter # 切到你需要的版本分支比如 dev 或者某个 release 分支 git checkout dev # 把 flutter 命令指向这个分支的 bin 目录 export PATH$PWD/bin:$PATH # 验证版本 flutter --version接着创建工程关键点在--platforms参数上flutter create --platforms ohos --org com.example three_section_demo cd three_section_demo执行完你会看到工程里多了一个ohos目录这就是 OpenHarmony 的原生工程壳子后面签名、编译 HAP 包都从它入手。如果你省略--platforms ohos默认生成的只有 android、ios、web 这些目录跟 OpenHarmony 搭不上边。2.3 第一次构建最容易卡住的点我第一次跑通这个过程踩了不少坑最有代表性的一个flutter 命令能执行flutter doctor也正常但创建工程后没有 ohos 目录。原因很简单PATH 里先找到的 Flutter 是官方主线的而不是 OHOS 分支的。解决办法是检查flutter --version输出确认它的路径指向 flutter_flutter 分支或者干脆用 fvm 绑定到分支目录。还有一个坑是 DevEco Studio 打开ohos目录后提示 SDK 路径不对。这种情况通常是因为 DevEco Studio 里设置的 OpenHarmony SDK 版本和分支要求的版本不一致进 SDK Manager 重新选一遍就好。3. 核心实现用 Scaffold 搭骨架用 Container 填血肉3.1 三段式的三个收纳位怎么划分我们先画出页面骨架。Scaffold 提供了非常合适的三个插槽appBar 放顶栏body 放中间内容bottomNavigationBar 放底栏。先别急着写代码我得提醒一句这里的 bottomNavigationBar 不等于你只能在里面放 BottomNavigationBar 组件它只是一个槽位放任何 Widget 都行。所以我们可以放一个自己用 Container 画的操作条这就给三段式布局留出了巨大的自由空间。骨架代码是这样Scaffold( appBar: AppBar( title: const Text(任务清单), centerTitle: true, ), body: Container( color: const Color(0xFFF2F3F5), child: /* 这里放中间内容区 */ ), bottomNavigationBar: /* 这里放自绘底栏 */, );这一层除了位置划分没有做多余的装饰。真正要花心思的是每一段内部怎么用 Container 去落实视觉细节。3.2 顶栏用 AppBar 还是自己画 ContainerScaffold 的 appBar 槽位最省心的选择就是 AppBar 组件它自动处理了状态栏高度、标题字号、加载动画位置这些细节。但如果你想完全掌控顶栏的样式比如做搜索框、做多按钮自定义布局也可以放一个 Container 进去。我给你看一个用 Container 手画顶栏的例子Container( height: 48, width: double.infinity, color: Theme.of(context).colorScheme.primary, alignment: Alignment.center, child: const Text( 任务清单, style: TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.w600), ), )这个版本看起来简单但它有一个隐藏问题直接放在 Scaffold body 的 Column 顶部时状态栏会盖住它。这时候就要靠外层套 SafeArea或者用 MediaQuery 手动避让。我在实际项目中试过一旦顶栏要做自定义搜索框、扫一扫、消息入口这些复杂组合用手画 Container 反而更灵活如果只是放个标题AppBar 是更稳的选择。建议能上 AppBar 就上 AppBar别为了炫技自己画。自定义需求出现时再容器化不迟。3.3 内容区Container ListView 渲染卡片流中间内容区是整个页面里最有信息量的一块我习惯用一个 Container 先铺底色再放 ListView 进去。Container 负责背景和外边距ListView 负责滚动和性能。这里有个很重要的认知Container 不是滚动容器它自己不能滚动。凡是内容超出屏幕的部分必须交给 ListView 或 SingleChildScrollView 这类可滚动组件。Container 在中间内容区的角色是“承载者”负责把底色铺好再把滚动区域圈起来。任务卡片本身也是 Container。每个卡片需要白色背景、圆角、阴影、内边距这些属性全部通过 BoxDecoration 配置。注意一个坑圆角和阴影不是 Container 的顶层属性而是 BoxDecoration 的属性。很多人直接把borderRadius写到 Container 参数里然后报错就是因为不了解这层关系。卡片容器大概是这个样子Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), boxShadow: const [ BoxShadow( color: Color(0x14000000), blurRadius: 8, offset: Offset(0, 2), ), ], ), child: Row( children: [ // 状态圆点、任务标题、状态文本 ], ), )在这里Container 的 padding 控制卡片内部文字和边界的距离margin 控制卡片和卡片之间的距离decoration 控制卡片看起来是什么样。这三个属性是三段式布局里所有模块卡片的核心套路学会了以后做任何列表页都能往上套。3.4 底栏用 Container 手写一个不依赖 BottomAppBar 的操作条底栏我建议直接手写 Container而不是默认用 BottomNavigationBar。原因很现实BottomNavigationBar 的定制空间有限遇到“左侧按钮 右侧统计文字”这种自定义布局时你还得在它内部塞各种组合组件不如从一开始就用 Container 画。手写底栏的关键在于底部安全区。现在的手机普遍有手势条屏幕底部会有一段透明区域如果你不管它底栏的按钮就会被手势条盖住。处理办法是读取 MediaQuery 的 viewPadding.bottom把它加到容器高度里同时用 padding 把内容顶上去Container( height: 56 bottomInset, padding: EdgeInsets.only(bottom: bottomInset), decoration: const BoxDecoration( color: Colors.white, border: Border(top: BorderSide(color: Color(0xFFE0E0E0), width: 0.5)), ), child: Row( children: [ Expanded( child: TextButton.icon( onPressed: onCreate, icon: const Icon(Icons.add_circle_outline), label: const Text(新建任务), ), ), Text(共 $totalCount 条), ], ), )这里的 bottomInset 是根据设备动态读取的不是写死 0。不同设备的手势条高度不一样动态读取才能通吃。这也是我反复强调的OpenHarmony 设备型号杂安全区处理如果不动态早晚要出适配问题。3.5 整合后的完整页面代码把三段拼起来完整代码差不多长这样import package:flutter/material.dart; void main() { runApp(const TaskApp()); } class TaskApp extends StatelessWidget { const TaskApp({super.key}); override Widget build(BuildContext context) { return MaterialApp( title: 三段式布局示例, debugShowCheckedModeBanner: false, theme: ThemeData( useMaterial3: true, colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF1E90FF)), ), home: const HomePage(), ); } } class HomePage extends StatelessWidget { const HomePage({super.key}); override Widget build(BuildContext context) { final bottomInset MediaQuery.of(context).viewPadding.bottom; return Scaffold( appBar: AppBar( title: const Text(任务清单), centerTitle: true, backgroundColor: Theme.of(context).colorScheme.primary, foregroundColor: Colors.white, ), body: Container( width: double.infinity, color: const Color(0xFFF2F3F5), child: ListView.builder( padding: const EdgeInsets.all(16), itemCount: 8, itemBuilder: (context, index) _TaskCard(index: index), ), ), bottomNavigationBar: Container( height: 56 bottomInset, padding: EdgeInsets.only(bottom: bottomInset), decoration: const BoxDecoration( color: Colors.white, border: Border( top: BorderSide(color: Color(0xFFE0E0E0), width: 0.5), ), ), child: Row( children: [ Expanded( child: TextButton.icon( onPressed: () {}, icon: const Icon(Icons.add_circle_outline), label: const Text(新建任务), ), ), const Padding( padding: EdgeInsets.only(right: 16), child: Text(共 8 条), ), ], ), ), ); } } class _TaskCard extends StatelessWidget { final int index; const _TaskCard({required this.index}); override Widget build(BuildContext context) { return Container( margin: const EdgeInsets.only(bottom: 12), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), boxShadow: const [ BoxShadow( color: Color(0x14000000), blurRadius: 8, offset: Offset(0, 2), ), ], ), child: Row( children: [ Container( width: 12, height: 12, decoration: BoxDecoration( shape: BoxShape.circle, color: index.isEven ? const Color(0xFF1E90FF) : const Color(0xFFF5A623), ), ), const SizedBox(width: 12), Expanded( child: Text( 任务 ${index 1}, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w500), ), ), const Text( 待办, style: TextStyle(color: Color(0xFF999999), fontSize: 13), ), ], ), ); } }跑到设备上你能看到三段非常清晰地分开了蓝色顶栏、灰色内容区、白色底栏配圆角卡片。这就算一个合格的页面骨架了。4. Container 底层原理与常见布局坑4.1 Container 其实是一串组件的合成器Container 看起来像一个基础组件但它的底层其实是多个更基础组件的组合。这是 Flutter 面试里经常被考到的点也是理解“为什么 Container 有这么多参数”的关键。大致上Container 的 build 过程相当于按顺序把以下事情组合起来有 margin 就在外层套一个 Padding有 constraints 或宽高就套 ConstrainedBox有 padding 就再套一层 Padding有 alignment 就套 Align有 decoration 就套 DecoratedBox有 transform 就套 Transform。这些组件按固定顺序嵌套最后合成一个真正的渲染树。理解了这一点你就能解释很多迷惑行为为什么 Container 设置了 color 就不应该再在 decoration 里写 color因为它们最终是同一个 DecoratedBox 的属性通道Flutter 会直接报断言错误。这也是 Container 和 ColoredBox、SizedBox、Padding 这些单一组件的区别Container 是一个“多功能合体器”方便你用最少的嵌套写出同样效果但在性能和极简性上单一组件永远更轻。你可以把 Container 当成日常开发的主力但心里要清楚它到底是什么。4.2 Container 的尺寸决策逻辑Container 的尺寸规则很多人是靠试出来的其实内核很清晰如果 Container 有 child它会尽量贴合 child 的大小再加上 padding 带来的空间。如果 Container 没有 child也没有宽高和 constraints它就会尽可能填满父级给的可用空间。如果父级给的是无界约束比如 Row 里的水平方向无 child 的 Container 会收缩到 0。如果显式指定了 width、height它们会参与 ConstrainedBox 的约束计算最终尺寸不一定就是你写死的尺寸父级约束会压过它。这几句话看着抽象落到实际就是三个经典场景第一在一个 Row 里放一个有颜色但没有宽高的 Container你会发现它像一个 0 宽的竖线因为你没给它宽度无界约束下它没东西可撑。第二在 Scaffold 的 body 里放一个有颜色但没宽高的 Container它会自动铺满整个 body因为这里有紧约束。第三在 Column 里想让两个 Container 各占一半高度直接写死 height 容易被设备屏幕尺寸坑到正确做法是都用 Expanded 包一层。搞懂这些你就能少写很多“为什么我的颜色没显示出来”的调试时间。4.3 新手最容易翻车的两个案例案例一是“容器圆角死活不生效”。很多人这样写Container( color: Colors.white, borderRadius: BorderRadius.circular(12), )编译直接报错。因为 borderRadius 是 BoxDecoration 的子级属性给 Container 设置圆角必须走 decorationContainer( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), ), )这个错误我见过至少十几次都是同一个原因对 Container 的属性归属不理解把 BorderDecoration 的属性误当成 Container 顶层属性。案例二是“文字溢出出现黄黑色条纹”。三段式布局里最常见的是 Row 结构比如任务卡片的左侧圆点、中间文字、右侧状态一旦中间文字过长就会把 Row 撑爆。解决方法是把中间的 Text 用 Expanded 包起来再加上overflow: TextOverflow.ellipsis让文字在剩余空间内省略而不是无限拉伸。这两个案例一个是属性位置搞错一个是约束关系没吃透都是布局知识体系里的“地基问题”。地基打好后面的页面就不会反复返工。5. OpenHarmony 真机构建与适配验证5.1 设备连接与签名配置代码写完了最终要跑到 OpenHarmony 设备上看效果。真机调试需要先解决签名问题这是 OpenHarmony 和 Android/iOS 区别最大的一环也是新手最容易卡住的地方。在 DevEco Studio 里打开工程的ohos目录然后进 File Project Structure Signing Configs勾选自动签名登录开发者账号或者配置本机调试证书。签名配置完成后工程才能打包成一个可安装的 HAP 包。设备连接这边OpenHarmony 用 hdc 工具不是 adb。设备开 USB 调试后在终端里执行hdc list targets能看到设备序列号就说明连上了。此时回到 flutter 命令执行flutter devices应该也能识别出 ohos 设备。5.2 构建 HAP 与安装运行工程开发模式下最简单的是用 flutter run 直接跑flutter run -d 设备ID它会自动编译、打包、安装、启动。产物是.hap文件路径通常在ohos/entry/build/.../outputs/下。如果你只是要一个可安装包可以用 DevEco Studio 的 Build 菜单构建 HAP或者用命令行构建。这里有个体验差异OpenHarmony 分支的 hot reload 支持不如官方主线稳定。在 Android 上你改了代码按 r 就能看到效果在 OHOS 上偶尔会出现页面没刷新、或者状态错乱的情况。我的习惯是布局修改尽量用 hot restart不要依赖热重载只有涉及纯视觉微调时才试试热重载。5.3 真机上的安全区与状态栏适配真机一看很多模拟器上发现不了的问题就出来了。最常见的是状态栏和底部手势条。如果你用的是 Scaffold 的 AppBar状态栏避让是自动的不用操心。但如果你用手画的顶栏 Container就必须在它外面包 SafeArea或者用 MediaQuery 的状态栏高度撑开。否则顶栏文字直接顶进系统状态栏里这种视觉事故在真机上会显得特别业余。底部手势条同理。底栏如果写死高度 56在带手势条的设备上按钮会被系统导航区域盖住。我前面给的方案是动态读取 viewPadding.bottom这个值在真机上才有意义模拟器上经常是 0这也是为什么我强烈建议尽快上真机验证三段式布局的最终效果。6. 常见问题速查与排查实录6.1 问题、原因、解决对照表我把这段时间大家问得最多的问题整理成一个速查表方便你对着排查现象可能原因解决方式Container 圆角不生效borderRadius 写在顶层而不是 BoxDecoration 里改用 decoration 配置圆角Container 颜色不显示在 Row 等无界约束下没有宽高设置 width/height或包 Expanded文字溢出出现黄黑条纹Row 里 Text 超出剩余宽度Text 外包 Expanded加 overflow 省略页面只有一片白Container 没有设置 color也没有可见子组件明确设置 color/decoration顶栏压到状态栏里用手画 Container 且没处理安全区包 SafeArea或用 AppBar底栏内容被手势条遮挡没读取 viewPadding.bottom动态加上底部内边距hot reload 后布局异常OHOS 分支热重载支持不稳定改用 hot restart 或重新运行构建卡死或报签名错误未在 DevEco 配置签名进 Signing Configs 重新签名这些问题的根源绝大多数都集中在 Container 的属性使用和约束理解上。布局代码看起来简单但调试起来很耗时间把这张表背下来能少走很多弯路。6.2 从报错日志定位根因的经验有报错不可怕可怕的是不知道去哪找日志。我在 OpenHarmony 设备上调试的流程基本是三步第一步先看 Flutter 侧日志。flutter run 运行中的终端就有直接输出渲染异常、未捕获异常、Dart VM 的报错都会打在这里。看到[ERROR:flutter/runtime/dart_vm_initializer.cc]这种头部片段基本就是 Dart 层的问题。第二步通过 hdc 拉系统日志。如果 Flutter 侧日志不够用hdc hilog过滤关键 tag能看到原生侧有没有崩溃或者权限问题。这个方法在排查原生桥接、音频、传感器等问题时特别有用。第三步戴上“二分法”的眼镜。三段式布局出了问题先把顶栏、内容区、底栏拆开单独设置不同颜色背景肉眼定位是哪一段出问题再去查那一段内部的约束和属性。别从头到尾扫代码效率太低。6.3 三段式布局在 OHOS 设备上的实测表现我在 OpenHarmony 真机上跑了差不多一周这个骨架整体感受是布局本身没有任何平台阻碍。Flutter 的声明式布局在 OHOS 上同样流畅Container 这种组件不存在兼容性问题。真正需要关注的是迭代链路从改代码到看到真机效果的时长比 Android 要长不少。主要原因在于构建 HAP 的耗时和热重载的稳定性。如果你是在做业务页面这个节奏影响不大如果在做高频视觉调优建议把视觉参数尽可能抽成常量一次修改多处生效减少反复构建的次数。另外提一句OpenHarmony 分支对渲染引擎的跟进也是循序渐进的你可以持续关注 Impeller 这类新渲染引擎在 OHOS 上的落地进度但对当前的三段式布局开发没有实质影响布局代码写法和渲染引擎无关。7. 进阶方向组件通信与原生能力接入7.1 MethodChannel 与 EventChannel 的使用场景三段式布局只是页面外壳页面真正活起来必须和数据、原生能力交互。在 Flutter for OpenHarmony 里走的是和标准 Flutter 一致的通道机制MethodChannel 用于主动调用和一次性获取结果EventChannel 用于持续接收原生侧的事件流。举个例子你想在任务清单页读取设备型号可以在 Flutter 侧创建 MethodChannel调用原生方法获取结果你想监听网络状态变化原生侧持续上报Flutter 侧用 EventChannel 接收。这套机制是组件通信的基础布局里的按钮点击、卡片刷新最终都会接到这里。具体到 OHOS 侧的原生实现需要在 DevEco Studio 的工程里找到对应的 Ability Context用 HarmonyOS 的 API 注册通道。细节和 Android 侧略有差异但概念上完全可以迁移。7.2 PlatformView 与原生控件嵌入布局做大了难免遇到 Flutter 自己画不出来的东西比如高德地图、相机预览、视频播放控件。这类场景要用 PlatformView 把原生视图嵌进 Flutter 页面。在 OpenHarmony 分支上PlatformView 的支持能力还在持续完善不同的重构版本对原生的适配程度不一样。我的建议是动手前先查当前分支的支持范围别默认它能稳稳承载所有原生控件。先用最简单的原生视图打个样验证通道可用性再上真业务。这也是为什么我把 MediaQuery 和安全区的适配放在前面讲PlatformView 嵌入后原生视图的尺寸和位置管理会比纯 Flutter 区域更复杂底子不牢后面接原生控件时更容易乱。7.3 路由状态保持与功能扩展建议三段式布局里如果涉及页面跳转可以用 Navigator 做路由。很多人问 Navigator 切换页面后会不会丢失状态答案取决于你是否做了保活处理。普通的 push 进去再回来原页面的状态默认还在但如果页面被销毁了一部分比如 Tab 切换用了便宜的切换方式状态就丢了。想保持列表滚动位置和子组件状态可以用 IndexedStack 保存多页面实例或者用 AutomaticKeepAliveClientMixin 给列表页面做保活。这些都是三式布局业务化之后立刻会碰到的点。再往后扩展内容区可以接 RefreshIndicator 做下拉刷新底栏可以接 FloatingActionButton 做快捷入口顶栏可以做搜索联动。骨架搭对了业务功能往上挂只是在 Container 里换内容的事。最后再说一个我自己在实际开发中养成的习惯不管三段式布局多简单我都会在最小屏的设备和最常见的大屏设备上各跑一遍只验证一件事——三段的高度分配和内容溢出。很多问题在开发设备上看不出来换到小屏就露馅。页面贵在稳能在任何设备上保持三段式布局的结构清晰比盲目堆叠花哨动画重要得多。往后的项目里把 Scaffold 当骨架、把 Container 当积木这个思路可以一直复用下去。
返回列表