Flutter功能性组件(2):弹出框

 


一、showModalBottomSheet(模态底部弹出框)

showModalBottomSheet 用于显示一个模态底部弹出框。

属性解析:

Future<T?> showModalBottomSheet<T>({
  required BuildContext context, // 表示底部弹出框所处的上下文,通常来自当前 widget。
  required WidgetBuilder builder, // 用于构建弹出框内容的函数。它传递一个 BuildContext 并返回一个 Widget,这通常是弹出框的主体。
  Color? backgroundColor, // 底部弹出框的背景颜色。
  String? barrierLabel, // 屏障的语义标签,用于无障碍功能。
  double? elevation, // 底部弹出框的阴影高度。
  ShapeBorder? shape, // 底部弹出框的形状,例如圆角矩形。
  Clip? clipBehavior, // 如何裁剪底部弹出框的内容。
  BoxConstraints? constraints, // 底部弹出框的布局约束。
  Color? barrierColor, // 弹出框背景障碍物(屏幕其余部分)的颜色。
  bool isScrollControlled = false, // 是否允许弹出框内部滚动。默认为 false。
  double scrollControlDisabledMaxHeightRatio = _defaultScrollControlDisabledMaxHeightRatio, // 在禁用滚动控制时,最大高度的比例。
  bool useRootNavigator = false, // 是否使用根导航器来推送弹出框。默认为 false。
  bool isDismissible = true, // 指示用户点击屏幕背景(弹出框外部)时是否关闭弹出框。默认为 true。
  bool enableDrag = true, // 是否允许通过拖动来关闭弹出框。默认为 true。
  bool? showDragHandle, // 是否显示拖动手柄。
  bool useSafeArea = false, // 是否考虑安全区域(如异形屏幕的凹槽、状态栏等)。默认为 false。
  RouteSettings? routeSettings, // 传递给弹出框路由的配置信息,如名称和参数。
  AnimationController? transitionAnimationController, // 自定义弹出框的动画控制器。
  Offset? anchorPoint, // 指定弹出框弹出的锚点位置。
})

示例:

// 主页面
class MyHomeBody extends StatelessWidget {
  const MyHomeBody({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Column(
      // 垂直布局
      mainAxisAlignment: MainAxisAlignment.spaceEvenly, // 平均分布各个 Widget
      crossAxisAlignment: CrossAxisAlignment.stretch, // 填充整个交叉轴
      mainAxisSize: MainAxisSize.max,
      children: <Widget>[
        ElevatedButton(
          onPressed: () {
            _showMyModalBottomSheet(context);
          },
          style: ElevatedButton.styleFrom(
            minimumSize: const Size(160, 80),
          ),
          child: Text("AlertDialog(提示对话框)"),
        ),
      ],
    );
  }
}

// showModalBottomSheet(模态底部弹出框)
Future<void> _showMyModalBottomSheet(BuildContext context) async {
  return showModalBottomSheet<void>(
    context: context,
    backgroundColor: Colors.white,
    shape: RoundedRectangleBorder(
      borderRadius: BorderRadius.vertical(top: Radius.circular(25.0)),
    ),
    isScrollControlled: true,
    builder: (BuildContext context) {
      return Padding(
        padding: const EdgeInsets.all(16.0),
        child: SingleChildScrollView(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: <Widget>[
              Text('This is a modal bottom sheet.'),
              SizedBox(height: 20),
              ElevatedButton(
                child: Text('Close'),
                onPressed: () {
                  Navigator.of(context).pop();
                },
              ),
            ],
          ),
        ),
      );
    },
  );
}

效果图如下所示:

Flutter_dialog_G.png


二、Persistent Bottom Sheet(Scaffold的showBottomSheet方法)

这个方法显示一个持久的底部弹出层,不会在点击外部区域时自动关闭。

属性解析:

