「这是我参与11月更文挑战的第16天,活动详情查看:2021最后一次更文挑战」。
开发 Web 页面时,经常访问 API 的时候,整个页面会遮罩起来,等 API 处理完再正常显示画面。
那 Flutter 应用的遮罩该怎么整呢?
Flutter 提供了模态屏障组件 ModalBarrier,可以使用该组件来写。
以为自己实现了个什么小功能,结果一搜索,网上已经有很多相关文章了。
代码也折腾了半天,还是发出来吧。
本文相关代码:helloflutter/helloloading at main · bettersun/helloflutter · GitHub
模态屏障组件 ModalBarrier
ModalBarrier有四个参数:
- color: 模态屏障组件的颜色,可改变透明度达到遮罩效果。
- dismissible: 这个参数设置为 true 时,点击会尝试 pop 路由栈。设为 false,点击无反应。
- semanticsLabel: 如果 dismissible 为 true, 这里定义模态屏障的语义标签。
- barrierSemanticsDismissible: 模态屏障的语义是否包含在语义树中。
后面两个具体不太明白是什么意思。
简单要求
- 可控制能否显示
- 加载中,可只转圈(CircularProgressIndicator),也可带有加载中的提示信息。
- 点击时可弹出路由栈,也可设置不弹出,如果一直加载无反应,应用容易就只能一直加载中了。
效果图
实现思路
- 子组件为非加载状态的页面。
- 使用 Stack 将 子组件、遮罩层和转圈(加载中的信息)叠加在一起,因为 Stack 中前面的组件在最底层,后面的组件在最顶层。所以组件的 child 为第一个组件,中间是遮罩组件 ModalBarrier ,最后一个是转圈(或带有提示信息)。
实现代码
loading.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
// 加载遮罩组件
class Loading extends StatelessWidget {
const Loading({
Key? key,
required this.child,
required this.isLoading,
this.message = "",
this.maskOpacity = 0.2,
this.maskColor = Colors.lightBlueAccent,
this.maskDismissible = true,
}) : assert(maskOpacity >= 0.0 && maskOpacity <= 1.0),
super(key: key);
// 子组件(非空)
final Widget child;
// 是否加载中(非空) true: 加载中 false:非加载中
final bool isLoading;
// 遮罩层显示的信息
final String message;
// 遮罩层的透明度(0.0 ~ 1.0) 默认0.2
final double maskOpacity;
// 遮罩层的颜色 默认 Colors.lightBlueAccent
final Color maskColor;
// 点击遮罩层 pop 路由 true: pop(默认), false: 不pop
final bool maskDismissible;
@override
Widget build(BuildContext context) {
return Stack(
children: [
child,
Visibility(
visible: isLoading,
child: ModalBarrier(color: maskColor.withOpacity(maskOpacity), dismissible: maskDismissible),
),
isLoading
? Center(
child: message == ""
? const CircularProgressIndicator()
: Container(
margin: const EdgeInsets.all(16.0),
height: 160,
decoration: BoxDecoration(
color: const Color.fromRGBO(128, 128, 128, 0.2),
borderRadius: BorderRadius.circular(8.0),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const CircularProgressIndicator(),
Center(
child: Container(
margin: const EdgeInsets.all(8.0),
padding: const EdgeInsets.all(8.0),
decoration: BoxDecoration(
color: const Color.fromRGBO(196, 196, 196, 1.0),
borderRadius: BorderRadius.circular(4.0),
),
child: Text(message,
overflow: TextOverflow.ellipsis, style: Theme.of(context).textTheme.bodyText1),
),
),
],
),
),
)
: Container(),
],
);
}
}
具体用法
Loading(
child: Hello(),
isLoading: true,
message: "加载中",
),
正常要表示的画面 Hello() 用 Loading 加载遮罩组件嵌套起来,然后根据标志位等参数来控制遮罩层在子画面的显示。