Flutter Overlay 的使用 —— 自定义 Loading、Toast
背景
背景:项目中的 Toast 一直试用的是这个包 another_flushbar ,其 Likes 数量还挺多的,但是使用过程中遇到了一些问题,因为这个 Toast 的实现是利用类似 PageRouteBuilder 这样新打开一个路由页面的形式,但是页面中有些组件例如 showDialog、shouBottomSheet 也是使用这种方式,这样就产生了一个问题就是当 Toast 和 dialog 一起使用时当 Toast 出现 同时和 Dialog 消失时,路由就会产生冲突,就比较麻烦。
解决方案:google 了一下发现实现 Toast 更好的方法是使用 Overlay
来自 chatgpt 的回答
在 Flutter 中,Overlay
是一个用于在应用程序中创建和管理覆盖层的组件。它允许你在应用程序的界面上添加或移除覆盖物,并控制它们的显示和位置。Overlay
组件本质上是一个Stack
,它允许你在一个层次结构中放置多个覆盖物,并根据需要调整它们的顺序和位置。每个覆盖物都由OverlayEntry
来表示,它定义了要在Overlay
中显示的具体内容。
使用Overlay
和OverlayEntry
,你可以在应用程序的任何地方创建自定义的覆盖物,并将它们放置在其他组件之上。这对于创建弹出窗口、浮动按钮、通知栏等交互性或悬浮性的界面元素非常有用。
要在应用程序中使用Overlay
,你可以使用Overlay.of(context)
方法获取当前上下文中的Overlay
。然后,你可以使用insert
方法将OverlayEntry
添加到Overlay
中,并使用remove
方法将其从Overlay
中移除。
flutter_easyloading
在 pub 中找了一下,发现了一个 flutter_easyloading 库,Likes 数量很高,同时支持 loading 和 toast,底层也是使用了 Overlay 实现,但在把它引入项目中发现了一些问题:
- 因为这个库是全局单例,虽然它提供了 loading,toast ,但它的一些自定义配置只能针对一个实例也就说无法对 loading 和 toast 分别配置颜色什么的 https://github.com/nslogx/flutter_easyloading/issues/188 ,而且该项目未解决的 issue 数量特别多不知道是不是开发者已经放弃了
- 我发现特别多的 flutter 第三方库,都是会给一些配置选项让开发者自定义样式,但这些配置往往很难满足使用者的需求,其实像 loading 和 toast 这种,第三方库只需提供核心动画,真正要展现的内容让使用者自己传进去就好了,这样的话也就不需要那么多的配置参数了,使用者也能实现自己的需求(flutter 官方的 widget 也是提供了很多配置参数,调试起来很费劲,再次吐槽一下为什么不能只把核心逻辑实现,具体展现交由用户传进去的 widget 来控制)
自己利用 Overlay 实现 Toast 、Loading
演示效果:
具体代码思路:
在 MaterialApp 设置 Overlay 容器
runApp(GetMaterialApp( ... builder: (context, child) => Scaffold( // 将整个根页面都放进 overlay 里面,其他的像 toast 等组件后续会动态的插入这个 Overlay 的 Stack 里面 body: Overlay( initialEntries: [OverlayEntry(builder: (context) => child)], ) ), );
以 Toast 为例:
class Toast { // toast 展现时间 static int duration = 2500; // overlay entry static OverlayEntry? _overlayEntry; // overlay 存在时间计时器 static Timer? _timer; // 对外暴露 success 和 error 方法 static void success(String? message) { _show('success', message ?? '', context: Get.context!); } static void error(String? message) { _show('error', message ?? '', context: Get.context!); } // toast 核心逻辑 static void _show( String type, String message, { required BuildContext context, }) { // 每次调用前先把上次的销毁,防止频繁调用 _timer?.cancel(); _overlayEntry?.remove(); // 创建 overlay entry _overlayEntry = OverlayEntry( // toast 需要展现的内容,具体动画实现见文末 // CustomerToastBody 就是 toast 具体要展现的样式,完全交给使用者自己定义 builder: (ctx) => CutomerAnimateWidget(child: CustomerToastBody()), ); // 将该自定义的 overlay entry 插入到顶层 overlayentry stack 中 Overlay.of(context).insert(_overlayEntry!); // 一定时间后自动关闭 overlay entry _timer = Timer(Duration(milliseconds: duration), () { _overlayEntry?.remove(); _overlayEntry = null; }); } }
- 抛开动画不谈上面的代码就完整的实现了一个 2.5s 自动消失的 Toast
- 含动画的完整实现见下方链接
- Toast https://gist.github.com/hugeorange/adb11487d8803cbd6aa3338cc399204c
- Loading https://gist.github.com/hugeorange/828f54e13e5c954d83a6ac51730e3e53
Flutter Overlay 的使用 —— 自定义 Loading、ToastFlutter Overlay 的使用 —— 自定义 Loading、ToastFlutter Overlay 的使用 —— 自定义 Loading、ToastFlutter Overlay 的使用 —— 自定义 Loading、ToastFlutter Overlay 的使用 —— 自定义 Loading、ToastFlutter Overlay 的使用 —— 自定义 Loading、ToastFlutter Overlay 的使用 —— 自定义 Loading、ToastFlutter Overlay 的使用 —— 自定义 Loading、ToastFlutter Overlay 的使用 —— 自定义 Loading、Toast