PersistentBottomSheetController showBottomSheet(
  WidgetBuilder builder, { // 用于构建弹出框内容的函数。它传递一个 BuildContext 并返回一个 Widget,这通常是弹出框的主体。
    Color? backgroundColor, // 底部弹出框的背景颜色
    double? elevation, // 底部弹出框的阴影高度。
    ShapeBorder? shape, // 底部弹出框的形状,例如圆角矩形。
    Clip? clipBehavior, // 如何裁剪底部弹出框的内容。
    BoxConstraints? constraints, // 底部弹出框的布局约束。
    bool? enableDrag, // 是否允许通过拖动来关闭弹出框。
    AnimationController? transitionAnimationController, // 自定义弹出框的动画控制器。
  })

示例:

// 主页面
class MyHomeBody extends StatelessWidget {
  const MyHomeBody({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Column(
      // 垂直布局
      mainAxisAlignment: MainAxisAlignment.spaceEvenly, // 平均分布各个 Widget
      crossAxisAlignment: CrossAxisAlignment.stretch, // 填充整个交叉轴
      mainAxisSize: MainAxisSize.max,
      children: <Widget>[
        ElevatedButton(
          onPressed: () {
            Scaffold.of(context).showBottomSheet(
              (BuildContext context) {
                return Container(
                  color: Colors.white,
                  height: 200,
                  child: Center(
                    child: Column(
                      mainAxisSize: MainAxisSize.min,
                      children: <Widget>[
                        ListTile(
                          leading: Icon(Icons.photo),
                          title: Text('照片'),
                          onTap: () {},
                        ),
                        ListTile(
                          leading: Icon(Icons.music_note),
                          title: Text('音乐'),
                          onTap: () {},
                        ),
                        ListTile(
                          leading: Icon(Icons.videocam),
                          title: Text('视频'),
                          onTap: () {},
                        ),
                      ],
                    ),
                  ),
                );
              },
            );
          },
          child: Text('显示持久底部弹出层'),
        ),
      ],
    );
  }
}

效果图如下所示:

Flutter_dialog_H.png


三、PopupMenuButton(弹出菜单)

PopupMenuButton 是一个 Flutter widget,用于显示弹出菜单,当用户点击按钮时,会显示一组选项。

属性解析:

const PopupMenuButton({
  super.key, // 控件的键值,用于标识控件。
  required this.itemBuilder, // 构建菜单项的函数,返回一个 List<PopupMenuEntry>。
  this.initialValue, // 菜单打开时选中的初始值。
  this.onOpened, // 菜单打开时的回调函数。
  this.onSelected, // 菜单选项被选中后的回调函数。
  this.onCanceled, // 菜单被取消时的回调函数。
  this.tooltip, // 按钮的提示文本。
  this.elevation, // 菜单的阴影高度。
  this.shadowColor, // 菜单阴影的颜色。
  this.surfaceTintColor, // 表面色调的颜色(用于材料设计3)。
  this.padding = const EdgeInsets.all(8.0), // 按钮的内边距。默认值为 EdgeInsets.all(8.0)。
  this.child, // 按钮的子小部件。如果不提供,将使用 icon。
  this.splashRadius, // 按钮点击时水波纹的半径。
  this.icon, // 按钮的图标。如果不提供,将使用 child。
  this.iconSize, // 图标的大小。
  this.offset = Offset.zero, // 菜单相对于按钮的偏移量。默认值为 Offset.zero。
  this.enabled = true, // 按钮是否可用。默认为 true。
  this.shape, // 菜单的外形。
  this.color, // 菜单的背景颜色。
  this.iconColor, // 图标的颜色。
  this.enableFeedback, // 是否启用声音和触觉反馈。
  this.constraints, // 按钮的约束条件。
  this.position, // 菜单的位置。
  this.clipBehavior = Clip.none, // 菜单的剪裁行为。
  this.useRootNavigator = false, // 是否使用根导航器来推送菜单。默认为 false。
  this.popUpAnimationStyle, // 弹出菜单的动画样式。
})

示例:

// ignore_for_file: prefer_const_constructors
import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text("Dialog Page"),
        ),
        body: PopupMenuButtonDemo(),
      ),
    );
  }
}

