Skip to content

Commit 1f0ff23

Browse files
committed
应用内升级检测逻辑
1 parent d45d1e4 commit 1f0ff23

File tree

15 files changed

+669
-14
lines changed

15 files changed

+669
-14
lines changed

android/app/src/main/AndroidManifest.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
<uses-permission android:name="android.permission.INTERNET"/>
1010
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
1111
<uses-permission android:name="android.permission.WAKE_LOCK" />
12+
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
1213

1314
<application
1415
android:name="io.flutter.app.FlutterApplication"

lib/app/api/app_info.dart

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import 'package:flutter_unit/app/res/path_unit.dart';
2+
import 'package:flutter_unit/app/utils/http_utils/http_util.dart';
3+
import 'package:flutter_unit/app/utils/http_utils/result_bean.dart';
4+
5+
class AppInfoApi {
6+
7+
static Future<ResultBean<AppInfo>> getAppVersion() async {
8+
String errorMsg = "";
9+
var result = await HttpUtil.getInstance()
10+
.client
11+
.get(PathUnit.appInfo)
12+
.catchError((err) {
13+
errorMsg = err.toString();
14+
});
15+
16+
// 获取的数据非空且 status = true
17+
if (result.data != null && result.data['status']) {
18+
// 说明有数据
19+
if (result.data['data'] != null) {
20+
return ResultBean.ok<AppInfo>(
21+
AppInfo(
22+
appName: result.data['data']['appName'],
23+
appVersion: result.data['data']['appVersion'],
24+
appUrl: result.data['data']['appUrl'],
25+
));
26+
} else {
27+
return ResultBean.ok<AppInfo>(null);
28+
}
29+
}
30+
return ResultBean.error('请求错误: $errorMsg');
31+
}
32+
}
33+
34+
class AppInfo{
35+
final String appName;
36+
final String appVersion;
37+
final String appUrl;
38+
39+
AppInfo({this.appName, this.appVersion, this.appUrl});
40+
}

lib/app/res/path_unit.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ class PathUnit{
1212

1313
static const categoryDataSync = '/categoryData/sync';
1414
static const categoryData = '/categoryData';
15+
static const appInfo = '/appInfo/name/FlutterUnit';
1516

1617
static const login = '/login';
1718

lib/app/utils/Toast.dart

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,20 @@ class Toast {
1414
backgroundColor: color??Theme.of(context).primaryColor,
1515
));
1616
}
17+
18+
static void error(BuildContext context,String msg){
19+
toast(context,msg, color:Colors.red, );
20+
}
21+
22+
static void warning(BuildContext context,String msg){
23+
toast(context,msg, color:Colors.orange, );
24+
}
25+
26+
static void success(BuildContext context,String msg){
27+
toast(context,msg, color:Theme.of(context).primaryColor, );
28+
}
29+
30+
static void green(BuildContext context,String msg){
31+
toast(context,msg, color:Colors.green, );
32+
}
1733
}

