Flutter的Overlay,你飘了。全局提示,悬浮按钮,弹出菜单

3,531 阅读3分钟

在Flutter开发中,我们经常会遇到需要在当前页面上显示一些额外内容的需求,例如弹窗、提示框、浮动按钮等。这时候,我们可以使用Overlay这个强大的工具来实现这些效果。

一、什么是Overlay

Overlay是Flutter中一个用于在应用程序的widget树顶层绘制浮动元素的工具。它允许我们在不改变widget树结构的情况下,添加悬浮在其他widget之上的内容。这对于实现全局提示、悬浮菜单等功能非常有用。

二、Overlay的基本使用

要使用Overlay,我们需要通过OverlayEntry来定义每一个悬浮的widget,并将它们插入到Overlay中。

以下是一个简单的示例,展示了如何使用Overlay来显示一个悬浮的提示框:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Overlay 示例'),
        ),
        body: OverlayExample(),
      ),
    );
  }
}

class OverlayExample extends StatefulWidget {
  @override
  _OverlayExampleState createState() => _OverlayExampleState();
}

class _OverlayExampleState extends State<OverlayExample> {
  OverlayEntry? _overlayEntry;

  @override
  Widget build(BuildContext context) {
    return Center(
      child: ElevatedButton(
        onPressed: () {
          _showOverlay(context);
        },
        child: Text('显示Overlay'),
      ),
    );
  }

  void _showOverlay(BuildContext context) {
    // 创建OverlayEntry
    _overlayEntry = OverlayEntry(
      builder: (context) => Positioned(
        top: 100,
        left: 50,
        child: Material(
          color: Colors.transparent,
          child: Container(
            color: Colors.black54,
            width: 200,
            height: 100,
            child: Center(
              child: Text(
                '这是一个Overlay',
                style: TextStyle(color: Colors.white, fontSize: 20),
              ),
            ),
          ),
        ),
      ),
    );

    // 插入OverlayEntry到Overlay中
    Overlay.of(context)?.insert(_overlayEntry!);

    // 3秒后移除OverlayEntry
    Future.delayed(Duration(seconds: 3), () {
      _overlayEntry?.remove();
      _overlayEntry = null;
    });
  }
}

在上述代码中,我们通过创建一个OverlayEntry并使用Overlay.of(context)?.insert(_overlayEntry!)将其插入到Overlay中。3秒后,我们移除了这个OverlayEntry。

三、Overlay的适用场景

  1. 全局提示:例如,用户登录成功后显示的全局提示信息。
  2. 悬浮按钮:如浮动的操作按钮,可以在任何页面上使用。
  3. 弹出菜单:点击某个按钮后显示的弹出菜单,可以悬浮在其他内容之上。

四、使用Overlay模拟推送消息显示效果

我们再来一个模拟,使用Overlay来模拟一个推送消息显示的效果。

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Overlay 推送消息示例'),
        ),
        body: PushNotificationExample(),
      ),
    );
  }
}

class PushNotificationExample extends StatefulWidget {
  @override
  _PushNotificationExampleState createState() => _PushNotificationExampleState();
}

class _PushNotificationExampleState extends State<PushNotificationExample> {
  OverlayEntry? _overlayEntry;

  @override
  Widget build(BuildContext context) {
    return Center(
      child: ElevatedButton(
        onPressed: () {
          _showNotification(context);
        },
        child: Text('显示推送消息'),
      ),
    );
  }

  void _showNotification(BuildContext context) {
    _overlayEntry = _createOverlayEntry();

    Overlay.of(context)?.insert(_overlayEntry!);

    // 5秒后移除推送消息
    Future.delayed(Duration(seconds: 5), () {
      _overlayEntry?.remove();
      _overlayEntry = null;
    });
  }

  OverlayEntry _createOverlayEntry() {
    return OverlayEntry(
      builder: (context) => Positioned(
        top: 50.0,
        right: 10.0,
        child: Material(
          color: Colors.transparent,
          child: Container(
            width: MediaQuery.of(context).size.width - 20,
            padding: EdgeInsets.symmetric(horizontal: 10, vertical: 5),
            decoration: BoxDecoration(
              color: Colors.blue,
              borderRadius: BorderRadius.circular(8),
              boxShadow: [
                BoxShadow(
                  color: Colors.black26,
                  blurRadius: 10,
                  offset: Offset(0, 4),
                ),
              ],
            ),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Expanded(
                  child: Text(
                    '这是一条推送消息!',
                    style: TextStyle(color: Colors.white),
                  ),
                ),
                IconButton(
                  icon: Icon(Icons.close, color: Colors.white),
                  onPressed: () {
                    _overlayEntry?.remove();
                    _overlayEntry = null;
                  },
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

在这个例子中,我们通过_createOverlayEntry方法创建了一个OverlayEntry,并通过Overlay.of(context)?.insert(_overlayEntry!)将其插入到Overlay中。推送消息会在5秒后自动消失,同时也可以通过点击关闭按钮手动关闭。

五、优缺点总结

优点

  • 灵活性高,可以在任何位置显示小部件。
  • 动态添加和移除 OverlayEntry,提高了界面的灵活性。
  • 控制层叠顺序,实现复杂的 UI 效果。

缺点

  • 需要手动管理 OverlayEntry,增加了代码复杂度。
  • 不当的管理可能导致内存泄漏。

本文,那就这样。