mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-05-06 01:57:45 +08:00
Compare commits
8 Commits
f0050dd6e6
...
2.0.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8ad130567e | ||
|
|
7eb21bc5a2 | ||
|
|
ea4316a847 | ||
|
|
2bbc97a950 | ||
|
|
0178d105ba | ||
|
|
771fa75f48 | ||
|
|
82483b33fc | ||
|
|
886c53c7d8 |
2
.github/workflows/mac.yml
vendored
2
.github/workflows/mac.yml
vendored
@@ -13,7 +13,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
build-mac-app:
|
build-mac-app:
|
||||||
name: Release Mac
|
name: Release Mac
|
||||||
runs-on: macos-latest
|
runs-on: macos-26
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v6
|
||||||
|
|||||||
@@ -131,5 +131,13 @@
|
|||||||
</array>
|
</array>
|
||||||
<key>UIStatusBarHidden</key>
|
<key>UIStatusBarHidden</key>
|
||||||
<false/>
|
<false/>
|
||||||
|
<key>NSLocalNetworkUsageDescription</key>
|
||||||
|
<string>需要访问本地网络以发现和连接 DLNA 投屏设备</string>
|
||||||
|
<key>NSBonjourServices</key>
|
||||||
|
<array>
|
||||||
|
<string>_ssdp._udp</string>
|
||||||
|
<string>_upnp._tcp</string>
|
||||||
|
<string>_http._tcp</string>
|
||||||
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -10,23 +10,21 @@ class CustomToast extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final ThemeData theme = Theme.of(context);
|
final colorScheme = ColorScheme.of(context);
|
||||||
return Container(
|
return Container(
|
||||||
margin: EdgeInsets.only(
|
margin: EdgeInsets.only(
|
||||||
bottom: MediaQuery.viewPaddingOf(context).bottom + 30,
|
bottom: MediaQuery.viewPaddingOf(context).bottom + 30,
|
||||||
),
|
),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 17, vertical: 10),
|
padding: const EdgeInsets.symmetric(horizontal: 17, vertical: 10),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: theme.colorScheme.primaryContainer.withValues(
|
color: colorScheme.primaryContainer.withValues(alpha: toastOpacity),
|
||||||
alpha: toastOpacity,
|
|
||||||
),
|
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(20)),
|
borderRadius: const BorderRadius.all(Radius.circular(20)),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
msg,
|
msg,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
color: theme.colorScheme.onPrimaryContainer,
|
color: colorScheme.onPrimaryContainer,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -41,7 +39,7 @@ class LoadingWidget extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final ThemeData theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
final onSurfaceVariant = theme.colorScheme.onSurfaceVariant;
|
final onSurfaceVariant = theme.colorScheme.onSurfaceVariant;
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 20),
|
padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 20),
|
||||||
@@ -58,7 +56,6 @@ class LoadingWidget extends StatelessWidget {
|
|||||||
strokeWidth: 3,
|
strokeWidth: 3,
|
||||||
valueColor: AlwaysStoppedAnimation(onSurfaceVariant),
|
valueColor: AlwaysStoppedAnimation(onSurfaceVariant),
|
||||||
),
|
),
|
||||||
|
|
||||||
//msg
|
//msg
|
||||||
Text(msg, style: TextStyle(color: onSurfaceVariant)),
|
Text(msg, style: TextStyle(color: onSurfaceVariant)),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -217,8 +217,8 @@ class RefreshIndicatorState extends State<RefreshIndicator>
|
|||||||
RefreshIndicatorStatus? _status;
|
RefreshIndicatorStatus? _status;
|
||||||
late Future<void> _pendingRefreshFuture;
|
late Future<void> _pendingRefreshFuture;
|
||||||
double? _dragOffset;
|
double? _dragOffset;
|
||||||
late Color _effectiveValueColor =
|
late Color _effectiveValueColor;
|
||||||
widget.color ?? Theme.of(context).colorScheme.primary;
|
// late Color _backgroundColor;
|
||||||
|
|
||||||
static final Animatable<double> _threeQuarterTween = Tween<double>(
|
static final Animatable<double> _threeQuarterTween = Tween<double>(
|
||||||
begin: 0.0,
|
begin: 0.0,
|
||||||
@@ -274,9 +274,10 @@ class RefreshIndicatorState extends State<RefreshIndicator>
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _setupColorTween() {
|
void _setupColorTween() {
|
||||||
|
final colorScheme = ColorScheme.of(context);
|
||||||
|
// _backgroundColor = colorScheme.surfaceContainerHighest;
|
||||||
// Reset the current value color.
|
// Reset the current value color.
|
||||||
_effectiveValueColor =
|
_effectiveValueColor = widget.color ?? colorScheme.primary;
|
||||||
widget.color ?? Theme.of(context).colorScheme.primary;
|
|
||||||
final Color color = _effectiveValueColor;
|
final Color color = _effectiveValueColor;
|
||||||
if (color.a == 0) {
|
if (color.a == 0) {
|
||||||
// Set an always stopped animation instead of a driven tween.
|
// Set an always stopped animation instead of a driven tween.
|
||||||
@@ -558,14 +559,52 @@ class RefreshIndicatorState extends State<RefreshIndicator>
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool _onDrag(double offset, double viewportDimension) {
|
bool _onDrag(double offset, double viewportDimension) {
|
||||||
if (_positionController.value > 0.0 &&
|
if (_positionController.value > 0.0 && _status == .drag) {
|
||||||
_status == RefreshIndicatorStatus.drag) {
|
|
||||||
_dragOffset = _dragOffset! + offset;
|
_dragOffset = _dragOffset! + offset;
|
||||||
_checkDragOffset(viewportDimension);
|
_checkDragOffset(viewportDimension);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// late final _refreshKey = GlobalKey();
|
||||||
|
// Widget _m3eRefreshProgressIndicator(bool showIndeterminateIndicator) {
|
||||||
|
// const indicatorMargin = EdgeInsets.all(4);
|
||||||
|
// const indicatorPadding = EdgeInsets.all(6);
|
||||||
|
// const indicatorSize = 41.0;
|
||||||
|
|
||||||
|
// final progress = _value.value;
|
||||||
|
// return Padding(
|
||||||
|
// padding: indicatorMargin,
|
||||||
|
// child: SizedBox(
|
||||||
|
// width: indicatorSize,
|
||||||
|
// height: indicatorSize,
|
||||||
|
// child: Material(
|
||||||
|
// type: MaterialType.circle,
|
||||||
|
// color: _backgroundColor,
|
||||||
|
// elevation: widget.elevation,
|
||||||
|
// child: Padding(
|
||||||
|
// padding: indicatorPadding,
|
||||||
|
// child: showIndeterminateIndicator
|
||||||
|
// ? M3ELoadingIndicator(
|
||||||
|
// childKey: _refreshKey,
|
||||||
|
// color: _effectiveValueColor,
|
||||||
|
// morphs: Morphs.refreshMorphs,
|
||||||
|
// size: null,
|
||||||
|
// )
|
||||||
|
// : RawM3ELoadingIndicator(
|
||||||
|
// key: _refreshKey,
|
||||||
|
// morph: Morphs.manualMorph,
|
||||||
|
// progress: progress,
|
||||||
|
// angle: -progress * math.pi,
|
||||||
|
// color: _valueColor.value!,
|
||||||
|
// size: null,
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
// ignore: camel_case_types
|
// ignore: camel_case_types
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
import 'dart:math' show pi;
|
import 'dart:math' show pi;
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/semantics.dart' show SemanticsConfiguration;
|
||||||
|
|
||||||
///
|
///
|
||||||
/// created by dom on 2026/02/14
|
/// created by dom on 2026/02/14
|
||||||
@@ -73,6 +74,7 @@ class RenderLoadingIndicator extends RenderBox {
|
|||||||
if (_progress == value) return;
|
if (_progress == value) return;
|
||||||
_progress = value;
|
_progress = value;
|
||||||
markNeedsPaint();
|
markNeedsPaint();
|
||||||
|
markNeedsSemanticsUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -119,6 +121,16 @@ class RenderLoadingIndicator extends RenderBox {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void describeSemanticsConfiguration(SemanticsConfiguration config) {
|
||||||
|
super.describeSemanticsConfiguration(config);
|
||||||
|
config
|
||||||
|
..role = .progressBar
|
||||||
|
..minValue = '0'
|
||||||
|
..maxValue = '100'
|
||||||
|
..value = (_progress * 100).round().toString();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool get isRepaintBoundary => true;
|
bool get isRepaintBoundary => true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ import 'package:flutter/material.dart';
|
|||||||
|
|
||||||
const Widget m3eLoading = Center(child: M3ELoadingIndicator());
|
const Widget m3eLoading = Center(child: M3ELoadingIndicator());
|
||||||
|
|
||||||
const Widget circularLoading = Center(child: CircularProgressIndicator());
|
|
||||||
|
|
||||||
const Widget linearLoading = SliverToBoxAdapter(
|
const Widget linearLoading = SliverToBoxAdapter(
|
||||||
child: LinearProgressIndicator(),
|
child: LinearProgressIndicator(),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -17,14 +17,27 @@
|
|||||||
|
|
||||||
import 'dart:math' as math;
|
import 'dart:math' as math;
|
||||||
|
|
||||||
|
import 'package:PiliPlus/common/widgets/loading_widget/morphs.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/physics.dart' show SpringSimulation;
|
import 'package:flutter/physics.dart' show SpringSimulation;
|
||||||
|
import 'package:flutter/semantics.dart';
|
||||||
import 'package:material_new_shapes/material_new_shapes.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
|
/// reimplement of https://github.com/EmilyMoonstone/material_3_expressive/tree/main/packages/loading_indicator_m3e
|
||||||
|
|
||||||
class M3ELoadingIndicator extends StatefulWidget {
|
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
|
@override
|
||||||
State<M3ELoadingIndicator> createState() => _M3ELoadingIndicatorState();
|
State<M3ELoadingIndicator> createState() => _M3ELoadingIndicatorState();
|
||||||
@@ -32,42 +45,25 @@ class M3ELoadingIndicator extends StatefulWidget {
|
|||||||
|
|
||||||
class _M3ELoadingIndicatorState extends State<M3ELoadingIndicator>
|
class _M3ELoadingIndicatorState extends State<M3ELoadingIndicator>
|
||||||
with SingleTickerProviderStateMixin {
|
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 int _morphIntervalMs = 650;
|
||||||
static const double _fullRotation = 360.0;
|
static const double _fullRotation = 2 * math.pi;
|
||||||
static const int _globalRotationDurationMs = 4666;
|
static const int _globalRotationDurationMs = 4666;
|
||||||
static const double _quarterRotation = _fullRotation / 4;
|
static const double _quarterRotation = _fullRotation / 4;
|
||||||
|
|
||||||
|
late final List<Morph> _morphs;
|
||||||
late final AnimationController _controller;
|
late final AnimationController _controller;
|
||||||
|
|
||||||
int _morphIndex = 1;
|
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),
|
SpringDescription.withDampingRatio(ratio: 0.6, stiffness: 200.0, mass: 1.0),
|
||||||
0.0,
|
0.0,
|
||||||
1.0,
|
1.0,
|
||||||
5.0,
|
5.0,
|
||||||
snapToEnd: true,
|
snapToEnd: true,
|
||||||
|
// tolerance: const Tolerance(velocity: 0.1, distance: 0.1),
|
||||||
);
|
);
|
||||||
|
|
||||||
void _statusListener(AnimationStatus status) {
|
void _statusListener(AnimationStatus status) {
|
||||||
@@ -78,23 +74,21 @@ class _M3ELoadingIndicatorState extends State<M3ELoadingIndicator>
|
|||||||
|
|
||||||
void _startAnimation() {
|
void _startAnimation() {
|
||||||
_morphIndex++;
|
_morphIndex++;
|
||||||
_morphRotationTargetAngle =
|
_morphRotationTarget =
|
||||||
(_morphRotationTargetAngle + _quarterRotation) % _fullRotation;
|
(_morphRotationTarget + _quarterRotation) % _fullRotation;
|
||||||
_controller
|
_controller.animateWith(_morphAnimationSpec);
|
||||||
..value = 0.0
|
|
||||||
..animateWith(_morphAnimationSpec);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
_morphs = widget.morphs ?? Morphs.loadingMorphs;
|
||||||
_controller =
|
_controller =
|
||||||
AnimationController(
|
AnimationController(
|
||||||
vsync: this,
|
vsync: this,
|
||||||
duration: const Duration(milliseconds: _morphIntervalMs),
|
duration: const Duration(milliseconds: _morphIntervalMs),
|
||||||
)
|
)
|
||||||
..addStatusListener(_statusListener)
|
..addStatusListener(_statusListener)
|
||||||
..value = 0.0
|
|
||||||
..animateWith(_morphAnimationSpec);
|
..animateWith(_morphAnimationSpec);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,82 +104,86 @@ class _M3ELoadingIndicatorState extends State<M3ELoadingIndicator>
|
|||||||
final elapsedInMs =
|
final elapsedInMs =
|
||||||
_morphIntervalMs * (_morphIndex - 1) +
|
_morphIntervalMs * (_morphIndex - 1) +
|
||||||
(_controller.lastElapsedDuration?.inMilliseconds ?? 0);
|
(_controller.lastElapsedDuration?.inMilliseconds ?? 0);
|
||||||
final globalRotationControllerValue =
|
final globalRotation =
|
||||||
(elapsedInMs % _globalRotationDurationMs) / _globalRotationDurationMs;
|
(elapsedInMs % _globalRotationDurationMs) /
|
||||||
final globalRotationDegrees = globalRotationControllerValue * _fullRotation;
|
_globalRotationDurationMs *
|
||||||
final totalRotationDegrees =
|
_fullRotation;
|
||||||
progress * _quarterRotation +
|
|
||||||
_morphRotationTargetAngle +
|
return progress * _quarterRotation + _morphRotationTarget + globalRotation;
|
||||||
globalRotationDegrees;
|
|
||||||
return totalRotationDegrees * (math.pi / 180.0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final color = Theme.of(context).colorScheme.secondaryFixedDim;
|
final color = widget.color ?? ColorScheme.of(context).secondaryFixedDim;
|
||||||
return AnimatedBuilder(
|
return AnimatedBuilder(
|
||||||
animation: _controller,
|
animation: _controller,
|
||||||
builder: (context, child) {
|
builder: (context, child) {
|
||||||
final progress = _controller.value;
|
final progress = _controller.value;
|
||||||
return _M3ELoadingIndicator(
|
return RawM3ELoadingIndicator(
|
||||||
|
// key: widget.childKey,
|
||||||
morph: _morphs[_morphIndex % _morphs.length],
|
morph: _morphs[_morphIndex % _morphs.length],
|
||||||
progress: progress,
|
progress: progress,
|
||||||
angle: _calcAngle(progress),
|
angle: _calcAngle(progress),
|
||||||
color: color,
|
color: color,
|
||||||
|
size: widget.size,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _M3ELoadingIndicator extends LeafRenderObjectWidget {
|
class RawM3ELoadingIndicator extends LeafRenderObjectWidget {
|
||||||
const _M3ELoadingIndicator({
|
const RawM3ELoadingIndicator({
|
||||||
|
super.key,
|
||||||
required this.morph,
|
required this.morph,
|
||||||
required this.progress,
|
required this.progress,
|
||||||
required this.angle,
|
required this.angle,
|
||||||
required this.color,
|
required this.color,
|
||||||
|
required this.size,
|
||||||
});
|
});
|
||||||
|
|
||||||
final Morph morph;
|
final Morph morph;
|
||||||
|
|
||||||
final double progress;
|
final double progress;
|
||||||
|
|
||||||
final double angle;
|
final double angle;
|
||||||
|
|
||||||
final Color color;
|
final Color color;
|
||||||
|
final Size size;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
RenderObject createRenderObject(BuildContext context) {
|
RenderObject createRenderObject(BuildContext context) {
|
||||||
return _RenderM3ELoadingIndicator(
|
return RenderM3ELoadingIndicator(
|
||||||
morph: morph,
|
morph: morph,
|
||||||
progress: progress,
|
progress: progress,
|
||||||
angle: angle,
|
angle: angle,
|
||||||
color: color,
|
color: color,
|
||||||
|
size: size,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void updateRenderObject(
|
void updateRenderObject(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
_RenderM3ELoadingIndicator renderObject,
|
RenderM3ELoadingIndicator renderObject,
|
||||||
) {
|
) {
|
||||||
renderObject
|
renderObject
|
||||||
..morph = morph
|
..morph = morph
|
||||||
..progress = progress
|
..progress = progress
|
||||||
..angle = angle
|
..angle = angle
|
||||||
..color = color;
|
..color = color
|
||||||
|
..preferredSize = size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _RenderM3ELoadingIndicator extends RenderBox {
|
class RenderM3ELoadingIndicator extends RenderBox {
|
||||||
_RenderM3ELoadingIndicator({
|
RenderM3ELoadingIndicator({
|
||||||
required Morph morph,
|
required Morph morph,
|
||||||
required double progress,
|
required double progress,
|
||||||
required double angle,
|
required double angle,
|
||||||
required Color color,
|
required Color color,
|
||||||
|
required Size size,
|
||||||
}) : _morph = morph,
|
}) : _morph = morph,
|
||||||
_progress = progress,
|
_progress = progress,
|
||||||
_angle = angle,
|
_angle = angle,
|
||||||
|
_preferredSize = size,
|
||||||
_color = color,
|
_color = color,
|
||||||
_paint = Paint()
|
_paint = Paint()
|
||||||
..style = PaintingStyle.fill
|
..style = PaintingStyle.fill
|
||||||
@@ -223,20 +221,38 @@ class _RenderM3ELoadingIndicator extends RenderBox {
|
|||||||
markNeedsPaint();
|
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
|
@override
|
||||||
void performLayout() {
|
void performLayout() {
|
||||||
size = constraints.constrainDimensions(40, 40);
|
size = computeDryLayout(constraints);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void describeSemanticsConfiguration(SemanticsConfiguration config) {
|
||||||
|
super.describeSemanticsConfiguration(config);
|
||||||
|
config.role = .loadingSpinner;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void paint(PaintingContext context, Offset offset) {
|
void paint(PaintingContext context, Offset offset) {
|
||||||
final width = size.width;
|
final width = size.width;
|
||||||
final value = size.width / 2;
|
final value = size.width / 2;
|
||||||
final matrix = Matrix4.identity()
|
final matrix =
|
||||||
..translateByDouble(offset.dx + value, offset.dy + value, 0.0, 1.0)
|
Matrix4.translationValues(offset.dx + value, offset.dy + value, 0.0)
|
||||||
..rotateZ(angle)
|
..rotateZ(angle)
|
||||||
..translateByDouble(-value, -value, 0.0, 1.0)
|
..translateByDouble(-value, -value, 0.0, 1.0)
|
||||||
..scaleByDouble(width, width, width, 1.0);
|
..scaleByDouble(width, width, width, 1.0);
|
||||||
final path = morph.toPath(progress: progress).transform(matrix.storage);
|
final path = morph.toPath(progress: progress).transform(matrix.storage);
|
||||||
|
|
||||||
context.canvas.drawPath(path, _paint);
|
context.canvas.drawPath(path, _paint);
|
||||||
|
|||||||
41
lib/common/widgets/loading_widget/morphs.dart
Normal file
41
lib/common/widgets/loading_widget/morphs.dart
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import 'package:material_new_shapes/material_new_shapes.dart';
|
||||||
|
|
||||||
|
abstract final class Morphs {
|
||||||
|
static List<Morph> buildMorph(
|
||||||
|
List<RoundedPolygon> shapes, {
|
||||||
|
bool loop = true,
|
||||||
|
}) {
|
||||||
|
assert(shapes.length >= 2);
|
||||||
|
return [
|
||||||
|
for (var i = 0; i < shapes.length - 1; i++)
|
||||||
|
Morph(shapes[i], shapes[i + 1]),
|
||||||
|
if (loop) Morph(shapes[shapes.length - 1], shapes[0]),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
static final loadingMorphs = buildMorph([
|
||||||
|
MaterialShapes.softBurst,
|
||||||
|
MaterialShapes.cookie9Sided,
|
||||||
|
MaterialShapes.pentagon,
|
||||||
|
MaterialShapes.pill,
|
||||||
|
MaterialShapes.sunny,
|
||||||
|
MaterialShapes.cookie4Sided,
|
||||||
|
MaterialShapes.oval,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// static final refreshMorphs = buildMorph([
|
||||||
|
// MaterialShapes.softBurst,
|
||||||
|
// MaterialShapes.cookie9Sided,
|
||||||
|
// MaterialShapes.gem,
|
||||||
|
// MaterialShapes.flower,
|
||||||
|
// MaterialShapes.sunny,
|
||||||
|
// MaterialShapes.cookie4Sided,
|
||||||
|
// MaterialShapes.oval,
|
||||||
|
// MaterialShapes.cookie12Sided,
|
||||||
|
// ]);
|
||||||
|
|
||||||
|
// static final manualMorph = Morph(
|
||||||
|
// MaterialShapes.circle,
|
||||||
|
// MaterialShapes.softBurst,
|
||||||
|
// );
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@ class BaseEmote {
|
|||||||
late String emoticonUnique;
|
late String emoticonUnique;
|
||||||
late double width;
|
late double width;
|
||||||
late double height;
|
late double height;
|
||||||
late final isUpower = emoticonUnique.startsWith('upower_');
|
late final isOfficial = emoticonUnique.startsWith('official_');
|
||||||
|
|
||||||
BaseEmote.fromJson(Map<String, dynamic> json) {
|
BaseEmote.fromJson(Map<String, dynamic> json) {
|
||||||
url = json['url'];
|
url = json['url'];
|
||||||
|
|||||||
@@ -181,21 +181,23 @@ class AuthorPanel extends StatelessWidget {
|
|||||||
Positioned(
|
Positioned(
|
||||||
top: 0,
|
top: 0,
|
||||||
right: 0,
|
right: 0,
|
||||||
height: height,
|
bottom: 0,
|
||||||
child: CachedNetworkImage(
|
child: Center(
|
||||||
height: height,
|
child: CachedNetworkImage(
|
||||||
memCacheHeight: height.cacheSize(context),
|
height: height,
|
||||||
imageUrl: ImageUtils.safeThumbnailUrl(
|
memCacheHeight: height.cacheSize(context),
|
||||||
moduleAuthor.decorate!.cardUrl,
|
imageUrl: ImageUtils.safeThumbnailUrl(
|
||||||
|
moduleAuthor.decorate!.cardUrl,
|
||||||
|
),
|
||||||
|
placeholder: (_, _) => const SizedBox.shrink(),
|
||||||
),
|
),
|
||||||
placeholder: (_, _) => const SizedBox.shrink(),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (moduleAuthor.decorate!.fan?.numStr?.isNotEmpty == true)
|
if (moduleAuthor.decorate!.fan?.numStr?.isNotEmpty == true)
|
||||||
Positioned(
|
Positioned(
|
||||||
top: 0,
|
top: 0,
|
||||||
|
bottom: 0,
|
||||||
right: height,
|
right: height,
|
||||||
height: height,
|
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
moduleAuthor.decorate!.fan!.numStr!.toString(),
|
moduleAuthor.decorate!.fan!.numStr!.toString(),
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import 'package:PiliPlus/common/widgets/flutter/popup_menu.dart';
|
|||||||
import 'package:PiliPlus/common/widgets/gesture/tap_gesture_recognizer.dart';
|
import 'package:PiliPlus/common/widgets/gesture/tap_gesture_recognizer.dart';
|
||||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||||
import 'package:PiliPlus/http/live.dart';
|
import 'package:PiliPlus/http/live.dart';
|
||||||
import 'package:PiliPlus/models/common/image_type.dart';
|
|
||||||
import 'package:PiliPlus/models_new/live/live_danmaku/danmaku_msg.dart';
|
import 'package:PiliPlus/models_new/live/live_danmaku/danmaku_msg.dart';
|
||||||
import 'package:PiliPlus/models_new/live/live_superchat/item.dart';
|
import 'package:PiliPlus/models_new/live/live_superchat/item.dart';
|
||||||
import 'package:PiliPlus/pages/live_room/controller.dart';
|
import 'package:PiliPlus/pages/live_room/controller.dart';
|
||||||
@@ -241,14 +240,20 @@ class LiveRoomChatPanel extends StatelessWidget {
|
|||||||
InlineSpan _buildMsg(double devicePixelRatio, DanmakuMsg obj) {
|
InlineSpan _buildMsg(double devicePixelRatio, DanmakuMsg obj) {
|
||||||
final uemote = obj.uemote;
|
final uemote = obj.uemote;
|
||||||
if (uemote != null) {
|
if (uemote != null) {
|
||||||
// "room_{{room_id}}_{{int}}" or "upower_[{{emote}}]"
|
// "room_{{room_id}}_{{int}}" , "upower_[{{emote}}]" , "official_{{int}}"
|
||||||
final isUpower = uemote.isUpower;
|
final double width, height;
|
||||||
|
if (uemote.isOfficial) {
|
||||||
|
width = uemote.width / devicePixelRatio;
|
||||||
|
height = uemote.height / devicePixelRatio;
|
||||||
|
} else {
|
||||||
|
width = height = 162.0 / devicePixelRatio;
|
||||||
|
}
|
||||||
return WidgetSpan(
|
return WidgetSpan(
|
||||||
child: NetworkImgLayer(
|
child: NetworkImgLayer(
|
||||||
src: uemote.url,
|
src: uemote.url,
|
||||||
type: ImageType.emote,
|
type: .emote,
|
||||||
width: isUpower ? uemote.width : uemote.width / devicePixelRatio,
|
width: width,
|
||||||
height: isUpower ? uemote.height : uemote.height / devicePixelRatio,
|
height: height,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -265,7 +270,7 @@ class LiveRoomChatPanel extends StatelessWidget {
|
|||||||
WidgetSpan(
|
WidgetSpan(
|
||||||
child: NetworkImgLayer(
|
child: NetworkImgLayer(
|
||||||
src: emote.url,
|
src: emote.url,
|
||||||
type: ImageType.emote,
|
type: .emote,
|
||||||
width: emote.width,
|
width: emote.width,
|
||||||
height: emote.height,
|
height: emote.height,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -73,3 +73,10 @@ foreach ($patch in $patches) {
|
|||||||
Write-Host "$patch applied"
|
Write-Host "$patch applied"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# TODO: remove
|
||||||
|
if ($platform.ToLower() -eq "android") {
|
||||||
|
"69e31205362b4e59b7eb89b24797e687b4b67afe" | Set-Content -Path .\bin\internal\engine.version
|
||||||
|
Remove-Item -Path ".\bin\cache" -Recurse -Force
|
||||||
|
flutter --version
|
||||||
|
}
|
||||||
@@ -28,5 +28,13 @@
|
|||||||
<string>MainMenu</string>
|
<string>MainMenu</string>
|
||||||
<key>NSPrincipalClass</key>
|
<key>NSPrincipalClass</key>
|
||||||
<string>NSApplication</string>
|
<string>NSApplication</string>
|
||||||
|
<key>NSLocalNetworkUsageDescription</key>
|
||||||
|
<string>需要访问本地网络以发现和连接 DLNA 投屏设备</string>
|
||||||
|
<key>NSBonjourServices</key>
|
||||||
|
<array>
|
||||||
|
<string>_ssdp._udp</string>
|
||||||
|
<string>_upnp._tcp</string>
|
||||||
|
<string>_http._tcp</string>
|
||||||
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
28
pubspec.lock
28
pubspec.lock
@@ -320,18 +320,18 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: connectivity_plus
|
name: connectivity_plus
|
||||||
sha256: "33bae12a398f841c6cda09d1064212957265869104c478e5ad51e2fb26c3973c"
|
sha256: b8fe52979ff12432ecf8f0abf6ff70410b1bb734be1c9e4f2f86807ad7166c79
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.0.0"
|
version: "7.1.0"
|
||||||
connectivity_plus_platform_interface:
|
connectivity_plus_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: connectivity_plus_platform_interface
|
name: connectivity_plus_platform_interface
|
||||||
sha256: "42657c1715d48b167930d5f34d00222ac100475f73d10162ddf43e714932f204"
|
sha256: "3c09627c536d22fd24691a905cdd8b14520de69da52c7a97499c8be5284a32ed"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.1"
|
version: "2.1.0"
|
||||||
convert:
|
convert:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -400,10 +400,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: device_info_plus
|
name: device_info_plus
|
||||||
sha256: "4df8babf73058181227e18b08e6ea3520cf5fc5d796888d33b7cb0f33f984b7c"
|
sha256: b4fed1b2835da9d670d7bed7db79ae2a94b0f5ad6312268158a9b5479abbacdd
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "12.3.0"
|
version: "12.4.0"
|
||||||
device_info_plus_platform_interface:
|
device_info_plus_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -906,10 +906,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: image_cropper
|
name: image_cropper
|
||||||
sha256: "2cd06f097b5bd18ff77d3f80fadef83e270ea23c82906bbf17febc3db8d68ec6"
|
sha256: d2555be1ec4b7b12fc502ede481c846ad44578fbb0748debd4c648b25ca07cad
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "12.1.0"
|
version: "12.1.1"
|
||||||
image_cropper_for_web:
|
image_cropper_for_web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1283,10 +1283,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: package_info_plus
|
name: package_info_plus
|
||||||
sha256: f69da0d3189a4b4ceaeb1a3defb0f329b3b352517f52bed4290f83d4f06bc08d
|
sha256: "468c26b4254ab01979fa5e4a98cb343ea3631b9acee6f21028997419a80e1a20"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "9.0.0"
|
version: "9.0.1"
|
||||||
package_info_plus_platform_interface:
|
package_info_plus_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1579,10 +1579,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: share_plus
|
name: share_plus
|
||||||
sha256: "14c8860d4de93d3a7e53af51bff479598c4e999605290756bbbe45cf65b37840"
|
sha256: "223873d106614442ea6f20db5a038685cc5b32a2fba81cdecaefbbae0523f7fa"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "12.0.1"
|
version: "12.0.2"
|
||||||
share_plus_platform_interface:
|
share_plus_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1905,10 +1905,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: vector_graphics
|
name: vector_graphics
|
||||||
sha256: "7076216a10d5c390315fbe536a30f1254c341e7543e6c4c8a815e591307772b1"
|
sha256: "81da85e9ca8885ade47f9685b953cb098970d11be4821ac765580a6607ea4373"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.20"
|
version: "1.1.21"
|
||||||
vector_graphics_codec:
|
vector_graphics_codec:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
|
|||||||
# In Windows, build-name is used as the major, minor, and patch parts
|
# In Windows, build-name is used as the major, minor, and patch parts
|
||||||
# of the product and file versions while build-number is used as the build suffix.
|
# of the product and file versions while build-number is used as the build suffix.
|
||||||
# update when release
|
# update when release
|
||||||
version: 2.0.1+1
|
version: 2.0.2+1
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=3.10.0"
|
sdk: ">=3.10.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user