WoltModalSheet 旨在革新 Flutter 模态表单的使用。该 UI 组件采用 Wolt 级别的设计质量,并在 Wolt 产品中广泛使用,提供了一个视觉吸引力强且用户友好的模态表单,具有多页面、页面过渡的动画效果以及页面内可滚动内容。
特点
多页面布局
在一个表单内浏览多个页面。

可滚动内容
每页内容的可滚动性更强,可轻松容纳大量内容。

响应式设计
模态表单会根据所有屏幕尺寸进行调整,在较大的屏幕上显示为对话框,在较小的屏幕上显示为底部表单,具体取决于用户指定的条件。

动画效果
通过动态的页面过渡和滚动动画吸引用户。
| 分页 | 滚动 |
|---|---|
![]() |
![]() |
命令式和声明式导航
该库展示了命令式和声明式导航模式的示例,用于在屏幕上显示模态表单。

动态分页
用户输入可以动态地塑造模态表单的页面列表。

状态管理集成
Wolt Modal Sheet 中的页面提供可自定义的外观,页面组件提供 WoltModalSheetPage 类实例。API 提供了一种管理页面组件之间状态的方法,可与 Bloc 和 Provider 等流行库一起使用。
入门
要使用此插件,请在您的 pubspec.yaml 文件中将 wolt_modal_sheet 添加为依赖项。
用法
此软件包有 4 个示例项目。
示例应用程序
example 应用演示了如何显示一个双页模态表单。
class MainApp extends StatelessWidget {
const MainApp({super.key});
@override
Widget build(BuildContext context) {
final pageIndexNotifier = ValueNotifier(0);
WoltModalSheetPage page1(BuildContext modalSheetContext) {
return WoltModalSheetPage.withSingleChild(
stickyActionBar: StickyActionBarWrapper(
child: Column(
children: [
WoltElevatedButton(
onPressed: () => Navigator.of(modalSheetContext).pop(),
theme: WoltElevatedButtonTheme.secondary,
child: const Text('Cancel'),
),
const SizedBox(height: 8),
WoltElevatedButton(
onPressed: () {
pageIndexNotifier.value = pageIndexNotifier.value + 1;
},
child: const Text('Next page'),
),
],
),
),
pageTitle: const ModalSheetTitle('Pagination'),
topBarTitle: const ModalSheetTopBarTitle('Pagination'),
closeButton: WoltModalSheetCloseButton(onClosed: Navigator.of(modalSheetContext).pop),
mainContentPadding: const EdgeInsetsDirectional.all(16),
child: const Padding(
padding: EdgeInsets.only(bottom: 120, top: 16),
child: Padding(
padding: EdgeInsets.only(bottom: 16),
child: Text(
'''
Pagination involves a sequence of screens the user navigates sequentially. We chose a lateral motion for these transitions. When proceeding forward, the next screen emerges from the right; moving backward, the screen reverts to its original position. We felt that sliding the next screen entirely from the right could be overly distracting. As a result, we decided to move and fade in the next page using 30% of the modal side.
''',
),
)),
);
}
WoltModalSheetPage page2(BuildContext modalSheetContext) {
return WoltModalSheetPage.withCustomSliverList(
mainContentPadding: EdgeInsetsDirectional.zero,
stickyActionBar: StickyActionBarWrapper(
padding: EdgeInsets.zero,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: WoltElevatedButton(
onPressed: () {
Navigator.of(modalSheetContext).pop();
pageIndexNotifier.value = 0;
},
child: const Text('Close'),
),
),
),
pageTitle: const Padding(
padding: EdgeInsets.symmetric(horizontal: 16),
child: ModalSheetTitle('Material Colors'),
),
heroImageHeight: 200,
heroImage: const Image(
image: AssetImage('lib/assets/images/material_colors_hero.png'),
fit: BoxFit.cover,
),
topBarTitle: const ModalSheetTopBarTitle('Material Colors'),
backButton: WoltModalSheetBackButton(onBackPressed: () {
pageIndexNotifier.value = pageIndexNotifier.value - 1;
}),
closeButton: WoltModalSheetCloseButton(onClosed: () {
Navigator.of(modalSheetContext).pop();
pageIndexNotifier.value = 0;
}),
sliverList: SliverList(
delegate: SliverChildBuilderDelegate(
(_, index) => ColorTile(color: allMaterialColors[index]),
childCount: allMaterialColors.length,
),
),
);
}
return MaterialApp(
home: Scaffold(
body: Builder(
builder: (context) {
return Center(
child: SizedBox(
width: 200,
child: WoltElevatedButton(
child: const Text('Show Wolt Modal Sheet'),
onPressed: () {
WoltModalSheet.show<void>(
pageIndexNotifier: pageIndexNotifier,
context: context,
pageListBuilderNotifier: (modalSheetContext) {
return [
page1(modalSheetContext),
page2(modalSheetContext),
];
},
modalTypeBuilder: (context) {
final size = MediaQuery.of(context).size.width;
if (size < 768) {
return WoltModalType.bottomSheet;
} else {
return WoltModalType.dialog;
}
},
onModalDismissedWithBarrierTap: () {
debugPrint('Closed modal sheet with barrier tap');
pageIndexNotifier.value = 0;
},
maxDialogWidth: 560,
minDialogWidth: 400,
minPageHeight: 0.4,
maxPageHeight: 0.9,
);
},
),
),
);
},
),
),
);
}
}
上面的代码片段生成如下内容:

具有命令式导航的 Playground 应用
playground 应用演示了如何以命令式的方式显示模态表单。此模块的目的是进行各种用例的尝试和实验。这些用例包括:它演示了
- 一个页面强制最大高度,与其内容无关。
- 一个带有英雄图片的页面
- 一个列表项被惰性构建的页面。
- 一个带有文本框的页面。
具有声明式导航的 Playground 应用
playground_navigator2 应用与 playground 应用内容相同,但模态表单是使用 Navigator 2.0(Router API)以声明式方式显示的。
咖啡机应用用于状态管理示例
最后,coffee_maker 应用演示了如何使用 Provider 状态管理库的特定用法来管理页面组件之间的状态。
代码片段演示了如何使用 Change Notifier Provider 来装饰模态表单,以便可以根据当前状态重建页面组件。
void _onCoffeeOrderSelectedInAddWaterState(BuildContext context, String coffeeOrderId) {
final model = context.read<StoreOnlineViewModel>();
final pageIndexNotifier = ValueNotifier(0);
WoltModalSheet.show(
pageIndexNotifier: pageIndexNotifier,
context: context,
decorator: (child) {
return ChangeNotifierProvider<StoreOnlineViewModel>.value(
value: model,
builder: (_, __) => child,
);
},
pageListBuilderNotifier: AddWaterModalPageBuilder.build(
coffeeOrderId: coffeeOrderId,
goToPreviousPage: () => pageIndexNotifier.value = pageIndexNotifier.value - 1,
goToNextPage: () => pageIndexNotifier.value = pageIndexNotifier.value + 1,
),
modalTypeBuilder: _modalTypeBuilder,
);
}

附加信息
在 这篇博文 中,我们探讨了指导 WoltModalSheet 功能的基础设计决策。

