您的位置:

Flutter 弹框全解析

一、基础概念

Flutter中的弹框分为两种类型:Dialog和BottomSheet。Dialog一般指具有模态的弹框,使用showDialog函数实现;而BottomSheet一般指非模态的弹框,使用showModalBottomSheet实现。除此之外,还有一些其他类型的弹框,如底部菜单弹框,选择器弹框等,这里就不一一列举了。

Flutter的弹框构造器通常包括以下几个部分:弹框主体Widget、背景遮罩Widget、动画控制器、弹框位置等等。接下来我们会重点讲述这些部分的实现过程。

二、Dialog的实现

Dialog是模态的弹框类型,即弹框出现时,背景被遮挡且无法操作。

1、弹框主体Widget

在Flutter中,Dialog通常由一个AlertDialog Widget或SimpleDialog Widget构成。其中AlertDialog可以自定义其标题、内容和按钮;而SimpleDialog只包含多个选项。

AlertDialog的构建过程类似下面的代码:

showDialog(
  context: context,
  builder: (BuildContext context) {
    return AlertDialog(
      title: Text("弹框标题"),
      content: Text("弹框内容"),
      actions: [
        FlatButton(
          child: Text("取消"),
          onPressed: () {
            Navigator.of(context).pop();  // 关闭弹框
          },
        ),
        FlatButton(
          child: Text("确定"),
          onPressed: () {
            // 实现确认逻辑
          },
        ),
      ],
    );
  },
);

  

2、背景遮罩Widget

这里我们使用了showDialog函数来构建Dialog,同时此函数还允许我们自定义弹框显示时的遮罩背景。这可以通过指定barrierColor、barrierOpacity和barrierDismissible等属性来实现。其中,barrierColor和barrierOpacity用于设置遮罩的颜色和透明度,而barrierDismissible则用于控制是否允许用户点击遮罩背景来关闭弹框(即点击空白处关闭弹框)。

showDialog(
  context: context,
  barrierColor: Colors.black12,  // 遮罩颜色
  barrierOpacity: 0.5,  // 遮罩透明度
  barrierDismissible: false,  // 是否允许点击空白处关闭弹框
  builder: (BuildContext context) {
    return AlertDialog(
      //...
    );
  },
);

3、动画控制器

想要Dialog具有更好的动画效果,我们可以使用Flutter内置的动画库Animations。通过AnimatedWidget或AnimatedBuilder等顶层Widget,我们可以方便地构建各种动画效果。这里以FadeTransition为例,演示如何实现Dialog的淡入淡出效果。

class MyDialog extends StatefulWidget {
  const MyDialog({Key key}) : super(key: key);

  @override
  _MyDialogState createState() => _MyDialogState();
}

class _MyDialogState extends State with SingleTickerProviderStateMixin {
  AnimationController _controller;
  Animation
    _animation;

  @override
  void initState() {
    super.initState();

    _controller = AnimationController(
      duration: const Duration(milliseconds: 500),
      vsync: this,
    );
    _animation = CurvedAnimation(parent: _controller, curve: Curves.easeOut);

    _controller.forward();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return FadeTransition(
      opacity: _animation,
      child: Center(
        child: AlertDialog(
          title: Text("弹框标题"),
          content: Text("弹框内容"),
          actions: 
    [
            FlatButton(
              child: Text("取消"),
              onPressed: () {
                Navigator.of(context).pop();
              },
            ),
            FlatButton(
              child: Text("确定"),
              onPressed: () {
                //...
              },
            ),
          ],
        ),
      ),
    );
  }
}

void showMyDialog(BuildContext context) {
  showDialog(
    context: context,
    builder: (BuildContext context) {
      return MyDialog();
    },
  );
}

    
   
  

三、BottomSheet的实现

BottomSheet是非模态的弹框类型,即弹框出现时,背景不被遮挡且可以进行操作。BottomSheet有两种类型:PersistentBottomSheet和ModalBottomSheet。其中,PersistentBottomSheet会一直保持显示直至手动关闭,而ModalBottomSheet会在点击某个按钮后弹出,在外部区域进行操作时自动隐藏。

1、弹框主体Widget

在Flutter中,BottomSheet的构建器是builder属性,它是个函数类型,返回一个Widget。另外我们也可以使用官方提供的BottomSheet Widget。与Dialog不同,BottomSheet不包含标题和按钮等元素,通常只包含一部分滚动内容。

ModalBottomSheet(
  builder: (BuildContext context) {
    return Container(
      height: 200.0,
      child: ListView(
        children: [
          ListTile(title: Text('选项1')),
          ListTile(title: Text('选项2')),
          ListTile(title: Text('选项3')),
          ListTile(title: Text('选项4')),
        ],
      ),
    );
  },
);

  

2、背景遮罩Widget

与Dialog类似,BottomSheet也有相应的遮罩背景设置属性。但由于其本身不是模态的,所以不需要只读化背景,也就意味着无需设置barrierDismissible属性。

showModalBottomSheet(
  context: context,
  backgroundColor: Colors.black12,  // 设置背景色
  builder: (BuildContext context) {
    return Container(
      height: 200.0,
      child: ListView(
        children: [
          //...
        ],
      ),
    );
  },
);

  

3、动画控制器

维护动画控制器实现BottomSheet的进入和退出动画。

class MyBottomSheet extends StatefulWidget {
  const MyBottomSheet({Key key}) : super(key: key);

  @override
  _MyBottomSheetState createState() => _MyBottomSheetState();
}

class _MyBottomSheetState extends State with SingleTickerProviderStateMixin {
  AnimationController _controller;
  Animation
    _animation;

  @override
  void initState() {
    super.initState();

    _controller = AnimationController(
      duration: const Duration(milliseconds: 500),
      vsync: this,
    );
    _animation = CurvedAnimation(parent: _controller, curve: Curves.easeOut);

    _controller.forward();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return SlideTransition(
      position: Tween
    (
        begin: Offset(0.0, 1.0),
        end: Offset(0.0, 0.0),
      ).animate(_controller),
      child: Container(
        height: 200.0,
        child: ListView(
          children: 
     [
            //...
          ],
        ),
      ),
    );
  }
}

void showMyBottomSheet(BuildContext context) {
  showModalBottomSheet(
    context: context,
    builder: (BuildContext context) {
      return MyBottomSheet();
    },
  );
}

     
    
   
  

四、结语

以上就是Flutter弹框的实现方案。除了这两种类型外,还有很多其他类型的弹框类型,每种类型都有自己的实现方式。我们需要具体问题具体分析,灵活运用。在实践中不断探索,准确地找到需要用到的弹框类型,并实现对应的构造器。