高温限电,疫情防控,一波未平,一波又起。
学习是不可能学习的,只能在居家摸鱼才能勉强维持生活这样子。
Flutter中有集成的弹窗方法,大致是这样:
void showPopup() {
showModalBottomSheet(
context: context,
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.0)),
builder: (BuildContext context) {
return Container(
color: Colors.amber,
child: Column(
children: [
ElevatedButton(onPressed: () {}, child: Text("1")),
ElevatedButton(onPressed: () {}, child: Text("2")),
],
),
);
});
}
效果大致如下:
就是使用showModalBottomSheet或类似的API,但相对来说可定制的参数较少,比如较为重要的位置是难以控制的,而且也不美观。
当然,不自由的组件很难美观起来。
所以想办法自定义一下。
自定义也简单,看看showModalBottomSheet的源码,依葫芦画瓢改改就行,大多自定义API都可以这么干。
而showModalBottomSheet的源码是这样的:
Future<T?> showModalBottomSheet<T>({
required BuildContext context,
required WidgetBuilder builder,
Color? backgroundColor,
double? elevation,
ShapeBorder? shape,
Clip? clipBehavior,
BoxConstraints? constraints,
Color? barrierColor,
bool isScrollControlled = false,
bool useRootNavigator = false,
bool isDismissible = true,
bool enableDrag = true,
RouteSettings? routeSettings,
AnimationController? transitionAnimationController,
Offset? anchorPoint,
}) {
assert(context != null);
assert(builder != null);
assert(isScrollControlled != null);
assert(useRootNavigator != null);
assert(isDismissible != null);
assert(enableDrag != null);
assert(debugCheckHasMediaQuery(context));
assert(debugCheckHasMaterialLocalizations(context));
final NavigatorState navigator = Navigator.of(context, rootNavigator: useRootNavigator);
return navigator.push(_ModalBottomSheetRoute<T>(
builder: builder,
capturedThemes: InheritedTheme.capture(from: context, to: navigator.context),
isScrollControlled: isScrollControlled,
barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel,
backgroundColor: backgroundColor,
elevation: elevation,
shape: shape,
clipBehavior: clipBehavior,
constraints: constraints,
isDismissible: isDismissible,
modalBarrierColor: barrierColor,
enableDrag: enableDrag,
settings: routeSettings,
transitionAnimationController: transitionAnimationController,
anchorPoint: anchorPoint,
));
}
一下就明白了,弹窗是弹出另一页,自然得另有路由:
navigator.push(_ModalBottomSheetRoute);
接下来抄抄_ModalBottomSheetRoute就行。
class PopupFreeWindow extends PopupRoute {
///子组件
final Widget child;
///切换动画时长,必要属性
final Duration duration;
///间隔,用于微调位置
final EdgeInsets margin;
///分布,用于控制大体位置
final Alignment alignment;
///外围遮罩背景色
Color? outerBackgroudColor;
///子控件具体宽度
double width;
///子控件具体高度
double height;
///宽度比例
double widthFactor;
///高度比例
double heightFactor;
///是否点击外围收起弹窗
bool dismissable;
PopupFreeWindow(
{required this.child,
this.duration = const Duration(milliseconds: 300),
this.alignment = Alignment.bottomCenter,
this.margin =
const EdgeInsets.only(bottom: kBottomNavigationBarHeight * 1.5),
this.widthFactor = 0.95,
this.heightFactor = 0.3,
this.width = 0,
this.height = 0,
this.dismissable = true});
@override
Color? get barrierColor =>
outerBackgroudColor ?? Colors.black.withOpacity(0.3);
@override
bool get barrierDismissible => dismissable;
@override
String? get barrierLabel => null;
@override
Duration get transitionDuration => duration;
@override
Widget buildPage(BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation) {
Widget? content;
if (width > 0 && height >= 0) {
content = Container(
alignment: alignment,
margin: margin,
child: Container(
child: child,
width: width,
height: height,
),
);
} else {
content = FractionallySizedBox(
widthFactor: widthFactor,
heightFactor: heightFactor,
child: Container(
child: child,
margin: margin,
),
alignment: alignment);
}
return FadeTransition(
opacity: animation,
child: SafeArea(
child: content,
));
}
}
测试代码:
void showbottom() {
final size = MediaQuery.of(context).size;
final width = size.width;
final height = size.height;
print("screen w=$width,h=$height");
Navigator.of(context).push(PopupFreeWindow(
// widthFactor: 0.95,
// heightFactor: 0.4,
height: 200,
width: width - 30,
child: ChatBubble(
direction: ArrowDirection.bottom,
arrowWidth: 30,
arrowHeight: 20,
conicWeight: 4.5,
child: GridMenu(),
),
));
}
根据真实的屏幕大小,来定制弹窗的大小,默认的Alignment为底部,可以调整,默认底部的间隔为状态栏的1.5倍,可以调整,这里这么写是为了结合上一篇博客中的ChatBubble,实现底部悬浮菜单的效果。
实际上都可以自行定义。
效果见下图。
以上。
(继续摸鱼去了~)