Compare commits
1 Commits
v0.8.88-pr
...
v0.8.88-pr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9428d4ef4c |
@@ -1,6 +1,6 @@
|
||||
module core
|
||||
|
||||
go 1.25
|
||||
go 1.20
|
||||
|
||||
replace github.com/metacubex/mihomo => ./Clash.Meta
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
@@ -13,14 +12,14 @@ class CommonPrint {
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
void log(String? text, {LogLevel logLevel = LogLevel.info}) {
|
||||
void log(String? text) {
|
||||
final payload = '[APP] $text';
|
||||
debugPrint(payload);
|
||||
if (!globalState.isInit) {
|
||||
return;
|
||||
}
|
||||
globalState.appController.addLog(
|
||||
Log.app(payload).copyWith(logLevel: logLevel),
|
||||
Log.app(payload),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,13 +73,13 @@ class Request {
|
||||
}
|
||||
|
||||
final Map<String, IpInfo Function(Map<String, dynamic>)> _ipInfoSources = {
|
||||
'https://ipwho.is': IpInfo.fromIpWhoIsJson,
|
||||
'https://api.myip.com': IpInfo.fromMyIpJson,
|
||||
'https://ipapi.co/json': IpInfo.fromIpApiCoJson,
|
||||
'https://ident.me/json': IpInfo.fromIdentMeJson,
|
||||
'http://ip-api.com/json': IpInfo.fromIpAPIJson,
|
||||
'https://api.ip.sb/geoip': IpInfo.fromIpSbJson,
|
||||
'https://ipinfo.io/json': IpInfo.fromIpInfoIoJson,
|
||||
'https://ipwho.is/': IpInfo.fromIpWhoIsJson,
|
||||
'https://api.myip.com/': IpInfo.fromMyIpJson,
|
||||
'https://ipapi.co/json/': IpInfo.fromIpApiCoJson,
|
||||
'https://ident.me/json/': IpInfo.fromIdentMeJson,
|
||||
'http://ip-api.com/json/': IpInfo.fromIpAPIJson,
|
||||
'https://api.ip.sb/geoip/': IpInfo.fromIpSbJson,
|
||||
'https://ipinfo.io/json/': IpInfo.fromIpInfoIoJson,
|
||||
};
|
||||
|
||||
Future<Result<IpInfo?>> checkIp({CancelToken? cancelToken}) async {
|
||||
@@ -92,13 +92,11 @@ class Request {
|
||||
}
|
||||
}
|
||||
|
||||
final future = dio
|
||||
.get<Map<String, dynamic>>(
|
||||
source.key,
|
||||
cancelToken: cancelToken,
|
||||
options: Options(responseType: ResponseType.json),
|
||||
)
|
||||
.timeout(const Duration(seconds: 10));
|
||||
final future = dio.get<Map<String, dynamic>>(
|
||||
source.key,
|
||||
cancelToken: cancelToken,
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
future
|
||||
.then((res) {
|
||||
if (res.statusCode == HttpStatus.ok && res.data != null) {
|
||||
|
||||
@@ -11,15 +11,16 @@ extension StringExtension on String {
|
||||
}
|
||||
|
||||
dynamic get splitByMultipleSeparators {
|
||||
final parts = split(
|
||||
RegExp(r'[, ;]+'),
|
||||
).where((part) => part.isNotEmpty).toList();
|
||||
final parts =
|
||||
split(RegExp(r'[, ;]+')).where((part) => part.isNotEmpty).toList();
|
||||
|
||||
return parts.length > 1 ? parts : this;
|
||||
}
|
||||
|
||||
int compareToLower(String other) {
|
||||
return toLowerCase().compareTo(other.toLowerCase());
|
||||
return toLowerCase().compareTo(
|
||||
other.toLowerCase(),
|
||||
);
|
||||
}
|
||||
|
||||
List<int> get encodeUtf16LeWithBom {
|
||||
@@ -65,9 +66,9 @@ extension StringExtension on String {
|
||||
return md5.convert(bytes).toString();
|
||||
}
|
||||
|
||||
// bool containsToLower(String target) {
|
||||
// return toLowerCase().contains(target);
|
||||
// }
|
||||
// bool containsToLower(String target) {
|
||||
// return toLowerCase().contains(target);
|
||||
// }
|
||||
}
|
||||
|
||||
extension StringExtensionSafe on String? {
|
||||
|
||||
@@ -181,10 +181,7 @@ class Windows {
|
||||
calloc.free(argumentsPtr);
|
||||
calloc.free(operationPtr);
|
||||
|
||||
commonPrint.log(
|
||||
'windows runas: $command $arguments resultCode:$result',
|
||||
logLevel: LogLevel.warning,
|
||||
);
|
||||
commonPrint.log('windows runas: $command $arguments resultCode:$result');
|
||||
|
||||
if (result < 42) {
|
||||
return false;
|
||||
|
||||
@@ -353,7 +353,7 @@ class AppController {
|
||||
try {
|
||||
await updateProfile(profile);
|
||||
} catch (e) {
|
||||
commonPrint.log(e.toString(), logLevel: LogLevel.warning);
|
||||
commonPrint.log(e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -529,7 +529,6 @@ class AppController {
|
||||
FlutterError.onError = (details) {
|
||||
commonPrint.log(
|
||||
'exception: ${details.exception} stack: ${details.stack}',
|
||||
logLevel: LogLevel.warning,
|
||||
);
|
||||
};
|
||||
updateTray(true);
|
||||
@@ -957,7 +956,7 @@ class AppController {
|
||||
final res = await futureFunction();
|
||||
return res;
|
||||
} catch (e) {
|
||||
commonPrint.log('$futureFunction ===> $e', logLevel: LogLevel.warning);
|
||||
commonPrint.log('$futureFunction ===> $e');
|
||||
if (realSilence) {
|
||||
globalState.showNotifier(e.toString());
|
||||
} else {
|
||||
|
||||
@@ -52,7 +52,7 @@ class CoreService extends CoreHandlerInterface {
|
||||
}
|
||||
},
|
||||
(error, stack) async {
|
||||
commonPrint.log('Service error: $error', logLevel: LogLevel.warning);
|
||||
commonPrint.log('Service error: $error');
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -97,7 +97,7 @@ class CoreService extends CoreHandlerInterface {
|
||||
_process?.stderr.listen((e) {
|
||||
final error = utf8.decode(e);
|
||||
if (error.isNotEmpty) {
|
||||
commonPrint.log(error, logLevel: LogLevel.warning);
|
||||
commonPrint.log(error);
|
||||
}
|
||||
});
|
||||
await _socketCompleter.future;
|
||||
|
||||
@@ -101,7 +101,7 @@ class MessageManagerState extends State<MessageManager> {
|
||||
_cancelMessage(messages.last.id);
|
||||
},
|
||||
child: Card(
|
||||
shape: const RoundedSuperellipseBorder(
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(14),
|
||||
),
|
||||
@@ -127,7 +127,6 @@ class MessageManagerState extends State<MessageManager> {
|
||||
),
|
||||
SizedBox(width: 16),
|
||||
IconButton(
|
||||
padding: EdgeInsets.all(2),
|
||||
visualDensity: VisualDensity.compact,
|
||||
onPressed: () {
|
||||
_cancelMessage(messages.last.id);
|
||||
|
||||
@@ -87,23 +87,13 @@ class ThemeManager extends ConsumerWidget {
|
||||
top: padding.top > height * 0.3 ? 20.0 : padding.top,
|
||||
),
|
||||
),
|
||||
child: Theme(
|
||||
data: Theme.of(context).copyWith(
|
||||
floatingActionButtonTheme: Theme.of(context).floatingActionButtonTheme
|
||||
.copyWith(
|
||||
shape: const RoundedSuperellipseBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(16.0)),
|
||||
),
|
||||
),
|
||||
),
|
||||
child: LayoutBuilder(
|
||||
builder: (_, container) {
|
||||
globalState.appController.updateViewSize(
|
||||
Size(container.maxWidth, container.maxHeight),
|
||||
);
|
||||
return _buildSystemUi(child);
|
||||
},
|
||||
),
|
||||
child: LayoutBuilder(
|
||||
builder: (_, container) {
|
||||
globalState.appController.updateViewSize(
|
||||
Size(container.maxWidth, container.maxHeight),
|
||||
);
|
||||
return _buildSystemUi(child);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -271,11 +271,9 @@ class AppIcon extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
decoration: ShapeDecoration(
|
||||
decoration: BoxDecoration(
|
||||
color: context.colorScheme.surfaceContainerHighest,
|
||||
shape: RoundedSuperellipseBorder(
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
),
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
),
|
||||
padding: EdgeInsets.all(8),
|
||||
child: Transform.translate(
|
||||
|
||||
@@ -169,8 +169,8 @@ class ScannerOverlay extends CustomPainter {
|
||||
final backgroundPath = Path()..addRect(Rect.largest);
|
||||
|
||||
final cutoutPath = Path()
|
||||
..addRSuperellipse(
|
||||
RSuperellipse.fromRectAndCorners(
|
||||
..addRRect(
|
||||
RRect.fromRectAndCorners(
|
||||
scanWindow,
|
||||
topLeft: Radius.circular(borderRadius),
|
||||
topRight: Radius.circular(borderRadius),
|
||||
@@ -195,7 +195,7 @@ class ScannerOverlay extends CustomPainter {
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = 4.0;
|
||||
|
||||
final border = RSuperellipse.fromRectAndCorners(
|
||||
final borderRect = RRect.fromRectAndCorners(
|
||||
scanWindow,
|
||||
topLeft: Radius.circular(borderRadius),
|
||||
topRight: Radius.circular(borderRadius),
|
||||
@@ -204,7 +204,7 @@ class ScannerOverlay extends CustomPainter {
|
||||
);
|
||||
|
||||
canvas.drawPath(backgroundWithCutout, backgroundPaint);
|
||||
canvas.drawRSuperellipse(border, borderPaint);
|
||||
canvas.drawRRect(borderRect, borderPaint);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -74,10 +74,9 @@ class _StartButtonState extends ConsumerState<StartButton>
|
||||
}
|
||||
return Theme(
|
||||
data: Theme.of(context).copyWith(
|
||||
floatingActionButtonTheme: Theme.of(context).floatingActionButtonTheme
|
||||
.copyWith(
|
||||
sizeConstraints: BoxConstraints(minWidth: 56, maxWidth: 200),
|
||||
),
|
||||
floatingActionButtonTheme: FloatingActionButtonThemeData(
|
||||
sizeConstraints: BoxConstraints(minWidth: 56, maxWidth: 200),
|
||||
),
|
||||
),
|
||||
child: AnimatedBuilder(
|
||||
animation: _controller.view,
|
||||
|
||||
@@ -128,11 +128,10 @@ class TrafficUsage extends StatelessWidget {
|
||||
Container(
|
||||
width: 20,
|
||||
height: 8,
|
||||
decoration: ShapeDecoration(
|
||||
decoration: BoxDecoration(
|
||||
color: primaryColor,
|
||||
shape: RoundedSuperellipseBorder(
|
||||
borderRadius:
|
||||
BorderRadius.circular(3),
|
||||
borderRadius: BorderRadius.circular(
|
||||
3,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -152,11 +151,10 @@ class TrafficUsage extends StatelessWidget {
|
||||
Container(
|
||||
width: 20,
|
||||
height: 8,
|
||||
decoration: ShapeDecoration(
|
||||
decoration: BoxDecoration(
|
||||
color: secondaryColor,
|
||||
shape: RoundedSuperellipseBorder(
|
||||
borderRadius:
|
||||
BorderRadius.circular(3),
|
||||
borderRadius: BorderRadius.circular(
|
||||
3,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/core/controller.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/common.dart';
|
||||
import 'package:fl_clash/providers/app.dart';
|
||||
import 'package:fl_clash/providers/config.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:fl_clash/widgets/widgets.dart';
|
||||
@@ -18,14 +19,12 @@ class DeveloperView extends ConsumerWidget {
|
||||
items: [
|
||||
ListItem(
|
||||
title: Text(appLocalizations.messageTest),
|
||||
minVerticalPadding: 14,
|
||||
onTap: () {
|
||||
context.showNotifier(appLocalizations.messageTestTip);
|
||||
},
|
||||
),
|
||||
ListItem(
|
||||
title: Text(appLocalizations.logsTest),
|
||||
minVerticalPadding: 14,
|
||||
onTap: () {
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
globalState.appController.addLog(
|
||||
@@ -38,7 +37,6 @@ class DeveloperView extends ConsumerWidget {
|
||||
),
|
||||
ListItem(
|
||||
title: Text(appLocalizations.crashTest),
|
||||
minVerticalPadding: 14,
|
||||
onTap: () {
|
||||
if (kDebugMode) {
|
||||
coreController.crash();
|
||||
@@ -47,20 +45,18 @@ class DeveloperView extends ConsumerWidget {
|
||||
),
|
||||
ListItem(
|
||||
title: Text(appLocalizations.clearData),
|
||||
minVerticalPadding: 14,
|
||||
onTap: () async {
|
||||
await globalState.appController.handleClear();
|
||||
},
|
||||
),
|
||||
// ListItem(
|
||||
// title: Text('Loading'),
|
||||
// minVerticalPadding: 14,
|
||||
// onTap: () {
|
||||
// ref.read(loadingProvider.notifier).value = !ref.read(
|
||||
// loadingProvider,
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
ListItem(
|
||||
title: Text('Loading'),
|
||||
onTap: () {
|
||||
ref.read(loadingProvider.notifier).value = !ref.read(
|
||||
loadingProvider,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -360,7 +360,7 @@ class DelayTestButton extends StatefulWidget {
|
||||
class _DelayTestButtonState extends State<DelayTestButton>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late AnimationController _controller;
|
||||
late Animation<double> _animation;
|
||||
late Animation<double> _scale;
|
||||
|
||||
Future<void> _healthcheck() async {
|
||||
if (_controller.isAnimating) {
|
||||
@@ -378,10 +378,10 @@ class _DelayTestButtonState extends State<DelayTestButton>
|
||||
super.initState();
|
||||
_controller = AnimationController(
|
||||
vsync: this,
|
||||
duration: const Duration(milliseconds: 400),
|
||||
duration: const Duration(milliseconds: 200),
|
||||
);
|
||||
_animation = Tween<double>(begin: 1.0, end: 0.0).animate(
|
||||
CurvedAnimation(parent: _controller, curve: Curves.easeInOutBack),
|
||||
_scale = Tween<double>(begin: 1.0, end: 0.0).animate(
|
||||
CurvedAnimation(parent: _controller, curve: const Interval(0, 1)),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -399,10 +399,7 @@ class _DelayTestButtonState extends State<DelayTestButton>
|
||||
return SizedBox(
|
||||
width: 56,
|
||||
height: 56,
|
||||
child: FadeTransition(
|
||||
opacity: _animation,
|
||||
child: ScaleTransition(scale: _animation, child: child),
|
||||
),
|
||||
child: Transform.scale(scale: _scale.value, child: child),
|
||||
);
|
||||
},
|
||||
child: FloatingActionButton(
|
||||
|
||||
@@ -177,9 +177,7 @@ class CommonCard extends StatelessWidget {
|
||||
style: ButtonStyle(
|
||||
padding: const WidgetStatePropertyAll(EdgeInsets.zero),
|
||||
shape: WidgetStatePropertyAll(
|
||||
RoundedSuperellipseBorder(
|
||||
borderRadius: BorderRadius.circular(radius),
|
||||
),
|
||||
RoundedRectangleBorder(borderRadius: BorderRadius.circular(radius)),
|
||||
),
|
||||
iconColor: WidgetStatePropertyAll(context.colorScheme.primary),
|
||||
iconSize: WidgetStateProperty.all(20),
|
||||
|
||||
@@ -37,7 +37,7 @@ class ColorSchemeBox extends StatelessWidget {
|
||||
),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: ClipRSuperellipse(
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(36),
|
||||
child: SizedBox(
|
||||
width: 72,
|
||||
@@ -47,16 +47,22 @@ class ColorSchemeBox extends StatelessWidget {
|
||||
children: [
|
||||
GridItem(
|
||||
mainAxisCellCount: 2,
|
||||
child: Container(color: colorScheme.primary),
|
||||
child: Container(
|
||||
color: colorScheme.primary,
|
||||
),
|
||||
),
|
||||
GridItem(
|
||||
mainAxisCellCount: 1,
|
||||
child: Container(color: colorScheme.secondary),
|
||||
child: Container(
|
||||
color: colorScheme.secondary,
|
||||
),
|
||||
),
|
||||
GridItem(
|
||||
mainAxisCellCount: 1,
|
||||
child: Container(color: colorScheme.tertiary),
|
||||
),
|
||||
child: Container(
|
||||
color: colorScheme.tertiary,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -67,8 +73,11 @@ class ColorSchemeBox extends StatelessWidget {
|
||||
const Positioned(
|
||||
bottom: 4,
|
||||
right: 4,
|
||||
child: Icon(Icons.colorize, size: 20),
|
||||
),
|
||||
child: Icon(
|
||||
Icons.colorize,
|
||||
size: 20,
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
@@ -103,7 +112,9 @@ class PrimaryColorBox extends ConsumerWidget {
|
||||
),
|
||||
);
|
||||
return Theme(
|
||||
data: themeData.copyWith(colorScheme: colorScheme),
|
||||
data: themeData.copyWith(
|
||||
colorScheme: colorScheme,
|
||||
),
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -18,21 +18,30 @@ class RadioDelegate<T> extends Delegate {
|
||||
final T value;
|
||||
final void Function()? onTab;
|
||||
|
||||
const RadioDelegate({required this.value, this.onTab});
|
||||
const RadioDelegate({
|
||||
required this.value,
|
||||
this.onTab,
|
||||
});
|
||||
}
|
||||
|
||||
class SwitchDelegate<T> extends Delegate {
|
||||
final bool value;
|
||||
final ValueChanged<bool>? onChanged;
|
||||
|
||||
const SwitchDelegate({required this.value, this.onChanged});
|
||||
const SwitchDelegate({
|
||||
required this.value,
|
||||
this.onChanged,
|
||||
});
|
||||
}
|
||||
|
||||
class CheckboxDelegate<T> extends Delegate {
|
||||
final bool value;
|
||||
final ValueChanged<bool?>? onChanged;
|
||||
|
||||
const CheckboxDelegate({this.value = false, this.onChanged});
|
||||
const CheckboxDelegate({
|
||||
this.value = false,
|
||||
this.onChanged,
|
||||
});
|
||||
}
|
||||
|
||||
class OpenDelegate extends Delegate {
|
||||
@@ -120,7 +129,6 @@ class ListItem<T> extends StatelessWidget {
|
||||
final double? horizontalTitleGap;
|
||||
final TextStyle? titleTextStyle;
|
||||
final TextStyle? subtitleTextStyle;
|
||||
final double minVerticalPadding;
|
||||
final void Function()? onTap;
|
||||
|
||||
const ListItem({
|
||||
@@ -135,7 +143,6 @@ class ListItem<T> extends StatelessWidget {
|
||||
this.onTap,
|
||||
this.titleTextStyle,
|
||||
this.subtitleTextStyle,
|
||||
this.minVerticalPadding = 12,
|
||||
this.tileTitleAlignment = ListTileTitleAlignment.center,
|
||||
}) : delegate = const Delegate();
|
||||
|
||||
@@ -151,7 +158,6 @@ class ListItem<T> extends StatelessWidget {
|
||||
this.dense,
|
||||
this.titleTextStyle,
|
||||
this.subtitleTextStyle,
|
||||
this.minVerticalPadding = 12,
|
||||
this.tileTitleAlignment = ListTileTitleAlignment.center,
|
||||
}) : onTap = null;
|
||||
|
||||
@@ -167,7 +173,6 @@ class ListItem<T> extends StatelessWidget {
|
||||
this.dense,
|
||||
this.titleTextStyle,
|
||||
this.subtitleTextStyle,
|
||||
this.minVerticalPadding = 12,
|
||||
this.tileTitleAlignment = ListTileTitleAlignment.center,
|
||||
}) : onTap = null;
|
||||
|
||||
@@ -183,7 +188,6 @@ class ListItem<T> extends StatelessWidget {
|
||||
this.dense,
|
||||
this.titleTextStyle,
|
||||
this.subtitleTextStyle,
|
||||
this.minVerticalPadding = 12,
|
||||
this.tileTitleAlignment = ListTileTitleAlignment.center,
|
||||
}) : onTap = null;
|
||||
|
||||
@@ -199,7 +203,6 @@ class ListItem<T> extends StatelessWidget {
|
||||
this.dense,
|
||||
this.titleTextStyle,
|
||||
this.subtitleTextStyle,
|
||||
this.minVerticalPadding = 12,
|
||||
this.tileTitleAlignment = ListTileTitleAlignment.center,
|
||||
}) : onTap = null;
|
||||
|
||||
@@ -214,10 +217,9 @@ class ListItem<T> extends StatelessWidget {
|
||||
this.dense,
|
||||
this.titleTextStyle,
|
||||
this.subtitleTextStyle,
|
||||
this.minVerticalPadding = 12,
|
||||
this.tileTitleAlignment = ListTileTitleAlignment.center,
|
||||
}) : trailing = null,
|
||||
onTap = null;
|
||||
}) : trailing = null,
|
||||
onTap = null;
|
||||
|
||||
const ListItem.switchItem({
|
||||
super.key,
|
||||
@@ -230,10 +232,9 @@ class ListItem<T> extends StatelessWidget {
|
||||
this.dense,
|
||||
this.titleTextStyle,
|
||||
this.subtitleTextStyle,
|
||||
this.minVerticalPadding = 12,
|
||||
this.tileTitleAlignment = ListTileTitleAlignment.center,
|
||||
}) : trailing = null,
|
||||
onTap = null;
|
||||
}) : trailing = null,
|
||||
onTap = null;
|
||||
|
||||
const ListItem.radio({
|
||||
super.key,
|
||||
@@ -246,10 +247,9 @@ class ListItem<T> extends StatelessWidget {
|
||||
this.dense,
|
||||
this.titleTextStyle,
|
||||
this.subtitleTextStyle,
|
||||
this.minVerticalPadding = 12,
|
||||
this.tileTitleAlignment = ListTileTitleAlignment.center,
|
||||
}) : leading = null,
|
||||
onTap = null;
|
||||
}) : leading = null,
|
||||
onTap = null;
|
||||
|
||||
Widget _buildListTile({
|
||||
void Function()? onTap,
|
||||
@@ -264,7 +264,7 @@ class ListItem<T> extends StatelessWidget {
|
||||
leading: leading ?? this.leading,
|
||||
horizontalTitleGap: horizontalTitleGap,
|
||||
title: title,
|
||||
minVerticalPadding: minVerticalPadding,
|
||||
minVerticalPadding: 12,
|
||||
subtitle: subtitle,
|
||||
titleAlignment: tileTitleAlignment,
|
||||
onTap: onTap,
|
||||
@@ -306,7 +306,9 @@ class ListItem<T> extends StatelessWidget {
|
||||
action();
|
||||
}
|
||||
|
||||
return _buildListTile(onTap: openAction);
|
||||
return _buildListTile(
|
||||
onTap: openAction,
|
||||
);
|
||||
},
|
||||
openBuilder: (_, action) {
|
||||
return openDelegate.wrap
|
||||
@@ -420,7 +422,9 @@ class ListItem<T> extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
return _buildListTile(onTap: onTap);
|
||||
return _buildListTile(
|
||||
onTap: onTap,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -444,9 +448,13 @@ class ListHeader extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
alignment: Alignment.centerLeft,
|
||||
padding:
|
||||
padding ??
|
||||
const EdgeInsets.only(left: 16, right: 8, top: 24, bottom: 8),
|
||||
padding: padding ??
|
||||
const EdgeInsets.only(
|
||||
left: 16,
|
||||
right: 8,
|
||||
top: 24,
|
||||
bottom: 8,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
@@ -458,18 +466,19 @@ class ListHeader extends StatelessWidget {
|
||||
Text(
|
||||
title,
|
||||
style: Theme.of(context).textTheme.labelLarge?.copyWith(
|
||||
color: Theme.of(
|
||||
context,
|
||||
).colorScheme.onSurfaceVariant.opacity80,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurfaceVariant
|
||||
.opacity80,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
if (subTitle != null)
|
||||
Text(
|
||||
subTitle!,
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
),
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -477,7 +486,12 @@ class ListHeader extends StatelessWidget {
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [...genActions(actions, space: space)],
|
||||
children: [
|
||||
...genActions(
|
||||
actions,
|
||||
space: space,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -492,11 +506,18 @@ List<Widget> generateSection({
|
||||
bool separated = true,
|
||||
}) {
|
||||
final genItems = separated
|
||||
? items.separated(const Divider(height: 0))
|
||||
? items.separated(
|
||||
const Divider(
|
||||
height: 0,
|
||||
),
|
||||
)
|
||||
: items;
|
||||
return [
|
||||
if (items.isNotEmpty && title != null)
|
||||
ListHeader(title: title, actions: actions),
|
||||
ListHeader(
|
||||
title: title,
|
||||
actions: actions,
|
||||
),
|
||||
...genItems,
|
||||
];
|
||||
}
|
||||
@@ -507,26 +528,22 @@ Widget generateSectionV2({
|
||||
List<Widget>? actions,
|
||||
bool separated = true,
|
||||
}) {
|
||||
final genItems = items
|
||||
.map<Widget>((item) {
|
||||
return ClipRSuperellipse(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
child: CommonCard(
|
||||
type: CommonCardType.filled,
|
||||
radius: 0,
|
||||
child: item,
|
||||
),
|
||||
);
|
||||
})
|
||||
.separated(const Divider(height: 2, color: Colors.transparent));
|
||||
return Column(
|
||||
children: [
|
||||
if (items.isNotEmpty && title != null)
|
||||
ListHeader(title: title, actions: actions),
|
||||
ClipRSuperellipse(
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
child: Column(children: [...genItems]),
|
||||
),
|
||||
ListHeader(
|
||||
title: title,
|
||||
actions: actions,
|
||||
),
|
||||
CommonCard(
|
||||
radius: 18,
|
||||
type: CommonCardType.filled,
|
||||
child: Column(
|
||||
children: [
|
||||
...items,
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
@@ -538,10 +555,18 @@ List<Widget> generateInfoSection({
|
||||
bool separated = true,
|
||||
}) {
|
||||
final genItems = separated
|
||||
? items.separated(const Divider(height: 0))
|
||||
? items.separated(
|
||||
const Divider(
|
||||
height: 0,
|
||||
),
|
||||
)
|
||||
: items;
|
||||
return [
|
||||
if (items.isNotEmpty) InfoHeader(info: info, actions: actions),
|
||||
if (items.isNotEmpty)
|
||||
InfoHeader(
|
||||
info: info,
|
||||
actions: actions,
|
||||
),
|
||||
...genItems,
|
||||
];
|
||||
}
|
||||
@@ -550,6 +575,8 @@ Widget generateListView(List<Widget> items) {
|
||||
return ListView.builder(
|
||||
itemCount: items.length,
|
||||
itemBuilder: (_, index) => items[index],
|
||||
padding: const EdgeInsets.only(bottom: 16),
|
||||
padding: const EdgeInsets.only(
|
||||
bottom: 16,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -243,7 +243,7 @@ class _ShadePainter extends CustomPainter {
|
||||
effectiveSquareRadius * 2,
|
||||
effectiveSquareRadius * 2,
|
||||
);
|
||||
final RSuperellipse rSuperellipse = RSuperellipse.fromRectAndRadius(
|
||||
final RRect rRect = RRect.fromRectAndRadius(
|
||||
rectBox,
|
||||
Radius.circular(trackBorderRadius),
|
||||
);
|
||||
@@ -254,8 +254,8 @@ class _ShadePainter extends CustomPainter {
|
||||
HSVColor.fromAHSV(1, colorHue, 1, 1).toColor(),
|
||||
],
|
||||
).createShader(rectBox);
|
||||
canvas.drawRSuperellipse(
|
||||
rSuperellipse,
|
||||
canvas.drawRRect(
|
||||
rRect,
|
||||
Paint()
|
||||
..style = PaintingStyle.fill
|
||||
..shader = horizontal,
|
||||
@@ -266,8 +266,8 @@ class _ShadePainter extends CustomPainter {
|
||||
end: Alignment.bottomCenter,
|
||||
colors: <Color>[Colors.transparent, Colors.black],
|
||||
).createShader(rectBox);
|
||||
canvas.drawRSuperellipse(
|
||||
rSuperellipse,
|
||||
canvas.drawRRect(
|
||||
rRect,
|
||||
Paint()
|
||||
..style = PaintingStyle.fill
|
||||
..shader = vertical,
|
||||
|
||||
@@ -38,9 +38,10 @@ class CommonPopupRoute<T> extends PopupRoute<T> {
|
||||
Widget child,
|
||||
) {
|
||||
final align = Alignment.topRight;
|
||||
final curveAnimation = animation
|
||||
.drive(Tween(begin: 0.0, end: 1.0))
|
||||
.drive(CurveTween(curve: Curves.easeOutBack));
|
||||
final animationValue = CurvedAnimation(
|
||||
parent: animation,
|
||||
curve: Curves.easeIn,
|
||||
).value;
|
||||
return SafeArea(
|
||||
child: ValueListenableBuilder(
|
||||
valueListenable: offsetNotifier,
|
||||
@@ -57,17 +58,15 @@ class CommonPopupRoute<T> extends PopupRoute<T> {
|
||||
},
|
||||
child: AnimatedBuilder(
|
||||
animation: animation,
|
||||
builder: (_, child) {
|
||||
return FadeTransition(
|
||||
opacity: curveAnimation,
|
||||
child: ScaleTransition(
|
||||
builder: (_, Widget? child) {
|
||||
return Opacity(
|
||||
opacity: 0.1 + 0.9 * animationValue,
|
||||
child: Transform.scale(
|
||||
alignment: align,
|
||||
scale: curveAnimation,
|
||||
child: SlideTransition(
|
||||
position: curveAnimation.drive(
|
||||
Tween(begin: const Offset(0, -0.02), end: Offset.zero),
|
||||
),
|
||||
child: child,
|
||||
scale: 0.7 + 0.3 * animationValue,
|
||||
child: Transform.translate(
|
||||
offset: Offset(0, -10) * (1 - animationValue),
|
||||
child: child!,
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -79,7 +78,7 @@ class CommonPopupRoute<T> extends PopupRoute<T> {
|
||||
}
|
||||
|
||||
@override
|
||||
Duration get transitionDuration => const Duration(milliseconds: 250);
|
||||
Duration get transitionDuration => const Duration(milliseconds: 150);
|
||||
}
|
||||
|
||||
class PopupController extends ValueNotifier<bool> {
|
||||
@@ -271,8 +270,8 @@ class CommonPopupMenu extends StatelessWidget {
|
||||
elevation: 12,
|
||||
color: context.colorScheme.surfaceContainer,
|
||||
clipBehavior: Clip.antiAlias,
|
||||
shape: RoundedSuperellipseBorder(
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
|
||||
@@ -39,7 +39,11 @@ class ExtendProps {
|
||||
});
|
||||
}
|
||||
|
||||
enum SheetType { page, bottomSheet, sideSheet }
|
||||
enum SheetType {
|
||||
page,
|
||||
bottomSheet,
|
||||
sideSheet,
|
||||
}
|
||||
|
||||
typedef SheetBuilder = Widget Function(BuildContext context, SheetType type);
|
||||
|
||||
@@ -51,24 +55,28 @@ Future<T?> showSheet<T>({
|
||||
final isMobile = globalState.appState.viewMode == ViewMode.mobile;
|
||||
return switch (isMobile) {
|
||||
true => showModalBottomSheet<T>(
|
||||
context: context,
|
||||
isScrollControlled: props.isScrollControlled,
|
||||
builder: (_) {
|
||||
return SafeArea(child: builder(context, SheetType.bottomSheet));
|
||||
},
|
||||
showDragHandle: false,
|
||||
useSafeArea: props.useSafeArea,
|
||||
),
|
||||
context: context,
|
||||
isScrollControlled: props.isScrollControlled,
|
||||
builder: (_) {
|
||||
return SafeArea(
|
||||
child: builder(context, SheetType.bottomSheet),
|
||||
);
|
||||
},
|
||||
showDragHandle: false,
|
||||
useSafeArea: props.useSafeArea,
|
||||
),
|
||||
false => showModalSideSheet<T>(
|
||||
useSafeArea: props.useSafeArea,
|
||||
isScrollControlled: props.isScrollControlled,
|
||||
context: context,
|
||||
constraints: BoxConstraints(maxWidth: props.maxWidth ?? 360),
|
||||
filter: props.blur ? commonFilter : null,
|
||||
builder: (_) {
|
||||
return builder(context, SheetType.sideSheet);
|
||||
},
|
||||
),
|
||||
useSafeArea: props.useSafeArea,
|
||||
isScrollControlled: props.isScrollControlled,
|
||||
context: context,
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: props.maxWidth ?? 360,
|
||||
),
|
||||
filter: props.blur ? commonFilter : null,
|
||||
builder: (_) {
|
||||
return builder(context, SheetType.sideSheet);
|
||||
},
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -79,16 +87,21 @@ Future<T?> showExtend<T>(
|
||||
}) {
|
||||
final isMobile = globalState.appState.viewMode == ViewMode.mobile;
|
||||
return switch (isMobile || props.forceFull) {
|
||||
true => BaseNavigator.push(context, builder(context, SheetType.page)),
|
||||
true => BaseNavigator.push(
|
||||
context,
|
||||
builder(context, SheetType.page),
|
||||
),
|
||||
false => showModalSideSheet<T>(
|
||||
useSafeArea: props.useSafeArea,
|
||||
context: context,
|
||||
constraints: BoxConstraints(maxWidth: props.maxWidth ?? 360),
|
||||
filter: props.blur ? commonFilter : null,
|
||||
builder: (context) {
|
||||
return builder(context, SheetType.sideSheet);
|
||||
},
|
||||
),
|
||||
useSafeArea: props.useSafeArea,
|
||||
context: context,
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: props.maxWidth ?? 360,
|
||||
),
|
||||
filter: props.blur ? commonFilter : null,
|
||||
builder: (context) {
|
||||
return builder(context, SheetType.sideSheet);
|
||||
},
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -121,11 +134,13 @@ class _AdaptiveSheetScaffoldState extends State<AdaptiveSheetScaffold> {
|
||||
automaticallyImplyLeading: bottomSheet
|
||||
? false
|
||||
: widget.actions.isEmpty && sideSheet
|
||||
? false
|
||||
: true,
|
||||
? false
|
||||
: true,
|
||||
centerTitle: bottomSheet,
|
||||
backgroundColor: backgroundColor,
|
||||
title: Text(widget.title),
|
||||
title: Text(
|
||||
widget.title,
|
||||
),
|
||||
actions: genActions([
|
||||
if (widget.actions.isEmpty && sideSheet) CloseButton(),
|
||||
...widget.actions,
|
||||
@@ -135,11 +150,9 @@ class _AdaptiveSheetScaffoldState extends State<AdaptiveSheetScaffold> {
|
||||
final handleSize = Size(32, 4);
|
||||
return Container(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: ShapeDecoration(
|
||||
decoration: BoxDecoration(
|
||||
color: backgroundColor,
|
||||
shape: RoundedSuperellipseBorder(
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(28.0)),
|
||||
),
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(28.0)),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@@ -150,16 +163,17 @@ class _AdaptiveSheetScaffoldState extends State<AdaptiveSheetScaffold> {
|
||||
alignment: Alignment.center,
|
||||
height: handleSize.height,
|
||||
width: handleSize.width,
|
||||
decoration: ShapeDecoration(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(handleSize.height / 2),
|
||||
color: context.colorScheme.onSurfaceVariant,
|
||||
shape: RoundedSuperellipseBorder(
|
||||
borderRadius: BorderRadius.circular(handleSize.height / 2),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
appBar,
|
||||
Flexible(flex: 1, child: widget.body),
|
||||
Flexible(
|
||||
flex: 1,
|
||||
child: widget.body,
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -82,12 +82,12 @@ class _SideSheetState extends State<SideSheet> {
|
||||
final Color surfaceTintColor = colorScheme.surfaceTint;
|
||||
final Color shadowColor = widget.shadowColor ?? Colors.transparent;
|
||||
final double elevation = widget.elevation ?? 0;
|
||||
final ShapeBorder shape =
|
||||
widget.shape ??
|
||||
RoundedSuperellipseBorder(borderRadius: BorderRadius.circular(0));
|
||||
final ShapeBorder shape = widget.shape ??
|
||||
RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(0),
|
||||
);
|
||||
|
||||
final BoxConstraints constraints =
|
||||
widget.constraints ??
|
||||
final BoxConstraints constraints = widget.constraints ??
|
||||
const BoxConstraints(maxWidth: 320, minWidth: 320);
|
||||
|
||||
final Clip clipBehavior = widget.clipBehavior ?? Clip.none;
|
||||
@@ -103,7 +103,10 @@ class _SideSheetState extends State<SideSheet> {
|
||||
child: widget.builder(context),
|
||||
);
|
||||
|
||||
return ConstrainedBox(constraints: constraints, child: sideSheet);
|
||||
return ConstrainedBox(
|
||||
constraints: constraints,
|
||||
child: sideSheet,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,8 +128,7 @@ class _SideSheetLayoutWithSizeListener extends SingleChildRenderObjectWidget {
|
||||
|
||||
@override
|
||||
_RenderSideSheetLayoutWithSizeListener createRenderObject(
|
||||
BuildContext context,
|
||||
) {
|
||||
BuildContext context) {
|
||||
return _RenderSideSheetLayoutWithSizeListener(
|
||||
onChildSizeChanged: onChildSizeChanged,
|
||||
animationValue: animationValue,
|
||||
@@ -136,10 +138,8 @@ class _SideSheetLayoutWithSizeListener extends SingleChildRenderObjectWidget {
|
||||
}
|
||||
|
||||
@override
|
||||
void updateRenderObject(
|
||||
BuildContext context,
|
||||
_RenderSideSheetLayoutWithSizeListener renderObject,
|
||||
) {
|
||||
void updateRenderObject(BuildContext context,
|
||||
_RenderSideSheetLayoutWithSizeListener renderObject) {
|
||||
renderObject.onChildSizeChanged = onChildSizeChanged;
|
||||
renderObject.animationValue = animationValue;
|
||||
renderObject.isScrollControlled = isScrollControlled;
|
||||
@@ -155,12 +155,12 @@ class _RenderSideSheetLayoutWithSizeListener extends RenderShiftedBox {
|
||||
required double animationValue,
|
||||
required bool isScrollControlled,
|
||||
required double scrollControlDisabledMaxHeightRatio,
|
||||
}) : _onChildSizeChanged = onChildSizeChanged,
|
||||
_animationValue = animationValue,
|
||||
_isScrollControlled = isScrollControlled,
|
||||
_scrollControlDisabledMaxHeightRatio =
|
||||
scrollControlDisabledMaxHeightRatio,
|
||||
super(child);
|
||||
}) : _onChildSizeChanged = onChildSizeChanged,
|
||||
_animationValue = animationValue,
|
||||
_isScrollControlled = isScrollControlled,
|
||||
_scrollControlDisabledMaxHeightRatio =
|
||||
scrollControlDisabledMaxHeightRatio,
|
||||
super(child);
|
||||
|
||||
Size _lastSize = Size.zero;
|
||||
|
||||
@@ -219,9 +219,8 @@ class _RenderSideSheetLayoutWithSizeListener extends RenderShiftedBox {
|
||||
|
||||
@override
|
||||
double computeMinIntrinsicWidth(double height) {
|
||||
final double width = _getSize(
|
||||
BoxConstraints.tightForFinite(height: height),
|
||||
).width;
|
||||
final double width =
|
||||
_getSize(BoxConstraints.tightForFinite(height: height)).width;
|
||||
if (width.isFinite) {
|
||||
return width;
|
||||
}
|
||||
@@ -230,9 +229,8 @@ class _RenderSideSheetLayoutWithSizeListener extends RenderShiftedBox {
|
||||
|
||||
@override
|
||||
double computeMaxIntrinsicWidth(double height) {
|
||||
final double width = _getSize(
|
||||
BoxConstraints.tightForFinite(height: height),
|
||||
).width;
|
||||
final double width =
|
||||
_getSize(BoxConstraints.tightForFinite(height: height)).width;
|
||||
if (width.isFinite) {
|
||||
return width;
|
||||
}
|
||||
@@ -241,9 +239,8 @@ class _RenderSideSheetLayoutWithSizeListener extends RenderShiftedBox {
|
||||
|
||||
@override
|
||||
double computeMinIntrinsicHeight(double width) {
|
||||
final double height = _getSize(
|
||||
BoxConstraints.tightForFinite(width: width),
|
||||
).height;
|
||||
final double height =
|
||||
_getSize(BoxConstraints.tightForFinite(width: width)).height;
|
||||
if (height.isFinite) {
|
||||
return height;
|
||||
}
|
||||
@@ -252,9 +249,8 @@ class _RenderSideSheetLayoutWithSizeListener extends RenderShiftedBox {
|
||||
|
||||
@override
|
||||
double computeMaxIntrinsicHeight(double width) {
|
||||
final double height = _getSize(
|
||||
BoxConstraints.tightForFinite(width: width),
|
||||
).height;
|
||||
final double height =
|
||||
_getSize(BoxConstraints.tightForFinite(width: width)).height;
|
||||
if (height.isFinite) {
|
||||
return height;
|
||||
}
|
||||
@@ -267,7 +263,9 @@ class _RenderSideSheetLayoutWithSizeListener extends RenderShiftedBox {
|
||||
}
|
||||
|
||||
BoxConstraints _getConstraintsForChild(BoxConstraints constraints) {
|
||||
return BoxConstraints(maxHeight: constraints.maxHeight);
|
||||
return BoxConstraints(
|
||||
maxHeight: constraints.maxHeight,
|
||||
);
|
||||
}
|
||||
|
||||
Offset _getPositionForChild(Size size, Size childSize) {
|
||||
@@ -278,9 +276,8 @@ class _RenderSideSheetLayoutWithSizeListener extends RenderShiftedBox {
|
||||
void performLayout() {
|
||||
size = _getSize(constraints);
|
||||
if (child != null) {
|
||||
final BoxConstraints childConstraints = _getConstraintsForChild(
|
||||
constraints,
|
||||
);
|
||||
final BoxConstraints childConstraints =
|
||||
_getConstraintsForChild(constraints);
|
||||
assert(childConstraints.debugAssertIsValid(isAppliedConstraint: true));
|
||||
child!.layout(
|
||||
childConstraints,
|
||||
@@ -291,9 +288,8 @@ class _RenderSideSheetLayoutWithSizeListener extends RenderShiftedBox {
|
||||
size,
|
||||
childConstraints.isTight ? childConstraints.smallest : child!.size,
|
||||
);
|
||||
final Size childSize = childConstraints.isTight
|
||||
? childConstraints.smallest
|
||||
: child!.size;
|
||||
final Size childSize =
|
||||
childConstraints.isTight ? childConstraints.smallest : child!.size;
|
||||
|
||||
if (_lastSize != childSize) {
|
||||
_lastSize = childSize;
|
||||
@@ -358,9 +354,8 @@ class _ModalSideSheetState<T> extends State<_ModalSideSheet<T>> {
|
||||
Widget build(BuildContext context) {
|
||||
assert(debugCheckHasMediaQuery(context));
|
||||
assert(debugCheckHasMaterialLocalizations(context));
|
||||
final MaterialLocalizations localizations = MaterialLocalizations.of(
|
||||
context,
|
||||
);
|
||||
final MaterialLocalizations localizations =
|
||||
MaterialLocalizations.of(context);
|
||||
final String routeLabel = _getRouteLabel(localizations);
|
||||
|
||||
return AnimatedBuilder(
|
||||
@@ -411,27 +406,26 @@ class _ModalSideSheetState<T> extends State<_ModalSideSheet<T>> {
|
||||
}
|
||||
|
||||
class ModalSideSheetRoute<T> extends PopupRoute<T> {
|
||||
ModalSideSheetRoute({
|
||||
required this.builder,
|
||||
this.capturedThemes,
|
||||
this.barrierLabel,
|
||||
this.barrierOnTapHint,
|
||||
this.backgroundColor,
|
||||
this.elevation,
|
||||
this.shape,
|
||||
this.clipBehavior,
|
||||
this.constraints,
|
||||
this.modalBarrierColor,
|
||||
this.isDismissible = true,
|
||||
this.isScrollControlled = false,
|
||||
this.scrollControlDisabledMaxHeightRatio =
|
||||
_defaultScrollControlDisabledMaxHeightRatio,
|
||||
super.settings,
|
||||
this.transitionAnimationController,
|
||||
this.anchorPoint,
|
||||
this.useSafeArea = false,
|
||||
super.filter,
|
||||
});
|
||||
ModalSideSheetRoute(
|
||||
{required this.builder,
|
||||
this.capturedThemes,
|
||||
this.barrierLabel,
|
||||
this.barrierOnTapHint,
|
||||
this.backgroundColor,
|
||||
this.elevation,
|
||||
this.shape,
|
||||
this.clipBehavior,
|
||||
this.constraints,
|
||||
this.modalBarrierColor,
|
||||
this.isDismissible = true,
|
||||
this.isScrollControlled = false,
|
||||
this.scrollControlDisabledMaxHeightRatio =
|
||||
_defaultScrollControlDisabledMaxHeightRatio,
|
||||
super.settings,
|
||||
this.transitionAnimationController,
|
||||
this.anchorPoint,
|
||||
this.useSafeArea = false,
|
||||
super.filter});
|
||||
|
||||
final WidgetBuilder builder;
|
||||
|
||||
@@ -510,11 +504,8 @@ class ModalSideSheetRoute<T> extends PopupRoute<T> {
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildPage(
|
||||
BuildContext context,
|
||||
Animation<double> animation,
|
||||
Animation<double> secondaryAnimation,
|
||||
) {
|
||||
Widget buildPage(BuildContext context, Animation<double> animation,
|
||||
Animation<double> secondaryAnimation) {
|
||||
final Widget content = DisplayFeatureSubScreen(
|
||||
anchorPoint: anchorPoint,
|
||||
child: Builder(
|
||||
@@ -548,7 +539,9 @@ class ModalSideSheetRoute<T> extends PopupRoute<T> {
|
||||
ColorTween(
|
||||
begin: barrierColor.opacity0,
|
||||
end: barrierColor,
|
||||
).chain(CurveTween(curve: barrierCurve)),
|
||||
).chain(
|
||||
CurveTween(curve: barrierCurve),
|
||||
),
|
||||
);
|
||||
return AnimatedModalBarrier(
|
||||
color: color,
|
||||
@@ -594,39 +587,32 @@ Future<T?> showModalSideSheet<T>({
|
||||
assert(debugCheckHasMediaQuery(context));
|
||||
assert(debugCheckHasMaterialLocalizations(context));
|
||||
|
||||
final NavigatorState navigator = Navigator.of(
|
||||
context,
|
||||
rootNavigator: useRootNavigator,
|
||||
);
|
||||
final NavigatorState navigator =
|
||||
Navigator.of(context, rootNavigator: useRootNavigator);
|
||||
final MaterialLocalizations localizations = MaterialLocalizations.of(context);
|
||||
return navigator.push(
|
||||
ModalSideSheetRoute<T>(
|
||||
builder: builder,
|
||||
filter: filter,
|
||||
capturedThemes: InheritedTheme.capture(
|
||||
from: context,
|
||||
to: navigator.context,
|
||||
),
|
||||
isScrollControlled: isScrollControlled,
|
||||
scrollControlDisabledMaxHeightRatio: scrollControlDisabledMaxHeightRatio,
|
||||
barrierLabel: barrierLabel ?? localizations.scrimLabel,
|
||||
barrierOnTapHint: localizations.scrimOnTapHint(
|
||||
localizations.bottomSheetLabel,
|
||||
),
|
||||
backgroundColor: backgroundColor,
|
||||
elevation: elevation,
|
||||
shape: shape,
|
||||
clipBehavior: clipBehavior,
|
||||
constraints: constraints,
|
||||
isDismissible: isDismissible,
|
||||
modalBarrierColor:
|
||||
barrierColor ?? Theme.of(context).bottomSheetTheme.modalBarrierColor,
|
||||
settings: routeSettings,
|
||||
transitionAnimationController: transitionAnimationController,
|
||||
anchorPoint: anchorPoint,
|
||||
useSafeArea: useSafeArea,
|
||||
),
|
||||
);
|
||||
return navigator.push(ModalSideSheetRoute<T>(
|
||||
builder: builder,
|
||||
filter: filter,
|
||||
capturedThemes:
|
||||
InheritedTheme.capture(from: context, to: navigator.context),
|
||||
isScrollControlled: isScrollControlled,
|
||||
scrollControlDisabledMaxHeightRatio: scrollControlDisabledMaxHeightRatio,
|
||||
barrierLabel: barrierLabel ?? localizations.scrimLabel,
|
||||
barrierOnTapHint:
|
||||
localizations.scrimOnTapHint(localizations.bottomSheetLabel),
|
||||
backgroundColor: backgroundColor,
|
||||
elevation: elevation,
|
||||
shape: shape,
|
||||
clipBehavior: clipBehavior,
|
||||
constraints: constraints,
|
||||
isDismissible: isDismissible,
|
||||
modalBarrierColor:
|
||||
barrierColor ?? Theme.of(context).bottomSheetTheme.modalBarrierColor,
|
||||
settings: routeSettings,
|
||||
transitionAnimationController: transitionAnimationController,
|
||||
anchorPoint: anchorPoint,
|
||||
useSafeArea: useSafeArea,
|
||||
));
|
||||
}
|
||||
|
||||
// class ModalAppBar extends StatelessWidget {
|
||||
|
||||
@@ -8,10 +8,8 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter/physics.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
|
||||
const EdgeInsetsGeometry _kHorizontalItemPadding = EdgeInsets.symmetric(
|
||||
vertical: 2,
|
||||
horizontal: 3,
|
||||
);
|
||||
const EdgeInsetsGeometry _kHorizontalItemPadding =
|
||||
EdgeInsets.symmetric(vertical: 2, horizontal: 3);
|
||||
|
||||
const Radius _kCornerRadius = Radius.circular(9);
|
||||
|
||||
@@ -65,11 +63,11 @@ class CommonTabBar<T extends Object> extends StatefulWidget {
|
||||
this.padding = _kHorizontalItemPadding,
|
||||
this.backgroundColor,
|
||||
this.proportionalWidth = false,
|
||||
}) : assert(children.length >= 2),
|
||||
assert(
|
||||
groupValue == null || children.keys.contains(groupValue),
|
||||
'The groupValue must be either null or one of the keys in the children map.',
|
||||
);
|
||||
}) : assert(children.length >= 2),
|
||||
assert(
|
||||
groupValue == null || children.keys.contains(groupValue),
|
||||
'The groupValue must be either null or one of the keys in the children map.',
|
||||
);
|
||||
final Map<T, Widget> children;
|
||||
final Set<T> disabledChildren;
|
||||
final T? groupValue;
|
||||
@@ -192,9 +190,8 @@ class _CommonTabBarState<T extends Object> extends State<CommonTabBar<T>>
|
||||
void _playThumbScaleAnimation({required bool isExpanding}) {
|
||||
thumbScaleAnimation = thumbScaleController.drive(
|
||||
Tween<double>(
|
||||
begin: thumbScaleAnimation.value,
|
||||
end: isExpanding ? 1 : _kMinThumbScale,
|
||||
),
|
||||
begin: thumbScaleAnimation.value,
|
||||
end: isExpanding ? 1 : _kMinThumbScale),
|
||||
);
|
||||
thumbScaleController.animateWith(_kThumbSpringAnimationSimulation);
|
||||
}
|
||||
@@ -234,9 +231,8 @@ class _CommonTabBarState<T extends Object> extends State<CommonTabBar<T>>
|
||||
void onDown(DragDownDetails details) {
|
||||
final T touchDownSegment = segmentForXPosition(details.localPosition.dx);
|
||||
_startedOnSelectedSegment = touchDownSegment == highlighted;
|
||||
_startedOnDisabledSegment = widget.disabledChildren.contains(
|
||||
touchDownSegment,
|
||||
);
|
||||
_startedOnDisabledSegment =
|
||||
widget.disabledChildren.contains(touchDownSegment);
|
||||
if (widget.disabledChildren.contains(touchDownSegment)) {
|
||||
return;
|
||||
}
|
||||
@@ -377,10 +373,8 @@ class _CommonTabBarState<T extends Object> extends State<CommonTabBar<T>>
|
||||
child: Container(
|
||||
clipBehavior: Clip.antiAlias,
|
||||
padding: widget.padding.resolve(Directionality.of(context)),
|
||||
decoration: ShapeDecoration(
|
||||
shape: RoundedSuperellipseBorder(
|
||||
borderRadius: const BorderRadius.all(_kCornerRadius),
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.all(_kCornerRadius),
|
||||
color: widget.backgroundColor,
|
||||
),
|
||||
child: AnimatedBuilder(
|
||||
@@ -461,9 +455,8 @@ class _SegmentState<T> extends State<_Segment<T>>
|
||||
end: widget.shouldScaleContent ? _kMinThumbScale : 1.0,
|
||||
),
|
||||
);
|
||||
highlightPressScaleController.animateWith(
|
||||
_kThumbSpringAnimationSimulation,
|
||||
);
|
||||
highlightPressScaleController
|
||||
.animateWith(_kThumbSpringAnimationSimulation);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -487,21 +480,20 @@ class _SegmentState<T> extends State<_Segment<T>>
|
||||
alignment: Alignment.center,
|
||||
children: <Widget>[
|
||||
AnimatedOpacity(
|
||||
opacity: widget.shouldFadeoutContent
|
||||
? _kContentPressedMinOpacity
|
||||
: 1,
|
||||
opacity:
|
||||
widget.shouldFadeoutContent ? _kContentPressedMinOpacity : 1,
|
||||
duration: _kOpacityAnimationDuration,
|
||||
curve: Curves.ease,
|
||||
child: AnimatedDefaultTextStyle(
|
||||
style: DefaultTextStyle.of(context).style.merge(
|
||||
TextStyle(
|
||||
fontWeight: widget.highlighted
|
||||
? _kHighlightedFontWeight
|
||||
: _kFontWeight,
|
||||
fontSize: _kFontSize,
|
||||
color: widget.enabled ? null : _kDisabledContentColor,
|
||||
),
|
||||
),
|
||||
TextStyle(
|
||||
fontWeight: widget.highlighted
|
||||
? _kHighlightedFontWeight
|
||||
: _kFontWeight,
|
||||
fontSize: _kFontSize,
|
||||
color: widget.enabled ? null : _kDisabledContentColor,
|
||||
),
|
||||
),
|
||||
duration: _kHighlightAnimationDuration,
|
||||
curve: Curves.ease,
|
||||
child: ScaleTransition(
|
||||
@@ -513,9 +505,7 @@ class _SegmentState<T> extends State<_Segment<T>>
|
||||
),
|
||||
DefaultTextStyle.merge(
|
||||
style: const TextStyle(
|
||||
fontWeight: _kHighlightedFontWeight,
|
||||
fontSize: _kFontSize,
|
||||
),
|
||||
fontWeight: _kHighlightedFontWeight, fontSize: _kFontSize),
|
||||
child: widget.child,
|
||||
),
|
||||
],
|
||||
@@ -580,7 +570,9 @@ class _SegmentSeparatorState extends State<_SegmentSeparator>
|
||||
return Padding(
|
||||
padding: _kSeparatorInset,
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(color: Colors.transparent),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.transparent,
|
||||
),
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
@@ -620,9 +612,7 @@ class _CommonTabBarRenderWidget<T extends Object>
|
||||
|
||||
@override
|
||||
void updateRenderObject(
|
||||
BuildContext context,
|
||||
_RenderSegmentedControl<T> renderObject,
|
||||
) {
|
||||
BuildContext context, _RenderSegmentedControl<T> renderObject) {
|
||||
assert(renderObject.state == state);
|
||||
renderObject
|
||||
..thumbColor = thumbColor
|
||||
@@ -639,24 +629,20 @@ enum _SegmentLocation { leftmost, rightmost, inbetween }
|
||||
|
||||
class _RenderSegmentedControl<T extends Object> extends RenderBox
|
||||
with
|
||||
ContainerRenderObjectMixin<
|
||||
RenderBox,
|
||||
ContainerBoxParentData<RenderBox>
|
||||
>,
|
||||
RenderBoxContainerDefaultsMixin<
|
||||
RenderBox,
|
||||
ContainerBoxParentData<RenderBox>
|
||||
> {
|
||||
ContainerRenderObjectMixin<RenderBox,
|
||||
ContainerBoxParentData<RenderBox>>,
|
||||
RenderBoxContainerDefaultsMixin<RenderBox,
|
||||
ContainerBoxParentData<RenderBox>> {
|
||||
_RenderSegmentedControl({
|
||||
required int? highlightedIndex,
|
||||
required Color thumbColor,
|
||||
required double thumbScale,
|
||||
required bool proportionalWidth,
|
||||
required this.state,
|
||||
}) : _highlightedIndex = highlightedIndex,
|
||||
_thumbColor = thumbColor,
|
||||
_thumbScale = thumbScale,
|
||||
_proportionalWidth = proportionalWidth;
|
||||
}) : _highlightedIndex = highlightedIndex,
|
||||
_thumbColor = thumbColor,
|
||||
_thumbScale = thumbScale,
|
||||
_proportionalWidth = proportionalWidth;
|
||||
|
||||
final _CommonTabBarState<T> state;
|
||||
|
||||
@@ -853,9 +839,7 @@ class _RenderSegmentedControl<T extends Object> extends RenderBox
|
||||
child = nonSeparatorChildAfter(child);
|
||||
}
|
||||
return math.min(
|
||||
childWidth,
|
||||
(constraints.maxWidth - totalSeparatorWidth) / childCount,
|
||||
);
|
||||
childWidth, (constraints.maxWidth - totalSeparatorWidth) / childCount);
|
||||
}
|
||||
|
||||
List<double> _getChildWidths(BoxConstraints constraints) {
|
||||
@@ -889,28 +873,20 @@ class _RenderSegmentedControl<T extends Object> extends RenderBox
|
||||
}
|
||||
|
||||
Size _computeOverallSize(BoxConstraints constraints) {
|
||||
final double maxChildHeight = _getMaxChildHeight(
|
||||
constraints,
|
||||
constraints.maxWidth,
|
||||
);
|
||||
final double maxChildHeight =
|
||||
_getMaxChildHeight(constraints, constraints.maxWidth);
|
||||
return constraints.constrain(
|
||||
Size(
|
||||
_getChildWidths(constraints).sum + totalSeparatorWidth,
|
||||
maxChildHeight,
|
||||
),
|
||||
Size(_getChildWidths(constraints).sum + totalSeparatorWidth,
|
||||
maxChildHeight),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
double? computeDryBaseline(
|
||||
covariant BoxConstraints constraints,
|
||||
TextBaseline baseline,
|
||||
) {
|
||||
covariant BoxConstraints constraints, TextBaseline baseline) {
|
||||
final List<double> segmentWidths = _getChildWidths(constraints);
|
||||
final double childHeight = _getMaxChildHeight(
|
||||
constraints,
|
||||
constraints.maxWidth,
|
||||
);
|
||||
final double childHeight =
|
||||
_getMaxChildHeight(constraints, constraints.maxWidth);
|
||||
|
||||
int index = 0;
|
||||
BaselineOffset baselineOffset = BaselineOffset.noBaseline;
|
||||
@@ -952,10 +928,8 @@ class _RenderSegmentedControl<T extends Object> extends RenderBox
|
||||
final BoxConstraints childConstraints = BoxConstraints.tight(
|
||||
Size(segmentWidths[index ~/ 2], childHeight),
|
||||
);
|
||||
child.layout(
|
||||
index.isEven ? childConstraints : separatorConstraints,
|
||||
parentUsesSize: true,
|
||||
);
|
||||
child.layout(index.isEven ? childConstraints : separatorConstraints,
|
||||
parentUsesSize: true);
|
||||
final _SegmentedControlContainerBoxParentData childParentData =
|
||||
child.parentData! as _SegmentedControlContainerBoxParentData;
|
||||
final Offset childOffset = Offset(start, 0);
|
||||
@@ -985,9 +959,9 @@ class _RenderSegmentedControl<T extends Object> extends RenderBox
|
||||
final double leftMost = firstChildOffset.dx;
|
||||
final double rightMost =
|
||||
(children.last.parentData! as _SegmentedControlContainerBoxParentData)
|
||||
.offset
|
||||
.dx +
|
||||
children.last.size.width;
|
||||
.offset
|
||||
.dx +
|
||||
children.last.size.width;
|
||||
assert(rightMost > leftMost);
|
||||
return Rect.fromLTRB(
|
||||
math.max(thumbRect.left, leftMost - _kThumbInsets.left),
|
||||
@@ -1018,10 +992,8 @@ class _RenderSegmentedControl<T extends Object> extends RenderBox
|
||||
if (thumbTween == null) {
|
||||
final Rect startingRect =
|
||||
moveThumbRectInBound(currentThumbRect, children) ?? newThumbRect;
|
||||
state.thumbAnimatable = RectTween(
|
||||
begin: startingRect,
|
||||
end: newThumbRect,
|
||||
);
|
||||
state.thumbAnimatable =
|
||||
RectTween(begin: startingRect, end: newThumbRect);
|
||||
} else if (newThumbRect != thumbTween.transform(1)) {
|
||||
final Rect startingRect =
|
||||
moveThumbRectInBound(currentThumbRect, children) ?? newThumbRect;
|
||||
@@ -1036,7 +1008,7 @@ class _RenderSegmentedControl<T extends Object> extends RenderBox
|
||||
|
||||
final Rect unscaledThumbRect =
|
||||
state.thumbAnimatable?.evaluate(state.thumbController) ??
|
||||
newThumbRect;
|
||||
newThumbRect;
|
||||
currentThumbRect = unscaledThumbRect;
|
||||
|
||||
final _SegmentLocation childLocation;
|
||||
@@ -1073,10 +1045,7 @@ class _RenderSegmentedControl<T extends Object> extends RenderBox
|
||||
final Paint separatorPaint = Paint();
|
||||
|
||||
void _paintSeparator(
|
||||
PaintingContext context,
|
||||
Offset offset,
|
||||
RenderBox child,
|
||||
) {
|
||||
PaintingContext context, Offset offset, RenderBox child) {
|
||||
final _SegmentedControlContainerBoxParentData childParentData =
|
||||
child.parentData! as _SegmentedControlContainerBoxParentData;
|
||||
context.paintChild(child, offset + childParentData.offset);
|
||||
@@ -1089,20 +1058,23 @@ class _RenderSegmentedControl<T extends Object> extends RenderBox
|
||||
}
|
||||
|
||||
void _paintThumb(PaintingContext context, Offset offset, Rect thumbRect) {
|
||||
final RSuperellipse thumbRSuperellipse = RSuperellipse.fromRectAndRadius(
|
||||
thumbRect.shift(offset),
|
||||
_kThumbRadius,
|
||||
);
|
||||
// const List<BoxShadow> thumbShadow = <BoxShadow>[
|
||||
// BoxShadow(color: Color(0x1F000000), offset: Offset(0, 3), blurRadius: 8),
|
||||
// BoxShadow(color: Color(0x0A000000), offset: Offset(0, 3), blurRadius: 1),
|
||||
// ];
|
||||
|
||||
context.canvas.drawRSuperellipse(
|
||||
thumbRSuperellipse.inflate(0.5),
|
||||
Paint()..color = const Color(0x0A000000),
|
||||
);
|
||||
final RRect thumbRRect =
|
||||
RRect.fromRectAndRadius(thumbRect.shift(offset), _kThumbRadius);
|
||||
|
||||
context.canvas.drawRSuperellipse(
|
||||
thumbRSuperellipse,
|
||||
Paint()..color = thumbColor,
|
||||
);
|
||||
// for (final BoxShadow shadow in thumbShadow) {
|
||||
// context.canvas
|
||||
// .drawRRect(thumbRRect.shift(shadow.offset), shadow.toPaint());
|
||||
// }
|
||||
|
||||
context.canvas.drawRRect(
|
||||
thumbRRect.inflate(0.5), Paint()..color = const Color(0x0A000000));
|
||||
|
||||
context.canvas.drawRRect(thumbRRect, Paint()..color = thumbColor);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
export 'activate_box.dart';
|
||||
export 'animate_grid.dart';
|
||||
export 'pop_scope.dart';
|
||||
export 'builder.dart';
|
||||
export 'card.dart';
|
||||
export 'chip.dart';
|
||||
export 'color_scheme_box.dart';
|
||||
export 'container.dart';
|
||||
export 'dialog.dart';
|
||||
export 'disabled_mask.dart';
|
||||
export 'donut_chart.dart';
|
||||
export 'effect.dart';
|
||||
export 'fade_box.dart';
|
||||
export 'float_layout.dart';
|
||||
export 'grid.dart';
|
||||
@@ -17,19 +13,23 @@ export 'input.dart';
|
||||
export 'keep_scope.dart';
|
||||
export 'line_chart.dart';
|
||||
export 'list.dart';
|
||||
export 'notification.dart';
|
||||
export 'null_status.dart';
|
||||
export 'open_container.dart';
|
||||
export 'palette.dart';
|
||||
export 'pop_scope.dart';
|
||||
export 'popup.dart';
|
||||
export 'scaffold.dart';
|
||||
export 'scroll.dart';
|
||||
export 'setting.dart';
|
||||
export 'sheet.dart';
|
||||
export 'side_sheet.dart';
|
||||
export 'subscription_info_view.dart';
|
||||
export 'super_grid.dart';
|
||||
export 'tab.dart';
|
||||
export 'text.dart';
|
||||
export 'super_grid.dart';
|
||||
export 'donut_chart.dart';
|
||||
export 'activate_box.dart';
|
||||
export 'wave.dart';
|
||||
export 'scroll.dart';
|
||||
export 'dialog.dart';
|
||||
export 'effect.dart';
|
||||
export 'palette.dart';
|
||||
export 'tab.dart';
|
||||
export 'container.dart';
|
||||
export 'notification.dart';
|
||||
|
||||
12
pubspec.lock
12
pubspec.lock
@@ -666,10 +666,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image_picker_android
|
||||
sha256: "8dfe08ea7fcf7467dbaf6889e72eebd5e0d6711caae201fdac780eb45232cd02"
|
||||
sha256: a45bef33deb24839a51fb85a4d9e504ead2b1ad1c4779d02d09bf6a8857cdd52
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.8.13+3"
|
||||
version: "0.8.13+2"
|
||||
image_picker_for_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1010,10 +1010,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pool
|
||||
sha256: "978783255c543aa3586a1b3c21f6e9d720eb315376a915872c61ef8b5c20177d"
|
||||
sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.5.2"
|
||||
version: "1.5.1"
|
||||
posix:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1471,10 +1471,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_android
|
||||
sha256: "199bc33e746088546a39cc5f36bac5a278c5e53b40cb3196f99e7345fdcfae6b"
|
||||
sha256: "07cffecb7d68cbc6437cd803d5f11a86fe06736735c3dfe46ff73bcb0f958eed"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.3.22"
|
||||
version: "6.3.21"
|
||||
url_launcher_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name: fl_clash
|
||||
description: A multi-platform proxy client based on ClashMeta, simple and easy to use, open-source and ad-free.
|
||||
publish_to: 'none'
|
||||
version: 0.8.88+2025092101
|
||||
version: 0.8.88+2025092001
|
||||
environment:
|
||||
sdk: '>=3.8.0 <4.0.0'
|
||||
|
||||
|
||||
Reference in New Issue
Block a user