Compare commits

..

1 Commits

Author SHA1 Message Date
chen08209
4dc08fe55a Add android separates the core process
Support core status check and force restart

Optimize proxies page and access page

Update flutter and pub dependencies

Update go version

Optimize more details
2025-09-20 23:28:15 +08:00
26 changed files with 434 additions and 412 deletions

View File

@@ -1,6 +1,6 @@
module core
go 1.20
go 1.25
replace github.com/metacubex/mihomo => ./Clash.Meta

View File

@@ -1,3 +1,4 @@
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';
@@ -12,14 +13,14 @@ class CommonPrint {
return _instance!;
}
void log(String? text) {
void log(String? text, {LogLevel logLevel = LogLevel.info}) {
final payload = '[APP] $text';
debugPrint(payload);
if (!globalState.isInit) {
return;
}
globalState.appController.addLog(
Log.app(payload),
Log.app(payload).copyWith(logLevel: logLevel),
);
}
}

View File

@@ -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,11 +92,13 @@ class Request {
}
}
final future = dio.get<Map<String, dynamic>>(
source.key,
cancelToken: cancelToken,
options: Options(responseType: ResponseType.json),
);
final future = dio
.get<Map<String, dynamic>>(
source.key,
cancelToken: cancelToken,
options: Options(responseType: ResponseType.json),
)
.timeout(const Duration(seconds: 10));
future
.then((res) {
if (res.statusCode == HttpStatus.ok && res.data != null) {

View File

@@ -11,16 +11,15 @@ 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 {
@@ -66,9 +65,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? {

View File

@@ -181,7 +181,10 @@ class Windows {
calloc.free(argumentsPtr);
calloc.free(operationPtr);
commonPrint.log('windows runas: $command $arguments resultCode:$result');
commonPrint.log(
'windows runas: $command $arguments resultCode:$result',
logLevel: LogLevel.warning,
);
if (result < 42) {
return false;

View File

@@ -353,7 +353,7 @@ class AppController {
try {
await updateProfile(profile);
} catch (e) {
commonPrint.log(e.toString());
commonPrint.log(e.toString(), logLevel: LogLevel.warning);
}
}
}
@@ -529,6 +529,7 @@ class AppController {
FlutterError.onError = (details) {
commonPrint.log(
'exception: ${details.exception} stack: ${details.stack}',
logLevel: LogLevel.warning,
);
};
updateTray(true);
@@ -956,7 +957,7 @@ class AppController {
final res = await futureFunction();
return res;
} catch (e) {
commonPrint.log('$futureFunction ===> $e');
commonPrint.log('$futureFunction ===> $e', logLevel: LogLevel.warning);
if (realSilence) {
globalState.showNotifier(e.toString());
} else {

View File

@@ -52,7 +52,7 @@ class CoreService extends CoreHandlerInterface {
}
},
(error, stack) async {
commonPrint.log('Service error: $error');
commonPrint.log('Service error: $error', logLevel: LogLevel.warning);
},
);
}
@@ -97,7 +97,7 @@ class CoreService extends CoreHandlerInterface {
_process?.stderr.listen((e) {
final error = utf8.decode(e);
if (error.isNotEmpty) {
commonPrint.log(error);
commonPrint.log(error, logLevel: LogLevel.warning);
}
});
await _socketCompleter.future;

View File

@@ -101,7 +101,7 @@ class MessageManagerState extends State<MessageManager> {
_cancelMessage(messages.last.id);
},
child: Card(
shape: const RoundedRectangleBorder(
shape: const RoundedSuperellipseBorder(
borderRadius: BorderRadius.all(
Radius.circular(14),
),
@@ -127,6 +127,7 @@ class MessageManagerState extends State<MessageManager> {
),
SizedBox(width: 16),
IconButton(
padding: EdgeInsets.all(2),
visualDensity: VisualDensity.compact,
onPressed: () {
_cancelMessage(messages.last.id);

View File

@@ -87,13 +87,23 @@ class ThemeManager extends ConsumerWidget {
top: padding.top > height * 0.3 ? 20.0 : padding.top,
),
),
child: LayoutBuilder(
builder: (_, container) {
globalState.appController.updateViewSize(
Size(container.maxWidth, container.maxHeight),
);
return _buildSystemUi(child);
},
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);
},
),
),
);
}

View File

@@ -271,9 +271,11 @@ class AppIcon extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
decoration: ShapeDecoration(
color: context.colorScheme.surfaceContainerHighest,
borderRadius: BorderRadius.circular(14),
shape: RoundedSuperellipseBorder(
borderRadius: BorderRadius.circular(14),
),
),
padding: EdgeInsets.all(8),
child: Transform.translate(

View File

@@ -169,8 +169,8 @@ class ScannerOverlay extends CustomPainter {
final backgroundPath = Path()..addRect(Rect.largest);
final cutoutPath = Path()
..addRRect(
RRect.fromRectAndCorners(
..addRSuperellipse(
RSuperellipse.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 borderRect = RRect.fromRectAndCorners(
final border = RSuperellipse.fromRectAndCorners(
scanWindow,
topLeft: Radius.circular(borderRadius),
topRight: Radius.circular(borderRadius),
@@ -204,7 +204,7 @@ class ScannerOverlay extends CustomPainter {
);
canvas.drawPath(backgroundWithCutout, backgroundPaint);
canvas.drawRRect(borderRect, borderPaint);
canvas.drawRSuperellipse(border, borderPaint);
}
@override

View File

@@ -74,9 +74,10 @@ class _StartButtonState extends ConsumerState<StartButton>
}
return Theme(
data: Theme.of(context).copyWith(
floatingActionButtonTheme: FloatingActionButtonThemeData(
sizeConstraints: BoxConstraints(minWidth: 56, maxWidth: 200),
),
floatingActionButtonTheme: Theme.of(context).floatingActionButtonTheme
.copyWith(
sizeConstraints: BoxConstraints(minWidth: 56, maxWidth: 200),
),
),
child: AnimatedBuilder(
animation: _controller.view,

View File

@@ -128,10 +128,11 @@ class TrafficUsage extends StatelessWidget {
Container(
width: 20,
height: 8,
decoration: BoxDecoration(
decoration: ShapeDecoration(
color: primaryColor,
borderRadius: BorderRadius.circular(
3,
shape: RoundedSuperellipseBorder(
borderRadius:
BorderRadius.circular(3),
),
),
),
@@ -151,10 +152,11 @@ class TrafficUsage extends StatelessWidget {
Container(
width: 20,
height: 8,
decoration: BoxDecoration(
decoration: ShapeDecoration(
color: secondaryColor,
borderRadius: BorderRadius.circular(
3,
shape: RoundedSuperellipseBorder(
borderRadius:
BorderRadius.circular(3),
),
),
),

View File

@@ -2,7 +2,6 @@ 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';
@@ -19,12 +18,14 @@ 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(
@@ -37,6 +38,7 @@ class DeveloperView extends ConsumerWidget {
),
ListItem(
title: Text(appLocalizations.crashTest),
minVerticalPadding: 14,
onTap: () {
if (kDebugMode) {
coreController.crash();
@@ -45,18 +47,20 @@ class DeveloperView extends ConsumerWidget {
),
ListItem(
title: Text(appLocalizations.clearData),
minVerticalPadding: 14,
onTap: () async {
await globalState.appController.handleClear();
},
),
ListItem(
title: Text('Loading'),
onTap: () {
ref.read(loadingProvider.notifier).value = !ref.read(
loadingProvider,
);
},
),
// ListItem(
// title: Text('Loading'),
// minVerticalPadding: 14,
// onTap: () {
// ref.read(loadingProvider.notifier).value = !ref.read(
// loadingProvider,
// );
// },
// ),
],
);
}

View File

@@ -360,7 +360,7 @@ class DelayTestButton extends StatefulWidget {
class _DelayTestButtonState extends State<DelayTestButton>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _scale;
late Animation<double> _animation;
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: 200),
duration: const Duration(milliseconds: 400),
);
_scale = Tween<double>(begin: 1.0, end: 0.0).animate(
CurvedAnimation(parent: _controller, curve: const Interval(0, 1)),
_animation = Tween<double>(begin: 1.0, end: 0.0).animate(
CurvedAnimation(parent: _controller, curve: Curves.easeInOutBack),
);
}
@@ -399,7 +399,10 @@ class _DelayTestButtonState extends State<DelayTestButton>
return SizedBox(
width: 56,
height: 56,
child: Transform.scale(scale: _scale.value, child: child),
child: FadeTransition(
opacity: _animation,
child: ScaleTransition(scale: _animation, child: child),
),
);
},
child: FloatingActionButton(

View File

@@ -177,7 +177,9 @@ class CommonCard extends StatelessWidget {
style: ButtonStyle(
padding: const WidgetStatePropertyAll(EdgeInsets.zero),
shape: WidgetStatePropertyAll(
RoundedRectangleBorder(borderRadius: BorderRadius.circular(radius)),
RoundedSuperellipseBorder(
borderRadius: BorderRadius.circular(radius),
),
),
iconColor: WidgetStatePropertyAll(context.colorScheme.primary),
iconSize: WidgetStateProperty.all(20),

View File

@@ -37,7 +37,7 @@ class ColorSchemeBox extends StatelessWidget {
),
child: Container(
padding: const EdgeInsets.all(8),
child: ClipRRect(
child: ClipRSuperellipse(
borderRadius: BorderRadius.circular(36),
child: SizedBox(
width: 72,
@@ -47,22 +47,16 @@ 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),
),
],
),
),
@@ -73,11 +67,8 @@ class ColorSchemeBox extends StatelessWidget {
const Positioned(
bottom: 4,
right: 4,
child: Icon(
Icons.colorize,
size: 20,
),
)
child: Icon(Icons.colorize, size: 20),
),
],
);
},
@@ -112,9 +103,7 @@ class PrimaryColorBox extends ConsumerWidget {
),
);
return Theme(
data: themeData.copyWith(
colorScheme: colorScheme,
),
data: themeData.copyWith(colorScheme: colorScheme),
child: child,
);
}

View File

@@ -18,30 +18,21 @@ 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 {
@@ -129,6 +120,7 @@ class ListItem<T> extends StatelessWidget {
final double? horizontalTitleGap;
final TextStyle? titleTextStyle;
final TextStyle? subtitleTextStyle;
final double minVerticalPadding;
final void Function()? onTap;
const ListItem({
@@ -143,6 +135,7 @@ class ListItem<T> extends StatelessWidget {
this.onTap,
this.titleTextStyle,
this.subtitleTextStyle,
this.minVerticalPadding = 12,
this.tileTitleAlignment = ListTileTitleAlignment.center,
}) : delegate = const Delegate();
@@ -158,6 +151,7 @@ class ListItem<T> extends StatelessWidget {
this.dense,
this.titleTextStyle,
this.subtitleTextStyle,
this.minVerticalPadding = 12,
this.tileTitleAlignment = ListTileTitleAlignment.center,
}) : onTap = null;
@@ -173,6 +167,7 @@ class ListItem<T> extends StatelessWidget {
this.dense,
this.titleTextStyle,
this.subtitleTextStyle,
this.minVerticalPadding = 12,
this.tileTitleAlignment = ListTileTitleAlignment.center,
}) : onTap = null;
@@ -188,6 +183,7 @@ class ListItem<T> extends StatelessWidget {
this.dense,
this.titleTextStyle,
this.subtitleTextStyle,
this.minVerticalPadding = 12,
this.tileTitleAlignment = ListTileTitleAlignment.center,
}) : onTap = null;
@@ -203,6 +199,7 @@ class ListItem<T> extends StatelessWidget {
this.dense,
this.titleTextStyle,
this.subtitleTextStyle,
this.minVerticalPadding = 12,
this.tileTitleAlignment = ListTileTitleAlignment.center,
}) : onTap = null;
@@ -217,9 +214,10 @@ 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,
@@ -232,9 +230,10 @@ 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,
@@ -247,9 +246,10 @@ 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: 12,
minVerticalPadding: minVerticalPadding,
subtitle: subtitle,
titleAlignment: tileTitleAlignment,
onTap: onTap,
@@ -306,9 +306,7 @@ class ListItem<T> extends StatelessWidget {
action();
}
return _buildListTile(
onTap: openAction,
);
return _buildListTile(onTap: openAction);
},
openBuilder: (_, action) {
return openDelegate.wrap
@@ -422,9 +420,7 @@ class ListItem<T> extends StatelessWidget {
);
}
return _buildListTile(
onTap: onTap,
);
return _buildListTile(onTap: onTap);
}
}
@@ -448,13 +444,9 @@ 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,
@@ -466,19 +458,18 @@ 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,
),
),
],
),
@@ -486,12 +477,7 @@ class ListHeader extends StatelessWidget {
Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.end,
children: [
...genActions(
actions,
space: space,
),
],
children: [...genActions(actions, space: space)],
),
],
),
@@ -506,18 +492,11 @@ 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,
];
}
@@ -528,22 +507,26 @@ 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,
),
CommonCard(
radius: 18,
type: CommonCardType.filled,
child: Column(
children: [
...items,
],
),
)
ListHeader(title: title, actions: actions),
ClipRSuperellipse(
borderRadius: BorderRadius.circular(14),
child: Column(children: [...genItems]),
),
],
);
}
@@ -555,18 +538,10 @@ 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,
];
}
@@ -575,8 +550,6 @@ 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),
);
}

