迁移到 Material 3
摘要
#Material 库已更新以匹配 Material 3 设计规范。更改包括新的组件和组件主题、更新的组件视觉效果等等。许多更新都是无缝的。当您针对 3.16(或更高版本)重新编译应用程序时,您将看到受影响的小部件的新版本。但完成迁移也需要一些手动工作。
迁移指南
#在 3.16 版本之前,您可以通过将useMaterial3
标志设置为 true 来选择加入 Material 3 更改。从 Flutter 3.16 版本(2023 年 11 月)开始,useMaterial3
默认为 true。
顺便说一句,您可以通过将useMaterial3
设置为false
来恢复应用程序中的 Material 2 行为。但是,这只是一个临时的解决方案。useMaterial3
标志和 Material 2 实现最终将作为 Flutter 弃用策略的一部分被删除。
颜色
#ThemeData.colorScheme
的默认值已更新以匹配 Material 3 设计规范。
ColorScheme.fromSeed
构造函数生成一个从给定的seedColor
派生的ColorScheme
。此构造函数生成的色彩旨在协同工作,并满足 Material 3 设计系统中辅助功能的对比度要求。
更新到 3.16 版本后,如果没有正确的ColorScheme
,您的 UI 可能会看起来有点奇怪。要解决此问题,请迁移到从ColorScheme.fromSeed
构造函数生成的ColorScheme
。
迁移前的代码
theme: ThemeData(
colorScheme: ColorScheme.light(primary: Colors.blue),
),
迁移后的代码
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
),
要生成基于内容的动态配色方案,请使用ColorScheme.fromImageProvider
静态方法。有关生成配色方案的示例,请查看从网络图片生成ColorScheme
示例。
Flutter Material 3 的更改包括新的背景颜色。ColorScheme.surfaceTint
指示提升的小部件。一些小部件使用不同的颜色。
要将应用程序的 UI 恢复到其以前的行为(我们不建议这样做)
- 将
Colors.grey[50]!
设置为ColorScheme.background
(当主题为Brightness.light
时)。 - 将
Colors.grey[850]!
设置为ColorScheme.background
(当主题为Brightness.dark
时)。
迁移前的代码
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
),
迁移后的代码
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple).copyWith(
background: Colors.grey[50]!,
),
),
darkTheme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.deepPurple,
brightness: Brightness.dark,
).copyWith(background: Colors.grey[850]!),
),
ColorScheme.surfaceTint
值指示 Material 3 中组件的提升高度。某些小部件可能同时使用surfaceTint
和shadowColor
来指示提升高度(例如,Card
和ElevatedButton
),而其他小部件可能仅使用surfaceTint
来指示提升高度(例如,AppBar
)。
要恢复到小部件的先前行为,请在主题中将Colors.transparent
设置为ColorScheme.surfaceTint
。要区分小部件的阴影和内容(当它没有阴影时),请在没有默认阴影颜色的小部件主题中将ColorScheme.shadow
颜色设置为shadowColor
属性。
迁移前的代码
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
),
迁移后的代码
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple).copyWith(
surfaceTint: Colors.transparent,
),
appBarTheme: AppBarTheme(
elevation: 4.0,
shadowColor: Theme.of(context).colorScheme.shadow,
),
),
ElevatedButton
现在使用新的颜色组合进行样式设置。以前,当useMaterial3
标志设置为 false 时,ElevatedButton
使用ColorScheme.primary
作为背景,使用ColorScheme.onPrimary
作为前景进行样式设置。要实现相同的视觉效果,请切换到新的FilledButton
小部件,而无需提升更改或投影。
迁移前的代码
ElevatedButton(
onPressed: () {},
child: const Text('Button'),
),
迁移后的代码
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).colorScheme.primary,
foregroundColor: Theme.of(context).colorScheme.onPrimary,
),
onPressed: () {},
child: const Text('Button'),
),
排版
#ThemeData.textTheme
的默认值已更新以匹配 Material 3 的默认值。更改包括更新的字体大小、字体粗细、字母间距和行高。有关更多详细信息,请查看TextTheme
文档。
如以下示例所示,在 3.16 版本之前,当具有长字符串的Text
小部件在受约束的布局中使用TextTheme.bodyLarge
时,文本将换行到两行。但是,3.16 版本将文本换行到三行。如果您必须实现以前的行为,请调整文本样式,如有必要,请调整字母间距。
迁移前的代码
ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 200),
child: Text(
'This is a very long text that should wrap to multiple lines.',
style: Theme.of(context).textTheme.bodyLarge,
),
),
迁移后的代码
ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 200),
child: Text(
'This is a very long text that should wrap to multiple lines.',
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
letterSpacing: 0.0,
),
),
),
组件
#某些组件不能仅仅更新以匹配 Material 3 设计规范,而是需要全新的实现。此类组件需要手动迁移,因为 Flutter SDK 不知道您到底想要什么。
用新的NavigationBar
小部件替换 Material 2 样式的BottomNavigationBar
小部件。它略高,包含药丸形导航指示器,并使用新的颜色映射。
迁移前的代码
BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.business),
label: 'Business',
),
BottomNavigationBarItem(
icon: Icon(Icons.school),
label: 'School',
),
],
),
迁移后的代码
NavigationBar(
destinations: const <Widget>[
NavigationDestination(
icon: Icon(Icons.home),
label: 'Home',
),
NavigationDestination(
icon: Icon(Icons.business),
label: 'Business',
),
NavigationDestination(
icon: Icon(Icons.school),
label: 'School',
),
],
),
查看从BottomNavigationBar
迁移到NavigationBar
上的完整示例。
用NavigationDrawer
替换Drawer
小部件,它提供药丸形导航指示器、圆角和新的颜色映射。
迁移前的代码
Drawer(
child: ListView(
children: <Widget>[
DrawerHeader(
child: Text(
'Drawer Header',
style: Theme.of(context).textTheme.titleLarge,
),
),
ListTile(
leading: const Icon(Icons.message),
title: const Text('Messages'),
onTap: () { },
),
ListTile(
leading: const Icon(Icons.account_circle),
title: const Text('Profile'),
onTap: () {},
),
ListTile(
leading: const Icon(Icons.settings),
title: const Text('Settings'),
onTap: () { },
),
],
),
),
迁移后的代码
NavigationDrawer(
children: <Widget>[
DrawerHeader(
child: Text(
'Drawer Header',
style: Theme.of(context).textTheme.titleLarge,
),
),
const NavigationDrawerDestination(
icon: Icon(Icons.message),
label: Text('Messages'),
),
const NavigationDrawerDestination(
icon: Icon(Icons.account_circle),
label: Text('Profile'),
),
const NavigationDrawerDestination(
icon: Icon(Icons.settings),
label: Text('Settings'),
),
],
),
查看从Drawer
迁移到NavigationDrawer
上的完整示例。
Material 3 引入了中型和大型应用栏,它们在滚动之前显示更大的标题。ColorScheme.surfaceTint
颜色用于在滚动时与内容分离,而不是使用投影。
以下代码演示了如何实现中型应用栏
CustomScrollView(
slivers: <Widget>[
const SliverAppBar.medium(
title: Text('Title'),
),
SliverToBoxAdapter(
child: Card(
child: SizedBox(
height: 1200,
child: Padding(
padding: const EdgeInsets.fromLTRB(8, 100, 8, 100),
child: Text(
'Here be scrolling content...',
style: Theme.of(context).textTheme.headlineSmall,
),
),
),
),
),
],
),
现在有两种类型的TabBar
小部件:主要和次要。次要选项卡用于内容区域内,以进一步分离相关内容并建立层次结构。查看TabBar.secondary
示例。
新的TabBar.tabAlignment
属性指定选项卡的水平对齐方式。
以下示例显示了如何在可滚动的TabBar
中修改选项卡对齐方式
AppBar(
title: const Text('Title'),
bottom: const TabBar(
tabAlignment: TabAlignment.start,
isScrollable: true,
tabs: <Widget>[
Tab(
icon: Icon(Icons.cloud_outlined),
),
Tab(
icon: Icon(Icons.beach_access_sharp),
),
Tab(
icon: Icon(Icons.brightness_5_sharp),
),
],
),
),
SegmentedButton
(ToggleButtons
的更新版本)使用完全圆角,布局高度和大小不同,并使用 Dart Set
来确定所选项目。
迁移前的代码
enum Weather { cloudy, rainy, sunny }
ToggleButtons(
isSelected: const [false, true, false],
onPressed: (int newSelection) { },
children: const <Widget>[
Icon(Icons.cloud_outlined),
Icon(Icons.beach_access_sharp),
Icon(Icons.brightness_5_sharp),
],
),
迁移后的代码
enum Weather { cloudy, rainy, sunny }
SegmentedButton<Weather>(
selected: const <Weather>{Weather.rainy},
onSelectionChanged: (Set<Weather> newSelection) { },
segments: const <ButtonSegment<Weather>>[
ButtonSegment(
icon: Icon(Icons.cloud_outlined),
value: Weather.cloudy,
),
ButtonSegment(
icon: Icon(Icons.beach_access_sharp),
value: Weather.rainy,
),
ButtonSegment(
icon: Icon(Icons.brightness_5_sharp),
value: Weather.sunny,
),
],
),
查看从ToggleButtons
迁移到SegmentedButton
上的完整示例。
新组件
#- “菜单栏和级联菜单”提供了一个桌面风格的菜单系统,可以通过鼠标或键盘完全遍历。菜单由
MenuBar
或MenuAnchor
锚定。新的菜单系统不是现有应用程序必须迁移到的内容,但是部署在 Web 或桌面平台上的应用程序应该考虑使用它而不是PopupMenuButton
(和相关)类。 DropdownMenu
结合了文本字段和菜单,生成有时称为组合框的内容。用户可以通过输入匹配的字符串或通过使用触摸、鼠标或键盘与菜单交互来从可能很长的列表中选择菜单项。这可以很好地替代DropdownButton
小部件,尽管这不是必需的。SearchBar
和SearchAnchor
用于用户输入搜索查询、应用程序计算匹配响应列表,然后用户选择一个或调整查询的交互。Badge
用一个只有几个字符的小标签装饰其子元素。例如 '+1'。徽章通常用于装饰NavigationDestination
、NavigationRailDestination
、NavigationDrawerDestination
或按钮图标(如TextButton.icon
)中的图标。FilledButton
和FilledButton.tonal
与ElevatedButton
非常相似,但没有提升更改和投影。FilterChip.elevated
、ChoiceChip.elevated
和ActionChip.elevated
是相同芯片的提升变体,具有投影和填充颜色。Dialog.fullscreen
填充整个屏幕,通常包含标题、操作按钮和顶部的关闭按钮。
时间线
#稳定版本:3.16
参考
#文档
API 文档
相关问题
相关 PR
除非另有说明,否则本网站上的文档反映了 Flutter 的最新稳定版本。页面最后更新于 2024-04-06。 查看源代码 或 报告问题.