lib/app/utils/convert.dart

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,18 @@ class Convert {
3434
GalleryType.anim: "动画手势",
3535
GalleryType.art: "艺术画廊",
3636
};
37+
38+
static String convertFileSize(int size){
39+
if(size==null) return '0 kb';
40+
double result = size / 1024.0;
41+
if(result<1024){
42+
return "${result.toStringAsFixed(2)}Kb";
43+
}else if(result>1024&&result<1024*1024){
44+
return "${(result/1024).toStringAsFixed(2)}Mb";
45+
}else{
46+
return "${(result/1024/1024).toStringAsFixed(2)}Gb";
47+
}
48+
}
49+
50+
3751
}
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
import 'dart:math';
2+
3+
import 'package:flutter/material.dart';
4+
import 'rnd.dart';
5+
6+
import 'particle.dart';
7+
8+
9+
final easingDelayDuration = Duration(seconds: 10);
10+
11+
/// Probabilities of Hour, Minute, Noise.
12+
// final particleDistributions = [2, 4, 100];
13+
14+
/// Number of "arms" to emit noise particles from center.
15+
final int noiseAngles = 2000;
16+
17+
/// Threshold for particles to go rouge. Lower = more particles.
18+
final rougeDistributionLmt = 85;
19+
20+
/// Threshold for particles to go jelly. Lower = more particles.
21+
final jellyDistributionLmt = 97;
22+
23+
24+
class ClockFx with ChangeNotifier {
25+
26+
double width; //宽
27+
double height;//高
28+
double sizeMin; // 宽高最小值
29+
Offset center; //画布中心
30+
Rect spawnArea; // 粒子活动区域
31+
32+
List<Particle> particles; // 所有粒子
33+
34+
int numParticles;// 最大粒子数
35+
36+
DateTime time; //时间
37+
38+
ClockFx({
39+
@required Size size,
40+
@required DateTime time,
41+
this.numParticles = 5000,
42+
}) {
43+
this.time = time;
44+
particles = List<Particle>.filled(numParticles, null);
45+
setSize(size);
46+
}
47+
48+
void init() {
49+
for (int i = 0; i < numParticles; i++) {
50+
particles[i] = Particle(color:Colors.black );
51+
resetParticle(i);
52+
}
53+
}
54+
55+
void setTime(DateTime time) {
56+
this.time = time;
57+
}
58+
59+
void setSize(Size size) {
60+
width = size.width;
61+
height = size.height;
62+
sizeMin = min(width, height);
63+
center = Offset(width / 2, height / 2);
64+
spawnArea = Rect.fromLTRB(
65+
center.dx - sizeMin / 100,
66+
center.dy - sizeMin / 100,
67+
center.dx + sizeMin / 100,
68+
center.dy + sizeMin / 100,
69+
);
70+
init();
71+
}
72+
73+
/// Resets a particle's values.
74+
Particle resetParticle(int i) {
75+
Particle p = particles[i];
76+
p.size = p.a = p.vx = p.vy = p.life = p.lifeLeft = 0;
77+
p.x = center.dx;
78+
p.y = center.dy;
79+
return p;
80+
}
81+
82+
void tick(Duration duration) {
83+
updateParticles(duration); // 更新粒子
84+
notifyListeners();// 通知监听者(画板)更新
85+
}
86+
87+
void updateParticles(Duration duration){
88+
var secFrac = DateTime.now().millisecond / 1000;
89+
90+
var vecSpeed = duration.compareTo(easingDelayDuration) > 0
91+
? max(.2, Curves.easeInOutSine.transform(1 - secFrac))
92+
: 1;
93+
94+
var vecSpeedInv = Curves.easeInSine.transform(secFrac);
95+
96+
var maxSpawnPerTick = 10;
97+
98+
particles.asMap().forEach((i, p) {
99+
p.x -= p.vx * vecSpeed;
100+
p.y -= p.vy * vecSpeed;
101+
102+
p.dist = _getDistanceFromCenter(p);
103+
p.distFrac = p.dist / (sizeMin / 2);
104+
105+
p.lifeLeft = p.life - p.distFrac;
106+
107+
p.vx -= p.lifeLeft * p.vx * .001;
108+
p.vy -= p.lifeLeft * p.vy * .001;
109+
110+
if (p.lifeLeft < .3) {
111+
p.size -= p.size * .0015;
112+
}
113+
114+
if (p.distribution > rougeDistributionLmt && p.distribution < jellyDistributionLmt) {
115+
var r = Rnd.getDouble(.2, 2.5) * vecSpeedInv * p.distFrac;
116+
p.x -= p.vx * r + (p.distFrac * Rnd.getDouble(-.4, .4));
117+
p.y -= p.vy * r + (p.distFrac * Rnd.getDouble(-.4, .4));
118+
}
119+
120+
if (p.distribution >= jellyDistributionLmt) {
121+
var r = Rnd.getDouble(.1, .9) * vecSpeedInv * (1 - p.lifeLeft);
122+
p.x += p.vx * r;
123+
p.y += p.vy * r;
124+
}
125+
126+
if (p.lifeLeft <= 0 || p.size <= .5) {
127+
resetParticle(i);
128+
if (maxSpawnPerTick > 0) {
129+
_activateParticle(p);
130+
maxSpawnPerTick--;
131+
}
132+
}
133+
134+
});
135+
}
136+
137+
void _activateParticle(Particle p) {
138+
p.x = Rnd.getDouble(spawnArea.left, spawnArea.right);
139+
p.y = Rnd.getDouble(spawnArea.top, spawnArea.bottom);
140+
p.isFilled = Rnd.getBool();
141+
p.size = Rnd.getDouble(3, 8);
142+
p.distFrac = 0;
143+
p.distribution = Rnd.getInt(1, 2);
144+
145+
double angle = Rnd.ratio * pi * 2;
146+
147+
148+
var am = _getMinuteRadians();
149+
var ah = _getHourRadians() % (pi * 2);
150+
var d = pi / 18;
151+
//
152+
// Probably not the most efficient solution right here.
153+
do {
154+
angle = Rnd.ratio * pi * 2;
155+
} while (_isBetween(angle, am - d, am + d) ||
156+
_isBetween(angle, ah - d, ah + d) );
157+
158+
p.life = Rnd.getDouble(0.75, .8);
159+
160+
p.size = sizeMin *
161+
(Rnd.ratio > .8
162+
? Rnd.getDouble(.0015, .003)
163+
: Rnd.getDouble(.002, .006));
164+
165+
p.vx = sin(-angle);
166+
p.vy = cos(-angle);
167+
168+
p.a = atan2(p.vy, p.vx) + pi;
169+
170+
double v = Rnd.getDouble(.5, 1);
171+
172+
p.vx *= v;
173+
p.vy *= v;
174+
}
175+
176+
double _getDistanceFromCenter(Particle p) {
177+
var a = pow(center.dx - p.x, 2);
178+
var b = pow(center.dy - p.y, 2);
179+
return sqrt(a + b);
180+
}
181+
182+
/// Gets the radians of the hour hand.
183+
double _getHourRadians() =>
184+
(time.hour * pi / 6) +
185+
(time.minute * pi / (6 * 60)) +
186+
(time.second * pi / (360 * 60));
187+
188+
/// Gets the radians of the minute hand.
189+
double _getMinuteRadians() =>
190+
(time.minute * (2 * pi) / 60) + (time.second * pi / (30 * 60));
191+
192+
/// Checks if a value is between two other values.
193+
bool _isBetween(double value, double min, double max) {
194+
return value >= min && value <= max;
195+
}
196+
197+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import 'dart:math';
2+
import 'dart:ui';
3+
4+
import 'package:flutter/foundation.dart';
5+
import 'package:flutter/material.dart';
6+
import 'package:flutter/scheduler.dart';
7+
8+
import 'clock_fx.dart';
9+
10+
/// create by 张风捷特烈 on 2021/2/7
11+
/// contact me by email [email protected]
12+
/// 说明:
13+
14+
class ClockWidget extends StatefulWidget {
15+
final double radius;
16+
17+
const ClockWidget({Key key, this.radius = 100}) : super(key: key);
18+
19+
@override
20+
_ClockWidgetState createState() => _ClockWidgetState();
21+
}
22+
23+
class _ClockWidgetState extends State<ClockWidget>
24+
with SingleTickerProviderStateMixin {
25+
Ticker _ticker;
26+
ClockFx _fx;
27+
28+
@override
29+
void initState() {
30+
super.initState();
31+
_ticker = createTicker(_tick)..start();
32+
_fx = ClockFx(
33+
size: Size(widget.radius * 2, widget.radius * 2),
34+
time: DateTime.now(),
35+
);
36+
}
37+
38+
@override
39+
void dispose() {
40+
_ticker.dispose();
41+
_fx.dispose();
42+
super.dispose();
43+
}
44+
45+
void _tick(Duration duration) {
46+
_fx.tick(duration);
47+
if (_fx.time.second != DateTime.now().second) {
48+
_fx.setTime(DateTime.now());
49+
}
50+
}
51+
52+
@override
53+
Widget build(BuildContext context) {
54+
return CustomPaint(
55+
size: Size(widget.radius * 2, widget.radius * 2),
56+
painter: ClockFxPainter(fx: _fx),
57+
);
58+
}
59+
}
60+
61+
/// Alpha value for noise particles.
62+
const double noiseAlpha = 160;
63+
64+
class ClockFxPainter extends CustomPainter {
65+
final ClockFx fx;
66+
67+
ClockFxPainter({@required this.fx}) : super(repaint: fx);
68+
69+
@override
70+
void paint(Canvas canvas, Size size) {
71+
fx.particles.forEach((p) {
72+
double a;
73+
a = max(0.0, (p.distFrac - .13) / p.distFrac) * 255;
74+
a = min(a, min(noiseAlpha, p.lifeLeft * 3 * 255));
75+
int alpha = a.floor();
76+
77+
Paint circlePaint = Paint()
78+
..style = PaintingStyle.fill
79+
..color = p.color.withAlpha(alpha);
80+
81+
canvas.drawCircle(Offset(p.x, p.y), p.size, circlePaint);
82+
});
83+
}
84+
85+
@override
86+
bool shouldRepaint(covariant ClockFxPainter oldDelegate) =>
87+
oldDelegate.fx != fx;
88+
}

0 commit comments

Comments
 (0)