View File

@@ -243,7 +243,7 @@ class _ShadePainter extends CustomPainter {
effectiveSquareRadius * 2,
effectiveSquareRadius * 2,
);
final RRect rRect = RRect.fromRectAndRadius(
final RSuperellipse rSuperellipse = RSuperellipse.fromRectAndRadius(
rectBox,
Radius.circular(trackBorderRadius),
);
@@ -254,8 +254,8 @@ class _ShadePainter extends CustomPainter {
HSVColor.fromAHSV(1, colorHue, 1, 1).toColor(),
],
).createShader(rectBox);
canvas.drawRRect(
rRect,
canvas.drawRSuperellipse(
rSuperellipse,
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.drawRRect(
rRect,
canvas.drawRSuperellipse(
rSuperellipse,
Paint()
..style = PaintingStyle.fill
..shader = vertical,

View File

@@ -38,10 +38,9 @@ class CommonPopupRoute<T> extends PopupRoute<T> {
Widget child,
) {
final align = Alignment.topRight;
final animationValue = CurvedAnimation(
parent: animation,
curve: Curves.easeIn,
).value;
final curveAnimation = animation
.drive(Tween(begin: 0.0, end: 1.0))
.drive(CurveTween(curve: Curves.easeOutBack));
return SafeArea(
child: ValueListenableBuilder(
valueListenable: offsetNotifier,
@@ -58,15 +57,17 @@ class CommonPopupRoute<T> extends PopupRoute<T> {
},
child: AnimatedBuilder(
animation: animation,
builder: (_, Widget? child) {
return Opacity(
opacity: 0.1 + 0.9 * animationValue,
child: Transform.scale(
builder: (_, child) {
return FadeTransition(
opacity: curveAnimation,
child: ScaleTransition(
alignment: align,
scale: 0.7 + 0.3 * animationValue,
child: Transform.translate(
offset: Offset(0, -10) * (1 - animationValue),
child: child!,
scale: curveAnimation,
child: SlideTransition(
position: curveAnimation.drive(
Tween(begin: const Offset(0, -0.02), end: Offset.zero),
),
child: child,
),
),
);
@@ -78,7 +79,7 @@ class CommonPopupRoute<T> extends PopupRoute<T> {
}
@override
Duration get transitionDuration => const Duration(milliseconds: 150);
Duration get transitionDuration => const Duration(milliseconds: 250);
}
class PopupController extends ValueNotifier<bool> {
@@ -270,8 +271,8 @@ class CommonPopupMenu extends StatelessWidget {
elevation: 12,
color: context.colorScheme.surfaceContainer,
clipBehavior: Clip.antiAlias,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
shape: RoundedSuperellipseBorder(
borderRadius: BorderRadius.circular(14),
),
child: Column(
mainAxisSize: MainAxisSize.min,

View File

@@ -39,11 +39,7 @@ class ExtendProps {
});
}
enum SheetType {
page,
bottomSheet,
sideSheet,
}
enum SheetType { page, bottomSheet, sideSheet }
typedef SheetBuilder = Widget Function(BuildContext context, SheetType type);
@@ -55,28 +51,24 @@ 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);
},
),
};
}
@@ -87,21 +79,16 @@ 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);
},
),
};
}
@@ -134,13 +121,11 @@ 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,
@@ -150,9 +135,11 @@ class _AdaptiveSheetScaffoldState extends State<AdaptiveSheetScaffold> {
final handleSize = Size(32, 4);
return Container(
clipBehavior: Clip.hardEdge,
decoration: BoxDecoration(
decoration: ShapeDecoration(
color: backgroundColor,
borderRadius: BorderRadius.vertical(top: Radius.circular(28.0)),
shape: RoundedSuperellipseBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(28.0)),
),
),
child: Column(
mainAxisSize: MainAxisSize.min,
@@ -163,17 +150,16 @@ class _AdaptiveSheetScaffoldState extends State<AdaptiveSheetScaffold> {
alignment: Alignment.center,
height: handleSize.height,
width: handleSize.width,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(handleSize.height / 2),
decoration: ShapeDecoration(
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),
],
),
);

View File

@@ -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 ??
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(0),
);
final ShapeBorder shape =
widget.shape ??
RoundedSuperellipseBorder(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,10 +103,7 @@ class _SideSheetState extends State<SideSheet> {
child: widget.builder(context),
);
return ConstrainedBox(
constraints: constraints,
child: sideSheet,
);
return ConstrainedBox(constraints: constraints, child: sideSheet);
}
}
@@ -128,7 +125,8 @@ class _SideSheetLayoutWithSizeListener extends SingleChildRenderObjectWidget {
@override
_RenderSideSheetLayoutWithSizeListener createRenderObject(
BuildContext context) {
BuildContext context,
) {
return _RenderSideSheetLayoutWithSizeListener(
onChildSizeChanged: onChildSizeChanged,
animationValue: animationValue,
@@ -138,8 +136,10 @@ 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,8 +219,9 @@ 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;
}
@@ -229,8 +230,9 @@ 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;
}
@@ -239,8 +241,9 @@ 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;
}
@@ -249,8 +252,9 @@ 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;
}
@@ -263,9 +267,7 @@ class _RenderSideSheetLayoutWithSizeListener extends RenderShiftedBox {
}
BoxConstraints _getConstraintsForChild(BoxConstraints constraints) {
return BoxConstraints(
maxHeight: constraints.maxHeight,
);
return BoxConstraints(maxHeight: constraints.maxHeight);
}
Offset _getPositionForChild(Size size, Size childSize) {
@@ -276,8 +278,9 @@ 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,
@@ -288,8 +291,9 @@ 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;
@@ -354,8 +358,9 @@ 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(
@@ -406,26 +411,27 @@ 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;
@@ -504,8 +510,11 @@ 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(
@@ -539,9 +548,7 @@ class ModalSideSheetRoute<T> extends PopupRoute<T> {
ColorTween(
begin: barrierColor.opacity0,
end: barrierColor,
).chain(
CurveTween(curve: barrierCurve),
),
).chain(CurveTween(curve: barrierCurve)),
);
return AnimatedModalBarrier(
color: color,
@@ -587,32 +594,39 @@ 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 {

View File

@@ -8,8 +8,10 @@ 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);
@@ -63,11 +65,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;
@@ -190,8 +192,9 @@ 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);
}
@@ -231,8 +234,9 @@ 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;
}
@@ -373,8 +377,10 @@ class _CommonTabBarState<T extends Object> extends State<CommonTabBar<T>>
child: Container(
clipBehavior: Clip.antiAlias,
padding: widget.padding.resolve(Directionality.of(context)),
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(_kCornerRadius),
decoration: ShapeDecoration(
shape: RoundedSuperellipseBorder(
borderRadius: const BorderRadius.all(_kCornerRadius),
),
color: widget.backgroundColor,
),
child: AnimatedBuilder(
@@ -455,8 +461,9 @@ class _SegmentState<T> extends State<_Segment<T>>
end: widget.shouldScaleContent ? _kMinThumbScale : 1.0,
),
);
highlightPressScaleController
.animateWith(_kThumbSpringAnimationSimulation);
highlightPressScaleController.animateWith(
_kThumbSpringAnimationSimulation,
);
}
}
@@ -480,20 +487,21 @@ 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(
@@ -505,7 +513,9 @@ class _SegmentState<T> extends State<_Segment<T>>
),
DefaultTextStyle.merge(
style: const TextStyle(
fontWeight: _kHighlightedFontWeight, fontSize: _kFontSize),
fontWeight: _kHighlightedFontWeight,
fontSize: _kFontSize,
),
child: widget.child,
),
],
@@ -570,9 +580,7 @@ class _SegmentSeparatorState extends State<_SegmentSeparator>
return Padding(
padding: _kSeparatorInset,
child: DecoratedBox(
decoration: BoxDecoration(
color: Colors.transparent,
),
decoration: BoxDecoration(color: Colors.transparent),
child: child,
),
);
@@ -612,7 +620,9 @@ 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
@@ -629,20 +639,24 @@ 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;
@@ -839,7 +853,9 @@ 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) {
@@ -873,20 +889,28 @@ 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;
@@ -928,8 +952,10 @@ 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);
@@ -959,9 +985,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),
@@ -992,8 +1018,10 @@ 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;
@@ -1008,7 +1036,7 @@ class _RenderSegmentedControl<T extends Object> extends RenderBox
final Rect unscaledThumbRect =
state.thumbAnimatable?.evaluate(state.thumbController) ??
newThumbRect;
newThumbRect;
currentThumbRect = unscaledThumbRect;
final _SegmentLocation childLocation;
@@ -1045,7 +1073,10 @@ 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);
@@ -1058,23 +1089,20 @@ class _RenderSegmentedControl<T extends Object> extends RenderBox
}
void _paintThumb(PaintingContext context, Offset offset, Rect thumbRect) {
// 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),
// ];
final RSuperellipse thumbRSuperellipse = RSuperellipse.fromRectAndRadius(
thumbRect.shift(offset),
_kThumbRadius,
);
final RRect thumbRRect =
RRect.fromRectAndRadius(thumbRect.shift(offset), _kThumbRadius);
context.canvas.drawRSuperellipse(
thumbRSuperellipse.inflate(0.5),
Paint()..color = const Color(0x0A000000),
);
// 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);
context.canvas.drawRSuperellipse(
thumbRSuperellipse,
Paint()..color = thumbColor,
);
}
@override