class PopupMenuButtonDemo extends StatelessWidget {
  const PopupMenuButtonDemo({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return Center(
      child: PopupMenuButton<String>(
        onSelected: (String result) {
          print('Selected: $result');
        },
        itemBuilder: (BuildContext context) => <PopupMenuEntry<String>>[
          const PopupMenuItem<String>(
            value: 'Option 1',
            child: Text('Option 1'),
          ),
          const PopupMenuItem<String>(
            value: 'Option 2',
            child: Text('Option 2'),
          ),
          const PopupMenuItem<String>(
            value: 'Option 3',
            child: Text('Option 3'),
          ),
        ],
        icon: Icon(Icons.more_vert),
        offset: Offset(0, 50), // 设置菜单相对于按钮的偏移量
        elevation: 8.0,
        onCanceled: () {
          print('Menu canceled');
        },
        tooltip: 'Show options',
      ),
    );
  }
}

效果图如下所示:

Flutter_dialog_I.png


四、ExpansionPanelList(展开和折叠的面板)

ExpansionPanelList 是一个 Flutter widget,用于显示一组可以展开和折叠的面板。每个面板在展开状态下可以显示更多内容,非常适合展示分层或分段的信息。

属性解析:

const ExpansionPanelList({
  super.key, // 控件的键值,用于标识控件。
  this.children = const <ExpansionPanel>[], // 要显示的扩展面板列表。每个 ExpansionPanel 可以包含标题和内容。
  this.expansionCallback, // 当某个面板展开或折叠时触发的回调函数。接受两个参数:面板索引和当前的展开状态。
  this.animationDuration = kThemeAnimationDuration, // 展开和折叠动画的持续时间。
  this.expandedHeaderPadding = _kPanelHeaderExpandedDefaultPadding, // 已展开面板标题的内边距。
  this.dividerColor, // 面板之间分隔线的颜色。
  this.elevation = 2, // 面板的阴影高度。
  this.expandIconColor, // 扩展图标的颜色。
  this.materialGapSize = 16.0, // 面板之间的间隙大小。
})

示例:

expansionPanelList.dart

// expansionPanelList.dart
// ignore_for_file: prefer_const_constructors
import 'package:flutter/material.dart';

class ExpansionPanelListDemo extends StatefulWidget {
  const ExpansionPanelListDemo({Key? key}) : super(key: key);
  @override
  _ExpansionPanelListDemoState createState() => _ExpansionPanelListDemoState();
}

class _ExpansionPanelListDemoState extends State<ExpansionPanelListDemo> {
  final List<Item> _data = generateItems(3);

  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      child: Container(
        padding: EdgeInsets.all(10.0),
        child: ExpansionPanelList(
          animationDuration: Duration(milliseconds: 500),
          expansionCallback: (int index, bool isExpanded) {
            setState(() {
              _data[index].isExpanded = !isExpanded;
            });
          },
          children: _data.map<ExpansionPanel>((Item item) {
            return ExpansionPanel(
              headerBuilder: (BuildContext context, bool isExpanded) {
                return ListTile(
                  title: Text(item.headerValue),
                );
              },
              body: ListTile(
                title: Text(item.expandedValue),
                subtitle: Text('To delete this panel, tap the trash icon'),
                trailing: Icon(Icons.delete),
                onTap: () {
                  setState(() {
                    _data.removeWhere((currentItem) => item == currentItem);
                  });
                },
              ),
              isExpanded: item.isExpanded,
            );
          }).toList(),
        ),
      ),
    );
  }
}

class Item {
  Item({
    required this.expandedValue,
    required this.headerValue,
    this.isExpanded = false,
  });

  String expandedValue;
  String headerValue;
  bool isExpanded;
}

List<Item> generateItems(int numberOfItems) {
  return List<Item>.generate(numberOfItems, (int index) {
    return Item(
      headerValue: 'Panel $index',
      expandedValue: 'This is item number $index',
    );
  });
}

main.dart

// ignore_for_file: prefer_const_constructors
import 'package:flutter/material.dart';
import 'widgets/expansionPanelList.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text("Dialog Page"),
        ),
        body: ExpansionPanelListDemo(),
      ),
    );
  }
}

