响应式排版与组件主题
缩放文本并自定义组件主题,确保跨设备品牌样式一致。
响应式排版与组件主题 是 CoddyKit 上的免费 Flutter Mobile Development 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Flutter Mobile Development 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Flutter Mobile Development 课程共包含 4 节课。
本课时的部分内容尚未翻译,以英文显示。
Why Typography Scales
A design system keeps your app's look consistent across phones, foldables, and tablets. Two pillars hold it together: typography (how text looks at every size) and component theming (how buttons, cards, and inputs are styled once and reused everywhere).
In Material 3, both live inside ThemeData. Instead of styling each widget by hand, you define the rules centrally and let widgets inherit them. This lesson shows how to scale text responsively and customize component themes for consistent cross-device branding.
- TextTheme carries the type scale (display, headline, title, body, label).
- Component themes (e.g.
ElevatedButtonThemeData) shape each widget family.
The Material 3 Type Scale
Material 3 defines a type scale of 15 named styles grouped into 5 roles. Each role has Large, Medium, and Small variants:
displayLarge/Medium/Small— big, expressive hero text.headlineLarge/Medium/Small— section headers.titleLarge/Medium/Small— list and dialog titles.bodyLarge/Medium/Small— paragraphs and main content.labelLarge/Medium/Small— buttons, captions, chips.
You read these from context anywhere in your widget tree, so a heading always matches the same scale no matter where it appears.
Widget build(BuildContext context) {
final textTheme = Theme.of(context).textTheme;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Welcome', style: textTheme.displaySmall),
Text('Your daily summary', style: textTheme.titleMedium),
Text('Here is what happened today.', style: textTheme.bodyMedium),
],
);
}Customizing the TextTheme
To brand your typography, override the textTheme on ThemeData. A clean approach is to start from a base text theme and copyWith only the styles you want to change. This keeps the rest of the Material defaults intact.
Notice how each style is a TextStyle with explicit fontSize, fontWeight, and letterSpacing. Defining them in one place guarantees consistency across every screen.
final ThemeData appTheme = ThemeData(
useMaterial3: true,
textTheme: const TextTheme(
displaySmall: TextStyle(
fontSize: 36,
fontWeight: FontWeight.w700,
letterSpacing: -0.5,
),
titleMedium: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
),
bodyMedium: TextStyle(
fontSize: 14,
height: 1.4,
),
),
);Responsive Sizing with MediaQuery
Hard-coded font sizes look great on one device but cramped or oversized on another. Use MediaQuery to read the screen width and pick a scale factor. A common pattern: clamp a base size between a minimum and a maximum so text never gets absurdly small or large.
Here we compute a responsive headline size from the available width, then apply it on top of the theme's style with copyWith.
Widget build(BuildContext context) {
final width = MediaQuery.of(context).size.width;
// Scale headline between 24 and 40 based on width.
final headlineSize = (width / 14).clamp(24.0, 40.0);
final base = Theme.of(context).textTheme.headlineMedium;
return Text(
'Cross-device branding',
style: base?.copyWith(fontSize: headlineSize),
);
}Respecting the User's Text Scale
Accessibility matters: users can enlarge system text. In modern Flutter, read this via MediaQuery.textScalerOf(context), which returns a TextScaler. Avoid disabling it outright; instead clamp it so very large settings don't break your layout while still honoring the user's preference.
Wrap a subtree in MediaQuery with a clamped scaler to bound the growth.
Widget build(BuildContext context) {
final scaler = MediaQuery.textScalerOf(context)
.clamp(minScaleFactor: 1.0, maxScaleFactor: 1.6);
return MediaQuery(
data: MediaQuery.of(context).copyWith(textScaler: scaler),
child: const Text('Readable yet bounded'),
);
}A Pure-Dart Responsive Helper
The scaling logic itself is plain Dart and easy to unit-test in isolation, no Flutter needed. Below is a small function that maps a screen width to a font size with a linear ramp between two breakpoints, then clamps the result. Testing this pure logic separately keeps your widgets thin.
double responsiveFontSize(
double width, {
double minWidth = 320,
double maxWidth = 840,
double minSize = 14,
double maxSize = 22,
}) {
if (width <= minWidth) return minSize;
if (width >= maxWidth) return maxSize;
final t = (width - minWidth) / (maxWidth - minWidth);
return minSize + t * (maxSize - minSize);
}
void main() {
print(responsiveFontSize(300)); // 14.0 (clamped)
print(responsiveFontSize(580)); // ~17.0
print(responsiveFontSize(900)); // 22.0 (clamped)
}Introducing Component Themes
Beyond text, Material 3 lets you theme entire widget families centrally. Each has a dedicated *ThemeData slot on ThemeData:
elevatedButtonTheme,filledButtonTheme,textButtonThemecardTheme,chipTheme,appBarThemeinputDecorationThemefor text fields
Set them once and every matching widget inherits the styling. This is the heart of a design system: change the brand in one file, see it everywhere.
Theming Elevated Buttons
Use ElevatedButtonThemeData with a ButtonStyle to standardize padding, shape, and text style for every ElevatedButton in the app. Tying the button's label style back to labelLarge keeps it aligned with your type scale.
Because the theme supplies these defaults, individual buttons stay clean: ElevatedButton(onPressed: ..., child: Text('Save')) with no inline styling.
final elevatedButtonTheme = ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 14),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
textStyle: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
),
),
);Theming Cards and Inputs
Cards and text fields are everywhere in mobile UIs, so theming them pays off fast. CardThemeData controls elevation, shape, and margin; InputDecorationTheme standardizes borders, fills, and label behavior for every TextField.
With these in place, a form looks consistent across the app without repeating decoration code on each field.
final cardTheme = CardThemeData(
elevation: 1,
margin: const EdgeInsets.all(8),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
);
final inputDecorationTheme = InputDecorationTheme(
filled: true,
contentPadding: const EdgeInsets.all(16),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide.none,
),
);Assembling the Full ThemeData
Now combine typography and component themes into a single ThemeData. Pairing it with a ColorScheme.fromSeed gives you a coherent Material 3 palette. Pass this theme to MaterialApp(theme: ...) and the whole app picks it up.
This one object is your design system's source of truth: brand colors, type scale, and component styling, all together.
ThemeData buildBrandTheme() {
return ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF6750A4)),
textTheme: const TextTheme(
titleMedium: TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
bodyMedium: TextStyle(fontSize: 14, height: 1.4),
),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
),
cardTheme: CardThemeData(
elevation: 1,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
),
);
}Reading the Theme in Widgets
Once the theme is set, widgets should pull styling from it rather than hard-coding values. Use Theme.of(context) to access the textTheme, colorScheme, and any component theme.
This makes your widgets theme-agnostic: switch to a dark theme or rebrand, and they update automatically because they never assumed a specific color or size.
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Account', style: theme.textTheme.titleMedium),
const SizedBox(height: 8),
Text(
'Manage your profile and settings.',
style: theme.textTheme.bodyMedium?.copyWith(
color: theme.colorScheme.onSurfaceVariant,
),
),
],
),
),
);
}Quick Check
You want every TextField in your app to share the same rounded, filled border without repeating decoration code on each field. Which approach fits a Material 3 design system best?
Recap
You built the typography and component foundation of a Material 3 design system:
- Type scale: the 15 named styles (display, headline, title, body, label) read via
Theme.of(context).textTheme. - Custom TextTheme: override fonts and sizes centrally with
copyWith. - Responsive sizing: derive sizes from
MediaQuerywidth andclampthem, and respect (but bound) the user'stextScalerfor accessibility. - Component themes:
ElevatedButtonThemeData,CardThemeData, andInputDecorationThemestyle entire widget families once. - Single source of truth: assemble everything into one
ThemeDatawithColorScheme.fromSeed, then read it in theme-agnostic widgets.
The result is consistent cross-device branding that adapts to screen size and user preferences without per-widget styling.
用 AI 导师学习 Dart — 免费
在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。
- 课程
- 22
- 课程
- 88
常见问题解答
「响应式排版与组件主题」课时是免费的吗?
是的 — 「响应式排版与组件主题」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Flutter Mobile Development 课程的其余内容,请升级到 CoddyKit PRO。 Flutter Mobile Development 课程共包含 4 节课。
「响应式排版与组件主题」这节课中我会学到什么?
缩放文本并自定义组件主题,确保跨设备品牌样式一致。 你通过在浏览器中直接运行的动手代码来练习 Flutter Mobile Development,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Flutter Mobile Development 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Flutter Mobile Development 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。
「响应式排版与组件主题」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Flutter Mobile Development 课中编写并运行代码吗?
能。每节 Flutter Mobile Development 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。