View File

@@ -1,10 +1,14 @@
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';
@@ -13,23 +17,19 @@ 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 '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';
export 'text.dart';
export 'wave.dart';

View File

@@ -666,10 +666,10 @@ packages:
dependency: transitive
description:
name: image_picker_android
sha256: a45bef33deb24839a51fb85a4d9e504ead2b1ad1c4779d02d09bf6a8857cdd52
sha256: "8dfe08ea7fcf7467dbaf6889e72eebd5e0d6711caae201fdac780eb45232cd02"
url: "https://pub.dev"
source: hosted
version: "0.8.13+2"
version: "0.8.13+3"
image_picker_for_web:
dependency: transitive
description:
@@ -1010,10 +1010,10 @@ packages:
dependency: transitive
description:
name: pool
sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a"
sha256: "978783255c543aa3586a1b3c21f6e9d720eb315376a915872c61ef8b5c20177d"
url: "https://pub.dev"
source: hosted
version: "1.5.1"
version: "1.5.2"
posix:
dependency: transitive
description:
@@ -1471,10 +1471,10 @@ packages:
dependency: transitive
description:
name: url_launcher_android
sha256: "07cffecb7d68cbc6437cd803d5f11a86fe06736735c3dfe46ff73bcb0f958eed"
sha256: "199bc33e746088546a39cc5f36bac5a278c5e53b40cb3196f99e7345fdcfae6b"
url: "https://pub.dev"
source: hosted
version: "6.3.21"
version: "6.3.22"
url_launcher_ios:
dependency: transitive
description:

View File

@@ -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+2025092001
version: 0.8.88+2025092101
environment:
sdk: '>=3.8.0 <4.0.0'