效果图如下所示:

Flutter_dialog_J.png


五、SnackBar

SnackBar 是一个 Flutter 小部件,用于显示短暂的消息,通常在屏幕底部。

属性解析:

const SnackBar({
  super.key, // 用于标识小部件的唯一键。
  required this.content, // SnackBar 的主要内容,通常是一个 Text 小部件。
  this.backgroundColor, // 背景颜色。
  this.elevation, // 阴影高度。
  this.margin, // 外边距。
  this.padding, // 内边距。
  this.width, // 宽度。
  this.shape, // 形状,例如圆角矩形。
  this.hitTestBehavior, // 命中测试行为,决定如何处理点击事件。
  this.behavior, // SnackBar 的行为,可能是固定的或浮动的。
  this.action, // SnackBar 上的操作按钮。
  this.actionOverflowThreshold, // 操作按钮溢出的阈值。
  this.showCloseIcon, // 是否显示关闭图标。
  this.closeIconColor, // 关闭图标的颜色。
  this.duration = _snackBarDisplayDuration, // 显示时长。默认为 _snackBarDisplayDuration。
  this.animation, // 自定义动画。
  this.onVisible, // SnackBar 可见时调用的回调函数。
  this.dismissDirection, // SnackBar 可以被滑动以消失的方向。
  this.clipBehavior = Clip.hardEdge, // 裁剪行为,默认值为 Clip.hardEdge。
})

示例:

class SnackBarDemo extends StatelessWidget {
  const SnackBarDemo({Key? key}) : super(key: key);
  void _showSnackBar(BuildContext context) {
    final snackBar = SnackBar(
      content: Text('This is a Snackbar!'),
      action: SnackBarAction(
        label: 'Undo',
        onPressed: () {
          // Some code to undo the change.
        },
      ),
      backgroundColor: Colors.blue,
      elevation: 10.0,
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(10.0),
      ),
      duration: Duration(seconds: 3),
    );

    ScaffoldMessenger.of(context).showSnackBar(snackBar);
  }

效果图如下所示:

Flutter_dialog_K.png


六、自定义BottomSheet

如果你需要一个完全自定义的 BottomSheet 而不是使用 showModalBottomSheet 或 Scaffold.showBottomSheet,你可以直接创建一个自定义的 BottomSheet 组件:

// 主页面
class MyHomeBody extends StatelessWidget {
  const MyHomeBody({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Column(
      // 垂直布局
      mainAxisAlignment: MainAxisAlignment.spaceEvenly, // 平均分布各个 Widget
      crossAxisAlignment: CrossAxisAlignment.stretch, // 填充整个交叉轴
      mainAxisSize: MainAxisSize.max,
      children: <Widget>[
        ElevatedButton(
          onPressed: () {
            showCustomBottomSheet(context);
          },
          style: ElevatedButton.styleFrom(
            minimumSize: const Size(160, 80),
          ),
          child: Text("AlertDialog(提示对话框)"),
        ),
      ],
    );
  }
}

void showCustomBottomSheet(BuildContext context) {
  showModalBottomSheet(
    context: context,
    builder: (BuildContext context) {
      return Container(
        height: 300,
        padding: EdgeInsets.all(16),
        decoration: BoxDecoration(
          color: Colors.white,
          borderRadius: BorderRadius.only(
            topLeft: Radius.circular(20),
            topRight: Radius.circular(20),
          ),
        ),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            Text(
              '自定义底部弹出层',
              style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
            ),
            SizedBox(height: 16),
            Text('你可以在这里放置任何内容。'),
            SizedBox(height: 16),
            ElevatedButton(
              onPressed: () {
                Navigator.pop(context);
              },
              child: Text('关闭'),
            ),
          ],
        ),
      );
    },
  );
}

效果图如下所示:

Flutter_dialog_L.png


参考:

Flutter 初识:对话框和弹出层_flutter 正在发布 弹出层-CSDN博客


posted @   fengMisaka  阅读(1763)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· 【.NET】调用本地 Deepseek 模型
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
点击右上角即可分享
微信分享提示