如高德地图首页,底层是地图,上层是一个可拖拽的抽屉页面,想要实现这种效果,可以使用DraggableScrollableSheet
组件来实现。
使用时有以下几点需要注意:
-
一般是配合
Stack
来实现,这里有个小坑,直觉上我们可能使用Positioned
为上层Sheet
定位,但是会报错,需要使用Align
来布局。 -
snap
设为true
,再配合snapSizes
可以实现分段效果,就是拖拽结束后会回弹到指定的高度。 -
有些复杂的业务场景还需要外部代码控制
sheet
高度,在初始化时传入DraggableScrollableController
,通过controller
来控制,controller
设置后的值是不会触发snap
的回弹效果的。
最后放上一个简易封装以供参考:
import 'package:flutter/material.dart';
class DraggableSheetPage extends StatelessWidget {
const DraggableSheetPage({
super.key,
this.controller,
this.initialChildSize,
this.minChildSize,
this.maxChildSize,
this.snap,
this.snapSizes,
required this.childrenBuilder,
required this.sheetBuilder,
});
final DraggableScrollableController? controller;
final List<Widget> Function(BuildContext context) childrenBuilder;
final Widget Function(
BuildContext context,
ScrollController scrollController,
) sheetBuilder;
/// 默认值是 `0.5`.
final double? initialChildSize;
/// 默认值是 `0.1`.
final double? minChildSize;
/// 默认值是 `0.8`.
final double? maxChildSize;
/// true 表示开启分段, 拖拽结束后会会回弹到固定位置
final bool? snap;
/// 划分具体的回弹位置,大小必须在[minChildSize]和[maxChildSize]之间
final List<double>? snapSizes;
@override
Widget build(BuildContext context) {
return Stack(
children: [
...childrenBuilder(context),
// 底部可拖动面板
Align(
alignment: Alignment.bottomCenter,
child: DraggableScrollableSheet(
initialChildSize: initialChildSize ?? 0.5,
minChildSize: minChildSize ?? 0.1,
maxChildSize: maxChildSize ?? 0.8,
snap: snap ?? true,
snapSizes: snapSizes ?? [0.5],
builder: (BuildContext context, ScrollController scrollController) {
return Card(
elevation: 0.1,
margin: const EdgeInsets.all(0),
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(16),
topRight: Radius.circular(16),
),
),
child: sheetBuilder(context, scrollController),
);
},
),
),
],
);
}
}