小工具知识网

当前位置: 首页 > 行业知识 > Flutter-底部弹出框(Widget层级)

行业知识

Flutter-底部弹出框(Widget层级)

2026-11-06 19:50:26 来源:互联网转载
需求
  • 支持底部弹出对话框。
  • 支持手势滑动关闭。
  • 支持在widget中嵌入引用。
  • 支持底部弹出框弹出后不影响其他操作。
  • 支持弹出框中内容固定头部和下面列表时,支持触摸头部并在列表不在头部的时候支持滑动关闭
    简述

    通过上面的需求可知,就是在界面中可以支持底部弹出一个弹出框,但是又不影响除了这个弹出框外其他操作,同时支持手势滑动关闭。想到这里我们其实会想到一个控件DraggableScrollableSheet,Flutter提供一种可以支持在widget中引入的底部弹出布局,同时不影响其他的操作,所以我就使用这个控件测试了下,发现一个问题,就是当底部弹出框内容为上面有个头部,底部是个列表时,每次都需要列表滑动到顶部的时候才可以手势滑动关闭,所以需要支持触摸顶部布局也支持手势滑动关闭,所以在此控件上做一个修改。

    效果
    代码如下
    `import 'package:flutter/material.dart';/// 底部弹出Widget/// 1、支持手势下拉关闭/// 2、支持动画弹出收起/// 3、支持弹出无法关闭class DragBottomSheetWidget extends StatefulWidget {  DragBottomSheetWidget({    required Key key,    required this.builder,    this.duration,    this.childHeightRatio = 0.8,    this.onStateChange,  }) : super(key: key);  Function(bool)? onStateChange;  final double childHeightRatio;  final Duration? duration;  final ScrollableWidgetBuilder builder;  @override  State createState() => DragBottomSheetWidgetState();}class DragBottomSheetWidgetState extends State    with TickerProviderStateMixin {  final controller = DraggableScrollableController();  //是否可以关闭  bool isCanClose = true;  //是否展开  bool isExpand = false;  double verticalDistance = 0;  @override  void initState() {    super.initState();  }  @override  void dispose() {    controller.dispose();    super.dispose();  }  Future show({bool isCanClose = true}) {    return controller        .animateTo(1,            duration: widget.duration ?? const Duration(milliseconds: 200),            curve: Curves.linear)        .then((value) {      if (!isCanClose) {        setState(() {          this.isCanClose = isCanClose;        });      }    });  }  void hide() {    controller.animateTo(      0,      duration: widget.duration ?? const Duration(milliseconds: 200),      curve: Curves.linear,    );  }  void _dragJumpTo(double y) {    var size = y / MediaQuery.of(context).size.height;    controller.jumpTo(widget.childHeightRatio - size);  }  void _dragEndChange() {    controller.size >= widget.childHeightRatio / 2        ? controller.animateTo(            1,            duration: widget.duration ?? const Duration(milliseconds: 200),            curve: Curves.linear,          )        : hide();  }  @override  Widget build(BuildContext context) {    return NotificationListener(      onNotification: (notification) {        if (notification.extent == widget.childHeightRatio) {          if (!isExpand) {            isExpand = true;            widget.onStateChange?.call(true);          }        } else if (notification.extent  _DragBottomSheetPageState();}class _DragBottomSheetPageState extends State {  final GlobalKey bottomSheetKey =      GlobalKey();  bool isExpand = false;  @override  Widget build(BuildContext context) {    return Scaffold(      backgroundColor: Colors.deepPurple.withAlpha(50),      appBar: XYAppBar(        title: "底部弹出布局",        backgroundColor: Colors.transparent,        titleColor: Colors.white,        onBack: () {          Navigator.pop(context);        },      ),      body: Stack(        children: [          Positioned(              top: 0,              child: Column(                mainAxisSize: MainAxisSize.max,                children: [                  InkWell(                    onTap: () {                      if (isExpand) {                        bottomSheetKey.currentState?.hide();                      } else {                        bottomSheetKey.currentState?.show();                      }                    },                    child: Container(                      width: MediaQuery.of(context).size.width - 60,                      alignment: Alignment.center,                      height: 50,                      margin: const EdgeInsets.symmetric(horizontal: 30),                      decoration: BoxDecoration(                        borderRadius: BorderRadius.circular(32),                        color: Colors.deepPurple,                      ),                      child: Text(                        isExpand ? "收起" : "弹出",                        style: const TextStyle(                          fontSize: 16,                          fontWeight: FontWeight.w600,                          color: Colors.white,                        ),                      ),                    ),                  ),                  const SizedBox(height: 20),                ],              )),          Positioned(            child: DragBottomSheetWidget(              key: bottomSheetKey,              onStateChange: (isExpand) {                setState(() {                  this.isExpand = isExpand;                });              },              builder:                  (BuildContext context, ScrollController scrollController) {                return Container(                  alignment: Alignment.topLeft,                  child: Stack(                    children: [                      Container(                        height: 50,                        decoration: const BoxDecoration(                          borderRadius: BorderRadius.only(                              topLeft: Radius.circular(16),                              topRight: Radius.circular(16)),                          color: Colors.yellow,                        ),                      ),                      Expanded(                        child: Container(                          margin: const EdgeInsets.only(top: 50),                          child: ListView.builder(                            itemCount: 500,                            controller: scrollController,                            itemBuilder: (context, index) {                              return index % 2 == 0                                  ? Container(                                      height: 100,                                      width: MediaQuery.of(context).size.width,                                      color: Colors.red,                                    )                                  : Container(                                      height: 100,                                      width: MediaQuery.of(context).size.width,                                      color: Colors.blue,                                    );                            },                          ),                        ),                      ),                    ],                  ),                );              },            ),          ),        ],      ),    );  }}

    完整代码见:https://github.com/yixiaolunhui/flutter_xy

上一篇:win8怎么设置输入法?

下一篇:gRPC知识归档