mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-04-20 11:08:03 +08:00
opt: m3e loading (#1877)
* opt: loading * feat: refresh m3e * restore refreshIndicator --------- Co-authored-by: dom <githubaccount56556@proton.me>
This commit is contained in:
committed by
GitHub
parent
f0050dd6e6
commit
886c53c7d8
@@ -17,14 +17,27 @@
|
||||
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:PiliPlus/common/widgets/loading_widget/morphs.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/physics.dart' show SpringSimulation;
|
||||
import 'package:flutter/semantics.dart';
|
||||
import 'package:material_new_shapes/material_new_shapes.dart';
|
||||
|
||||
/// reimplement of https://github.com/EmilyMoonstone/material_3_expressive/tree/main/packages/loading_indicator_m3e
|
||||
|
||||
class M3ELoadingIndicator extends StatefulWidget {
|
||||
const M3ELoadingIndicator({super.key});
|
||||
const M3ELoadingIndicator({
|
||||
super.key,
|
||||
// this.childKey,
|
||||
this.morphs,
|
||||
this.color,
|
||||
this.size = const Size.square(40),
|
||||
});
|
||||
final List<Morph>? morphs;
|
||||
|
||||
final Color? color;
|
||||
final Size size;
|
||||
// final Key? childKey;
|
||||
|
||||
@override
|
||||
State<M3ELoadingIndicator> createState() => _M3ELoadingIndicatorState();
|
||||
@@ -32,42 +45,25 @@ class M3ELoadingIndicator extends StatefulWidget {
|
||||
|
||||
class _M3ELoadingIndicatorState extends State<M3ELoadingIndicator>
|
||||
with SingleTickerProviderStateMixin {
|
||||
static final List<Morph> _morphs = () {
|
||||
final List<RoundedPolygon> shapes = [
|
||||
MaterialShapes.softBurst,
|
||||
MaterialShapes.cookie9Sided,
|
||||
MaterialShapes.pentagon,
|
||||
MaterialShapes.pill,
|
||||
MaterialShapes.sunny,
|
||||
MaterialShapes.cookie4Sided,
|
||||
MaterialShapes.oval,
|
||||
];
|
||||
return [
|
||||
for (var i = 0; i < shapes.length; i++)
|
||||
Morph(
|
||||
shapes[i],
|
||||
shapes[(i + 1) % shapes.length],
|
||||
),
|
||||
];
|
||||
}();
|
||||
|
||||
static const int _morphIntervalMs = 650;
|
||||
static const double _fullRotation = 360.0;
|
||||
static const double _fullRotation = 2 * math.pi;
|
||||
static const int _globalRotationDurationMs = 4666;
|
||||
static const double _quarterRotation = _fullRotation / 4;
|
||||
|
||||
late final List<Morph> _morphs;
|
||||
late final AnimationController _controller;
|
||||
|
||||
int _morphIndex = 1;
|
||||
|
||||
double _morphRotationTargetAngle = _quarterRotation;
|
||||
double _morphRotationTarget = _quarterRotation;
|
||||
|
||||
final _morphAnimationSpec = SpringSimulation(
|
||||
static final _morphAnimationSpec = SpringSimulation(
|
||||
SpringDescription.withDampingRatio(ratio: 0.6, stiffness: 200.0, mass: 1.0),
|
||||
0.0,
|
||||
1.0,
|
||||
5.0,
|
||||
snapToEnd: true,
|
||||
// tolerance: const Tolerance(velocity: 0.1, distance: 0.1),
|
||||
);
|
||||
|
||||
void _statusListener(AnimationStatus status) {
|
||||
@@ -78,23 +74,21 @@ class _M3ELoadingIndicatorState extends State<M3ELoadingIndicator>
|
||||
|
||||
void _startAnimation() {
|
||||
_morphIndex++;
|
||||
_morphRotationTargetAngle =
|
||||
(_morphRotationTargetAngle + _quarterRotation) % _fullRotation;
|
||||
_controller
|
||||
..value = 0.0
|
||||
..animateWith(_morphAnimationSpec);
|
||||
_morphRotationTarget =
|
||||
(_morphRotationTarget + _quarterRotation) % _fullRotation;
|
||||
_controller.animateWith(_morphAnimationSpec);
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_morphs = widget.morphs ?? Morphs.loadingMorphs;
|
||||
_controller =
|
||||
AnimationController(
|
||||
vsync: this,
|
||||
duration: const Duration(milliseconds: _morphIntervalMs),
|
||||
)
|
||||
..addStatusListener(_statusListener)
|
||||
..value = 0.0
|
||||
..animateWith(_morphAnimationSpec);
|
||||
}
|
||||
|
||||
@@ -110,82 +104,86 @@ class _M3ELoadingIndicatorState extends State<M3ELoadingIndicator>
|
||||
final elapsedInMs =
|
||||
_morphIntervalMs * (_morphIndex - 1) +
|
||||
(_controller.lastElapsedDuration?.inMilliseconds ?? 0);
|
||||
final globalRotationControllerValue =
|
||||
(elapsedInMs % _globalRotationDurationMs) / _globalRotationDurationMs;
|
||||
final globalRotationDegrees = globalRotationControllerValue * _fullRotation;
|
||||
final totalRotationDegrees =
|
||||
progress * _quarterRotation +
|
||||
_morphRotationTargetAngle +
|
||||
globalRotationDegrees;
|
||||
return totalRotationDegrees * (math.pi / 180.0);
|
||||
final globalRotation =
|
||||
(elapsedInMs % _globalRotationDurationMs) /
|
||||
_globalRotationDurationMs *
|
||||
_fullRotation;
|
||||
|
||||
return progress * _quarterRotation + _morphRotationTarget + globalRotation;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final color = Theme.of(context).colorScheme.secondaryFixedDim;
|
||||
final color = widget.color ?? ColorScheme.of(context).secondaryFixedDim;
|
||||
return AnimatedBuilder(
|
||||
animation: _controller,
|
||||
builder: (context, child) {
|
||||
final progress = _controller.value;
|
||||
return _M3ELoadingIndicator(
|
||||
return RawM3ELoadingIndicator(
|
||||
// key: widget.childKey,
|
||||
morph: _morphs[_morphIndex % _morphs.length],
|
||||
progress: progress,
|
||||
angle: _calcAngle(progress),
|
||||
color: color,
|
||||
size: widget.size,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _M3ELoadingIndicator extends LeafRenderObjectWidget {
|
||||
const _M3ELoadingIndicator({
|
||||
class RawM3ELoadingIndicator extends LeafRenderObjectWidget {
|
||||
const RawM3ELoadingIndicator({
|
||||
super.key,
|
||||
required this.morph,
|
||||
required this.progress,
|
||||
required this.angle,
|
||||
required this.color,
|
||||
required this.size,
|
||||
});
|
||||
|
||||
final Morph morph;
|
||||
|
||||
final double progress;
|
||||
|
||||
final double angle;
|
||||
|
||||
final Color color;
|
||||
final Size size;
|
||||
|
||||
@override
|
||||
RenderObject createRenderObject(BuildContext context) {
|
||||
return _RenderM3ELoadingIndicator(
|
||||
return RenderM3ELoadingIndicator(
|
||||
morph: morph,
|
||||
progress: progress,
|
||||
angle: angle,
|
||||
color: color,
|
||||
size: size,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void updateRenderObject(
|
||||
BuildContext context,
|
||||
_RenderM3ELoadingIndicator renderObject,
|
||||
RenderM3ELoadingIndicator renderObject,
|
||||
) {
|
||||
renderObject
|
||||
..morph = morph
|
||||
..progress = progress
|
||||
..angle = angle
|
||||
..color = color;
|
||||
..color = color
|
||||
..preferredSize = size;
|
||||
}
|
||||
}
|
||||
|
||||
class _RenderM3ELoadingIndicator extends RenderBox {
|
||||
_RenderM3ELoadingIndicator({
|
||||
class RenderM3ELoadingIndicator extends RenderBox {
|
||||
RenderM3ELoadingIndicator({
|
||||
required Morph morph,
|
||||
required double progress,
|
||||
required double angle,
|
||||
required Color color,
|
||||
required Size size,
|
||||
}) : _morph = morph,
|
||||
_progress = progress,
|
||||
_angle = angle,
|
||||
_preferredSize = size,
|
||||
_color = color,
|
||||
_paint = Paint()
|
||||
..style = PaintingStyle.fill
|
||||
@@ -223,20 +221,38 @@ class _RenderM3ELoadingIndicator extends RenderBox {
|
||||
markNeedsPaint();
|
||||
}
|
||||
|
||||
Size _preferredSize;
|
||||
set preferredSize(Size value) {
|
||||
if (_preferredSize == value) return;
|
||||
_preferredSize = size;
|
||||
markNeedsLayout();
|
||||
}
|
||||
|
||||
@override
|
||||
Size computeDryLayout(covariant BoxConstraints constraints) {
|
||||
return constraints.constrain(_preferredSize);
|
||||
}
|
||||
|
||||
@override
|
||||
void performLayout() {
|
||||
size = constraints.constrainDimensions(40, 40);
|
||||
size = computeDryLayout(constraints);
|
||||
}
|
||||
|
||||
@override
|
||||
void describeSemanticsConfiguration(SemanticsConfiguration config) {
|
||||
super.describeSemanticsConfiguration(config);
|
||||
config.role = .loadingSpinner;
|
||||
}
|
||||
|
||||
@override
|
||||
void paint(PaintingContext context, Offset offset) {
|
||||
final width = size.width;
|
||||
final value = size.width / 2;
|
||||
final matrix = Matrix4.identity()
|
||||
..translateByDouble(offset.dx + value, offset.dy + value, 0.0, 1.0)
|
||||
..rotateZ(angle)
|
||||
..translateByDouble(-value, -value, 0.0, 1.0)
|
||||
..scaleByDouble(width, width, width, 1.0);
|
||||
final matrix =
|
||||
Matrix4.translationValues(offset.dx + value, offset.dy + value, 0.0)
|
||||
..rotateZ(angle)
|
||||
..translateByDouble(-value, -value, 0.0, 1.0)
|
||||
..scaleByDouble(width, width, width, 1.0);
|
||||
final path = morph.toPath(progress: progress).transform(matrix.storage);
|
||||
|
||||
context.canvas.drawPath(path, _paint);
|
||||
|
||||
Reference in New Issue
Block a user