forked from toly1994328/FlutterUnit
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
d45d1e4
commit 1f0ff23
Showing
15 changed files
with
669 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import 'package:flutter_unit/app/res/path_unit.dart'; | ||
import 'package:flutter_unit/app/utils/http_utils/http_util.dart'; | ||
import 'package:flutter_unit/app/utils/http_utils/result_bean.dart'; | ||
|
||
class AppInfoApi { | ||
|
||
static Future<ResultBean<AppInfo>> getAppVersion() async { | ||
String errorMsg = ""; | ||
var result = await HttpUtil.getInstance() | ||
.client | ||
.get(PathUnit.appInfo) | ||
.catchError((err) { | ||
errorMsg = err.toString(); | ||
}); | ||
|
||
// 获取的数据非空且 status = true | ||
if (result.data != null && result.data['status']) { | ||
// 说明有数据 | ||
if (result.data['data'] != null) { | ||
return ResultBean.ok<AppInfo>( | ||
AppInfo( | ||
appName: result.data['data']['appName'], | ||
appVersion: result.data['data']['appVersion'], | ||
appUrl: result.data['data']['appUrl'], | ||
)); | ||
} else { | ||
return ResultBean.ok<AppInfo>(null); | ||
} | ||
} | ||
return ResultBean.error('请求错误: $errorMsg'); | ||
} | ||
} | ||
|
||
class AppInfo{ | ||
final String appName; | ||
final String appVersion; | ||
final String appUrl; | ||
|
||
AppInfo({this.appName, this.appVersion, this.appUrl}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,197 @@ | ||
import 'dart:math'; | ||
|
||
import 'package:flutter/material.dart'; | ||
import 'rnd.dart'; | ||
|
||
import 'particle.dart'; | ||
|
||
|
||
final easingDelayDuration = Duration(seconds: 10); | ||
|
||
/// Probabilities of Hour, Minute, Noise. | ||
// final particleDistributions = [2, 4, 100]; | ||
|
||
/// Number of "arms" to emit noise particles from center. | ||
final int noiseAngles = 2000; | ||
|
||
/// Threshold for particles to go rouge. Lower = more particles. | ||
final rougeDistributionLmt = 85; | ||
|
||
/// Threshold for particles to go jelly. Lower = more particles. | ||
final jellyDistributionLmt = 97; | ||
|
||
|
||
class ClockFx with ChangeNotifier { | ||
|
||
double width; //宽 | ||
double height;//高 | ||
double sizeMin; // 宽高最小值 | ||
Offset center; //画布中心 | ||
Rect spawnArea; // 粒子活动区域 | ||
|
||
List<Particle> particles; // 所有粒子 | ||
|
||
int numParticles;// 最大粒子数 | ||
|
||
DateTime time; //时间 | ||
|
||
ClockFx({ | ||
@required Size size, | ||
@required DateTime time, | ||
this.numParticles = 5000, | ||
}) { | ||
this.time = time; | ||
particles = List<Particle>.filled(numParticles, null); | ||
setSize(size); | ||
} | ||
|
||
void init() { | ||
for (int i = 0; i < numParticles; i++) { | ||
particles[i] = Particle(color:Colors.black ); | ||
resetParticle(i); | ||
} | ||
} | ||
|
||
void setTime(DateTime time) { | ||
this.time = time; | ||
} | ||
|
||
void setSize(Size size) { | ||
width = size.width; | ||
height = size.height; | ||
sizeMin = min(width, height); | ||
center = Offset(width / 2, height / 2); | ||
spawnArea = Rect.fromLTRB( | ||
center.dx - sizeMin / 100, | ||
center.dy - sizeMin / 100, | ||
center.dx + sizeMin / 100, | ||
center.dy + sizeMin / 100, | ||
); | ||
init(); | ||
} | ||
|
||
/// Resets a particle's values. | ||
Particle resetParticle(int i) { | ||
Particle p = particles[i]; | ||
p.size = p.a = p.vx = p.vy = p.life = p.lifeLeft = 0; | ||
p.x = center.dx; | ||
p.y = center.dy; | ||
return p; | ||
} | ||
|
||
void tick(Duration duration) { | ||
updateParticles(duration); // 更新粒子 | ||
notifyListeners();// 通知监听者(画板)更新 | ||
} | ||
|
||
void updateParticles(Duration duration){ | ||
var secFrac = DateTime.now().millisecond / 1000; | ||
|
||
var vecSpeed = duration.compareTo(easingDelayDuration) > 0 | ||
? max(.2, Curves.easeInOutSine.transform(1 - secFrac)) | ||
: 1; | ||
|
||
var vecSpeedInv = Curves.easeInSine.transform(secFrac); | ||
|
||
var maxSpawnPerTick = 10; | ||
|
||
particles.asMap().forEach((i, p) { | ||
p.x -= p.vx * vecSpeed; | ||
p.y -= p.vy * vecSpeed; | ||
|
||
p.dist = _getDistanceFromCenter(p); | ||
p.distFrac = p.dist / (sizeMin / 2); | ||
|
||
p.lifeLeft = p.life - p.distFrac; | ||
|
||
p.vx -= p.lifeLeft * p.vx * .001; | ||
p.vy -= p.lifeLeft * p.vy * .001; | ||
|
||
if (p.lifeLeft < .3) { | ||
p.size -= p.size * .0015; | ||
} | ||
|
||
if (p.distribution > rougeDistributionLmt && p.distribution < jellyDistributionLmt) { | ||
var r = Rnd.getDouble(.2, 2.5) * vecSpeedInv * p.distFrac; | ||
p.x -= p.vx * r + (p.distFrac * Rnd.getDouble(-.4, .4)); | ||
p.y -= p.vy * r + (p.distFrac * Rnd.getDouble(-.4, .4)); | ||
} | ||
|
||
if (p.distribution >= jellyDistributionLmt) { | ||
var r = Rnd.getDouble(.1, .9) * vecSpeedInv * (1 - p.lifeLeft); | ||
p.x += p.vx * r; | ||
p.y += p.vy * r; | ||
} | ||
|
||
if (p.lifeLeft <= 0 || p.size <= .5) { | ||
resetParticle(i); | ||
if (maxSpawnPerTick > 0) { | ||
_activateParticle(p); | ||
maxSpawnPerTick--; | ||
} | ||
} | ||
|
||
}); | ||
} | ||
|
||
void _activateParticle(Particle p) { | ||
p.x = Rnd.getDouble(spawnArea.left, spawnArea.right); | ||
p.y = Rnd.getDouble(spawnArea.top, spawnArea.bottom); | ||
p.isFilled = Rnd.getBool(); | ||
p.size = Rnd.getDouble(3, 8); | ||
p.distFrac = 0; | ||
p.distribution = Rnd.getInt(1, 2); | ||
|
||
double angle = Rnd.ratio * pi * 2; | ||
|
||
|
||
var am = _getMinuteRadians(); | ||
var ah = _getHourRadians() % (pi * 2); | ||
var d = pi / 18; | ||
// | ||
// Probably not the most efficient solution right here. | ||
do { | ||
angle = Rnd.ratio * pi * 2; | ||
} while (_isBetween(angle, am - d, am + d) || | ||
_isBetween(angle, ah - d, ah + d) ); | ||
|
||
p.life = Rnd.getDouble(0.75, .8); | ||
|
||
p.size = sizeMin * | ||
(Rnd.ratio > .8 | ||
? Rnd.getDouble(.0015, .003) | ||
: Rnd.getDouble(.002, .006)); | ||
|
||
p.vx = sin(-angle); | ||
p.vy = cos(-angle); | ||
|
||
p.a = atan2(p.vy, p.vx) + pi; | ||
|
||
double v = Rnd.getDouble(.5, 1); | ||
|
||
p.vx *= v; | ||
p.vy *= v; | ||
} | ||
|
||
double _getDistanceFromCenter(Particle p) { | ||
var a = pow(center.dx - p.x, 2); | ||
var b = pow(center.dy - p.y, 2); | ||
return sqrt(a + b); | ||
} | ||
|
||
/// Gets the radians of the hour hand. | ||
double _getHourRadians() => | ||
(time.hour * pi / 6) + | ||
(time.minute * pi / (6 * 60)) + | ||
(time.second * pi / (360 * 60)); | ||
|
||
/// Gets the radians of the minute hand. | ||
double _getMinuteRadians() => | ||
(time.minute * (2 * pi) / 60) + (time.second * pi / (30 * 60)); | ||
|
||
/// Checks if a value is between two other values. | ||
bool _isBetween(double value, double min, double max) { | ||
return value >= min && value <= max; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import 'dart:math'; | ||
import 'dart:ui'; | ||
|
||
import 'package:flutter/foundation.dart'; | ||
import 'package:flutter/material.dart'; | ||
import 'package:flutter/scheduler.dart'; | ||
|
||
import 'clock_fx.dart'; | ||
|
||
/// create by 张风捷特烈 on 2021/2/7 | ||
/// contact me by email 1981462002@qq.com | ||
/// 说明: | ||
class ClockWidget extends StatefulWidget { | ||
final double radius; | ||
|
||
const ClockWidget({Key key, this.radius = 100}) : super(key: key); | ||
|
||
@override | ||
_ClockWidgetState createState() => _ClockWidgetState(); | ||
} | ||
|
||
class _ClockWidgetState extends State<ClockWidget> | ||
with SingleTickerProviderStateMixin { | ||
Ticker _ticker; | ||
ClockFx _fx; | ||
|
||
@override | ||
void initState() { | ||
super.initState(); | ||
_ticker = createTicker(_tick)..start(); | ||
_fx = ClockFx( | ||
size: Size(widget.radius * 2, widget.radius * 2), | ||
time: DateTime.now(), | ||
); | ||
} | ||
|
||
@override | ||
void dispose() { | ||
_ticker.dispose(); | ||
_fx.dispose(); | ||
super.dispose(); | ||
} | ||
|
||
void _tick(Duration duration) { | ||
_fx.tick(duration); | ||
if (_fx.time.second != DateTime.now().second) { | ||
_fx.setTime(DateTime.now()); | ||
} | ||
} | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return CustomPaint( | ||
size: Size(widget.radius * 2, widget.radius * 2), | ||
painter: ClockFxPainter(fx: _fx), | ||
); | ||
} | ||
} | ||
|
||
/// Alpha value for noise particles. | ||
const double noiseAlpha = 160; | ||
|
||
class ClockFxPainter extends CustomPainter { | ||
final ClockFx fx; | ||
|
||
ClockFxPainter({@required this.fx}) : super(repaint: fx); | ||
|
||
@override | ||
void paint(Canvas canvas, Size size) { | ||
fx.particles.forEach((p) { | ||
double a; | ||
a = max(0.0, (p.distFrac - .13) / p.distFrac) * 255; | ||
a = min(a, min(noiseAlpha, p.lifeLeft * 3 * 255)); | ||
int alpha = a.floor(); | ||
|
||
Paint circlePaint = Paint() | ||
..style = PaintingStyle.fill | ||
..color = p.color.withAlpha(alpha); | ||
|
||
canvas.drawCircle(Offset(p.x, p.y), p.size, circlePaint); | ||
}); | ||
} | ||
|
||
@override | ||
bool shouldRepaint(covariant ClockFxPainter oldDelegate) => | ||
oldDelegate.fx != fx; | ||
} |
Oops, something went wrong.