用慣了iOS的SVProgressHUD,但是在flutter pub上的并沒有找到類似的實(shí)現(xiàn),于是自己實(shí)現(xiàn)一個(gè)
主要實(shí)現(xiàn)四個(gè)基本功能
- Loading顯示
- 成功顯示
- 錯(cuò)誤顯示
- 進(jìn)度顯示:環(huán)形進(jìn)度條和文字
庫地址
https://pub.dartlang.org/packages/bmprogresshud
dependencies:
bmprogresshud: ^0.0.2
實(shí)現(xiàn)效果
- 由于HUD是蓋在視圖上面的,通常是整個(gè)頁面,故考慮直接在目標(biāo)Widget上套一層
ProgressHUD
- 我們需要在特定的地方獲取
ProgressHUD
進(jìn)行操作,這個(gè)有點(diǎn)類似Navigator
瓢宦,參考Navigator的用法,通過of
方法獲得
實(shí)際效果如下
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("hud demo"),
),
body: ProgressHud(
child: Container(
child: Builder(builder: (context) {
return RaisedButton(
onPressed: () async {
ProgressHud.of(context).show(ProgressHudType.loading, "加載中...");
await Future.delayed(const Duration(seconds: 1));
ProgressHud.of(context).dismiss();
},
child: Text("加載數(shù)據(jù)"),
);
}),
),
)
);
}
實(shí)現(xiàn)效果
1. 顯示和隱藏漸變
通過屬性opacity
和AnimationController
控制透明度灰羽,當(dāng)透明度為0時(shí)候驮履,通過Offstage
控制控件的隱藏
class ProgressHudState extends State<ProgressHud> with SingleTickerProviderStateMixin {
AnimationController _animation;
var _opacity = 0.0;
var _isVisible = false;
@override
void initState() {
_animation = AnimationController(
duration: const Duration(milliseconds: 200),
vsync: this
)..addListener(() {
setState(() {
// 修改透明度
_opacity = _animation.value;
});
})..addStatusListener((status) {
if (status == AnimationStatus.dismissed) {
setState(() {
// 隱藏動(dòng)畫結(jié)束鱼辙,隱藏控件
_isVisible = false;
});
}
});
super.initState();
}
...
}
我們通過動(dòng)畫的執(zhí)行方向控制動(dòng)畫
// 顯示動(dòng)畫
_animation.forward();
setState(() {
_isVisible = true;
});
// 隱藏動(dòng)畫
_animation.reverse();
2. 通過BuildContext
獲得Element樹的ProgressHUD
class ProgressHud extends StatefulWidget {
static ProgressHudState of(BuildContext context) {
return context.ancestorStateOfType(const TypeMatcher<ProgressHudState>());
}
...
}
3. 創(chuàng)建HUD
Widget _createHudView(Widget child) {
return Stack(
children: <Widget>[
// 如果不想屏蔽用戶操作,ignoring設(shè)置為true玫镐,這里設(shè)置為無法響應(yīng)
IgnorePointer(
ignoring: false,
child: Container(
color: Colors.transparent,
width: double.infinity,
height: double.infinity,
),
),
Center(
child: Container(
// 這里設(shè)置一定的偏移倒戏,因?yàn)閕PhoneX有下方安全區(qū)域,看起來會(huì)偏下
margin: EdgeInsets.fromLTRB(10, 10, 10, 10 - widget.offsetY * 2),
decoration: BoxDecoration(
color: Color.fromARGB(255, 33, 33, 33),
borderRadius: BorderRadius.circular(5)
),
// 設(shè)置最小寬高恐似,如果文字比較多杜跷,可以自適應(yīng)
constraints: BoxConstraints(
minHeight: 130,
minWidth: 130
),
child: Padding(
padding: EdgeInsets.all(12),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(
padding: EdgeInsets.all(15),
child: child,
),
Container(
child: Text(
_text,
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white, fontSize: 16)
),
)
],
),
),
),
),
],
);
}
4. 環(huán)形進(jìn)度
通過Painter畫兩個(gè)圓
import 'dart:math';
import 'package:flutter/material.dart';
class CircleProgressBarPainter extends CustomPainter {
final double progress;
final double strokeWidth;
final Color color;
final Color fillColor;
const CircleProgressBarPainter({
this.progress = 0,
this.strokeWidth = 3,
this.color = Colors.grey,
this.fillColor = Colors.white
});
@override
void paint(Canvas canvas, Size size) {
final paint = new Paint()
..color = this.color
..style = PaintingStyle.stroke
..strokeWidth = strokeWidth;
final double diam = min(size.width, size.height);
final centerX = size.width * 0.5;
final centerY = size.height * 0.5;
final radius = diam / 2.0;
canvas.drawCircle(Offset(centerX, centerY), radius, paint);
paint.color = this.fillColor;
// draw in center
var rect = Rect.fromLTWH((size.width - diam) * 0.5, 0, diam, diam);
canvas.drawArc(rect, -0.5 * pi, progress * 2 * pi, false, paint);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) => false;
}
完整代碼見這里: