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

资讯详情

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

Flutter 文字排版与 Material 主题定制实战:深度解析 Flutter Gallery 的 Fortnightly 示例

Flutter 文字排版与 Material 主题定制实战:深度解析 Flutter Gallery 的 Fortnightly 示例 Flutter 文字排版与 Material 主题定制实战深度解析 Flutter Gallery 的 Fortnightly 示例【免费下载链接】flutterFlutter makes it easy and fast to build beautiful apps for mobile and beyond项目地址: https://gitcode.com/GitHub_Trending/flutter41/flutterFortnightly 是 Flutter 官方 Gallery 中一个专注于文字密集型 UI的完整示例它还原了一个假想的在线报纸应用是学习打印级排版、Material 主题定制TextTheme/ThemeData与长文布局的首选范例。本文以 Fortnightly 目录下的 README 为骨架结合其唯一实现文件 fortnightly.dart 逐段拆解读完后你能掌握如何用MaterialApp的theme:参数注入整套排版主题、用TextStyle.apply()派生局部字样式、用StackSafeArea实现浮动短应用栏以及如何通过pubspec.yaml声明多字重字体并在代码中用FontWeight精确选取。1. Fortnightly 的定位Gallery 中的报纸排版演示README 开宗明义Fortnightly 是一个基于 Material 设计风格研究Material studyFortnightly——一份假想的在线报纸——构建的 Flutter 示例应用展示打印级自定义排版、Material 主题定制以及以文字为主体的 UI 设计与布局README 中给出的 material.io 官方设计研究页面属于外部资料本文不再附链接可自行按名称检索。从仓库注册方式看它作为 Flutter Gallery 的正式演示之一被登记在 demos.dart 中GalleryDemo( title: Fortnightly, subtitle: Newspaper typography app, icon: GalleryIcons.custom_typography, category: _kDemos, routeName: FortnightlyDemo.routeName, // /fortnightly buildRoute: (BuildContext context) const FortnightlyDemo(), ),路由常量/fortnightly定义在 fortnightly.dart 第 10 行示例通过 all.dart 统一导出。2. 示例的教学目标与涉及的 Widget/APIREADME 将学习目标归纳为两条帮助你理解如何定制与排布文字提供以下三部分的示例代码Text文字一个短应用栏左上角的菜单按钮头像图片AvatarREADME 列出的 Widget/API 清单及其在源码中的真实落点如下API在源码中的使用位置BeveledRectangleBorderShortAppBar的Material.shape切出圆角 22 的右下角Container的BoxConstraintsFruitPage头图的BoxConstraints.expand(height: 248)CircleAvatar作者头像backgroundImage为ExactAssetImageExactAssetImage头像图片提供器scale恒为 1字体FontsMerriweather、LibreFranklin在pubspec.yaml声明SafeArea保护短应用栏不被刘海/状态栏遮挡保护正文底部StackFruitPage与ShortAppBar的叠放SingleChildScrollView整篇文章的滚动容器Text/TextStyle/TextTheme全文排版3. 页面骨架FortnightlyDemo与浮动短应用栏3.1 主题通过构造参数注入MaterialAppREADME 第一条 Notice 强调主题以构造参数形式传给MaterialApptheme:。源码印证// fortnightly.dart L13-L32 Widget build(BuildContext context) { return MaterialApp( title: Fortnightly Demo, theme: _fortnightlyTheme, home: Scaffold( body: Stack( children: Widget[ const FruitPage(), SafeArea( child: ShortAppBar( onBackPressed: () { Navigator.pop(context); }, ), ), ], ), ), ); }页面是一个Stack底层是可滚动的整篇文章FruitPage上层是包在SafeArea里的ShortAppBar。这正是 README Notice 中SafeArea为带刘海屏与虚拟按键的屏幕如 iPhone X 及以上机型添加内边距的体现——它保证短应用栏不会与状态栏时间区域重叠且文章底部在需要时也会有底部留白。3.2ShortAppBar96×50 的浮层式菜单栏ShortAppBar 的完整实现L35-L68 展示了如何用极少代码构造报纸报头式的短应用栏SizedBox( width: 96, height: 50, child: Material( color: Theme.of(context).colorScheme.surface, elevation: 4, shape: const BeveledRectangleBorder( borderRadius: BorderRadius.only(bottomRight: Radius.circular(22)), ), child: Row( children: Widget[ IconButton( icon: const Icon(Icons.arrow_back), tooltip: Back, onPressed: onBackPressed, ), const SizedBox(width: 12), Image.asset( logos/fortnightly/fortnightly_logo.png, package: flutter_gallery_assets, ), ], ), ), )要点Material自带elevation: 4阴影配合BeveledRectangleBorder只圆bottomRight一角形成从左上角探出的卡片效果返回按钮通过onBackPressed回调与页面解耦页面层用Navigator.pop(context)响应报头 Logo 通过Image.asset从依赖包flutter_gallery_assets加载该资产已在 flutter_gallery 的 pubspec.yaml 第 46 行 声明packages/flutter_gallery_assets/logos/fortnightly/fortnightly_logo.png。4.FruitPage文字密集页面的布局解剖FruitPageL70-L178 是一篇完整的水果文化文章页其布局链条为SingleChildScrollView └─ SafeArea(top: false) └─ ColoredBox(color: colorScheme.surface) └─ Column ├─ Container(BoxConstraints.expand(height: 248) Image.asset fit: fitWidth) ├─ SizedBox(height: 17) └─ Padding(horizontal: 8) └─ Column(标签行 / 标题 / 副标题 / 作者行 / 正文)对应 README 的几条 Notice 逐一说明SingleChildScrollView包裹整篇文章保证无论屏幕尺寸与横竖屏如何全文都可完整滚动查看SafeArea(top: false)正文顶部不再避让因为顶部要顶格展示头图底部仍保留安全区留白BoxConstraintsonContainer头图用BoxConstraints.expand(height: 248)固定高度并横向撑满Image.asset(..., fit: BoxFit.fitWidth)保证图片按宽度等比缩放。4.1 标签行用.apply()派生继承样式文章顶部 US ¬ CULTURE 标签行中中间的¬分隔符展示了 README 点名的技巧——TextStyle.apply()只修改继承样式的一个参数Text(US, style: textTheme.labelSmall), Text( ¬ , style: textTheme.labelSmall!.apply(color: textTheme.displayMedium!.color), ), Text(CULTURE, style: textTheme.labelSmall),labelSmall继承自主题黑色、10px、w700 的LibreFranklin而¬仅把color覆盖为displayMedium的灰色。apply()的完整签名见 text_style.dart 第 971 行它支持color、fontSizeFactor/fontSizeDelta、fontWeightDelta按枚举索引加减字重等参数适合基于继承样式做单点微调的场景避免手写整份TextStyle。4.2 作者行CircleAvatarExactAssetImage 显式TextStyleconst CircleAvatar( backgroundImage: ExactAssetImage( people/square/trevor.png, package: flutter_gallery_assets, ), radius: 20, ), const SizedBox(width: 12), Text(by, style: textTheme.displayMedium), const SizedBox(width: 4), const Text( Connor Eghan, style: TextStyle( fontFamily: Merriweather, fontSize: 18, fontWeight: FontWeight.w500, color: Colors.black, ), ),这里对应 README 的另外两条 Notice头像用CircleAvatarradius: 20ExactAssetImage。ExactAssetImage是AssetBundleImageProvider的子类其 keyName 直接拼接packages/$package/$assetName且scale固定为传入值默认 1.0因此跨包引用头像资产时尺寸不会因设备像素比变化是精确像素头像的可靠选择。Connor Eghan的作者名没有走主题继承而是显式创建TextStyleMerriweather、18px、w500、黑色——这是 README 特意对比的显式样式 vs 主题继承两种写法前者完全脱离主题、样式恒定后者如by使用displayMedium会随主题变化。4.3 长字符串多行字面量 replaceAllREADME 指出可以在源码中把长字符串拆成多行。FruitPage的两个段落正是范例static final String paragraph1 Have you ever held a quince? Its strange; covered in a fuzz somewhere between peach skin and a spider web. And its hard as soft lumber. Youd be forgiven for thinking its veneered Larch-wood. But inhale the aroma and youll instantly know you have something wonderful. Its scent can fill a room for days. And all this before youve even cooked it. .replaceAll(\n, );Dart 三引号多行字符串保留可读的换行排版再用.replaceAll(\n, )把换行折叠为空格最终得到排版正确、源码可读的长文本。5. 排版主题_fortnightlyTheme与TextTheme定制整个示例的视觉基调由 L180-L234 的主题构建代码 决定final ThemeData _fortnightlyTheme _buildFortnightlyTheme(); ThemeData _buildFortnightlyTheme() { final base ThemeData(); return base.copyWith( primaryTextTheme: _buildTextTheme(base.primaryTextTheme), scaffoldBackgroundColor: Colors.white, ); } TextTheme _buildTextTheme(TextTheme base) { TextTheme theme base.apply(bodyColor: Colors.black); theme theme.apply(displayColor: Colors.black); theme theme.copyWith( headlineMedium: base.headlineMedium!.copyWith( fontFamily: Merriweather, fontStyle: FontStyle.italic, fontSize: 28, fontWeight: FontWeight.w800, color: Colors.black, height: .88, ), displayMedium: base.displayMedium!.copyWith( fontFamily: LibreFranklin, fontSize: 18, fontWeight: FontWeight.w500, color: Colors.black.withAlpha(153), ), headlineSmall: base.headlineSmall!.copyWith(fontWeight: FontWeight.w500), bodyMedium: base.bodyMedium!.copyWith( fontFamily: Merriweather, fontSize: 14, fontWeight: FontWeight.w300, color: const Color(0xFF666666), height: 1.11, ), bodyLarge: base.bodyLarge!.copyWith( fontFamily: Merriweather, fontSize: 16, fontWeight: FontWeight.w300, color: const Color(0xFF666666), height: 1.4, letterSpacing: .25, ), labelSmall: const TextStyle( fontFamily: LibreFranklin, fontSize: 10, fontWeight: FontWeight.w700, color: Colors.black, ), ); return theme; }这套两层定制是 Flutter 排版主题的标准模式base.apply(...)做全局色替换一次性把所有正文/显示层级换成黑色报纸的黑白调性copyWith(...)做逐槽位精修只重写需要偏离缺省的TextTheme槽位未列出的槽位保持base的值。各槽位的最终排版规格汇总TextTheme 槽位字体族字号字重颜色 / 其他headlineMedium文章标题Merriweather28w800 斜体黑、行高 0.88displayMediumby 等LibreFranklin18w500黑色 alpha 153headlineSmall继承继承w500继承bodyMedium副标题Merriweather14w300#666666、行高 1.11bodyLarge正文Merriweather16w300#666666、行高 1.4、字距 0.25labelSmall栏目标签LibreFranklin10w700黑注意FruitPage中读取的是Theme.of(context).primaryTextTheme——即主题中的主界面文字主题与按钮等控件默认的textTheme区分开。这套做法的意义在于页面内所有Text只引用语义槽位如bodyLarge具体字体/字号/颜色全部集中在主题里改排版只需改一处。6. 字体声明与FontWeight的映射README 的最后一条 Notice 说字体以多个文件导入表达 Bold、Light、Medium、Regular 等字重但代码中以FontWeight值访问例如FontWeight.w800。当前仓库中 pubspec.yaml 的fonts:段L202-L207 声明了Merriweather族- family: Merriweather fonts: - asset: packages/flutter_gallery_assets/fonts/merriweather/Merriweather-BlackItalic.ttf - asset: packages/flutter_gallery_assets/fonts/merriweather/Merriweather-Italic.ttf - asset: packages/flutter_gallery_assets/fonts/merriweather/Merriweather-Regular.ttf - asset: packages/flutter_gallery_assets/fonts/merriweather/Merriweather-Light.ttf同一段还声明了LibreFranklin的 Regular/Light/Medium/Bold 四个文件L196-L201且均未手写weight:——从源码结构看未显式标注weight:的资产由字体文件名约定与引擎的字重匹配机制处理代码侧只需用TextStyle的fontWeight如FontWeight.w800、FontWeight.w300取用最接近的已声明文件。这也解释了为什么_buildTextTheme里bodyLarge用w300就能拿到 Light 质感的正文、headlineMedium用w800得到粗重的标题。7. 如何运行与继续阅读Fortnightly 位于 Flutter 仓库的集成测试目录 dev/integration_tests/flutter_gallery 下它本身是 Gallery 的一个演示页Gallery 同时用作微基准测试的目标应用。查看与运行方式浏览核心文件示例说明dev/integration_tests/flutter_gallery/lib/demo/fortnightly/README.md唯一实现文件dev/integration_tests/flutter_gallery/lib/demo/fortnightly/fortnightly.dart运行方式flutter_gallery的 pubspec.yaml 声明resolution: workspace且sdk: ^3.11.0-0即它是当前 Flutter 仓库工作区的一部分需以本仓库自身作为 Flutter SDK 在该目录下执行flutter run启动 Gallery再在演示列表中选择 Fortnightly (Newspaper typography app) 进入该页。相关资产Logo、头图food/fruits.png、头像people/square/trevor.png均在 pubspec.yaml 的assets:段 中显式列出依赖独立包flutter_gallery_assetspubspec 注释提示该依赖与dev/benchmarks/complex_layout、dev/benchmarks/macrobenchmarks保持同步更新。8. 核心要点回顾Fortnightly 用一个不到 250 行的页面把文字密集 UI的全部关键决策都摆在了明面上主题注入MaterialApp(theme: ...)primaryTextTheme页面只引用语义槽位样式派生apply()改单参数、copyWith()整槽替换、显式TextStyle脱离主题三种写法各有所用布局三板斧Stack叠放浮动短栏、SafeArea处理刘海与底部安全区、SingleChildScrollView保证任意屏可读视觉细节BeveledRectangleBorder圆角浮层、BoxConstraints.expand定高头图、CircleAvatarExactAssetImage精确像素头像字体工程pubspec.yaml声明多字重资产FontWeight值选取字重多行字符串字面量保持长文可读。这套模式可直接迁移到任何以长文、文档、资讯流为核心的 Flutter 应用开发中。【免费下载链接】flutterFlutter makes it easy and fast to build beautiful apps for mobile and beyond项目地址: https://gitcode.com/GitHub_Trending/flutter41/flutter创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表