Files
MWClash/lib/widgets/sheet.dart
chen08209 9271d59f72 Fix windows tun issues
Fix windows backup recovery issues

Optimize overwrite handle

Optimize access control page

Optimize some details
2025-11-28 17:35:27 +08:00

181 lines
5.0 KiB
Dart

import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart';
import 'package:fl_clash/state.dart';
import 'package:flutter/material.dart';
import 'scaffold.dart';
import 'side_sheet.dart';
@immutable
class SheetProps {
final double? maxWidth;
final double? maxHeight;
final bool isScrollControlled;
final bool useSafeArea;
final bool blur;
const SheetProps({
this.maxWidth,
this.maxHeight,
this.useSafeArea = true,
this.isScrollControlled = false,
this.blur = true,
});
}
@immutable
class ExtendProps {
final double? maxWidth;
final bool useSafeArea;
final bool blur;
final bool forceFull;
const ExtendProps({
this.maxWidth,
this.useSafeArea = true,
this.blur = true,
this.forceFull = false,
});
}
enum SheetType { page, bottomSheet, sideSheet }
typedef SheetBuilder = Widget Function(BuildContext context, SheetType type);
Future<T?> showSheet<T>({
required BuildContext context,
required SheetBuilder builder,
SheetProps props = const SheetProps(),
}) {
final isMobile = globalState.appState.viewMode == ViewMode.mobile;
return switch (isMobile) {
true => showModalBottomSheet<T>(
context: context,
isScrollControlled: props.isScrollControlled,
builder: (_) {
return 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);
},
),
};
}
Future<T?> showExtend<T>(
BuildContext context, {
required SheetBuilder builder,
ExtendProps props = const ExtendProps(),
}) {
final isMobile = globalState.appState.viewMode == ViewMode.mobile;
return switch (isMobile || props.forceFull) {
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);
},
),
};
}
class AdaptiveSheetScaffold extends StatefulWidget {
final SheetType type;
final Widget body;
final String title;
final bool? centerTitle;
final List<Widget> actions;
const AdaptiveSheetScaffold({
super.key,
required this.type,
required this.body,
required this.title,
this.centerTitle,
this.actions = const [],
});
@override
State<AdaptiveSheetScaffold> createState() => _AdaptiveSheetScaffoldState();
}
class _AdaptiveSheetScaffoldState extends State<AdaptiveSheetScaffold> {
@override
Widget build(BuildContext context) {
final backgroundColor = context.colorScheme.surface;
final bottomSheet = widget.type == SheetType.bottomSheet;
final sideSheet = widget.type == SheetType.sideSheet;
final appBar = AppBar(
forceMaterialTransparency: bottomSheet ? true : false,
automaticallyImplyLeading: bottomSheet
? false
: widget.actions.isEmpty && sideSheet
? false
: true,
centerTitle:
widget.centerTitle ?? (bottomSheet && widget.actions.isEmpty),
backgroundColor: backgroundColor,
title: Text(widget.title),
actions: genActions([
if (widget.actions.isEmpty && sideSheet) CloseButton(),
...widget.actions,
]),
);
if (bottomSheet) {
final handleSize = Size(32, 4);
return Container(
clipBehavior: Clip.hardEdge,
decoration: ShapeDecoration(
color: backgroundColor,
shape: RoundedSuperellipseBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(28.0)),
),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: EdgeInsets.only(top: 16),
child: Container(
alignment: Alignment.center,
height: handleSize.height,
width: handleSize.width,
decoration: ShapeDecoration(
color: context.colorScheme.onSurfaceVariant,
shape: RoundedSuperellipseBorder(
borderRadius: BorderRadius.circular(handleSize.height / 2),
),
),
),
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 8),
child: appBar,
),
Flexible(flex: 1, child: widget.body),
SizedBox(height: MediaQuery.of(context).viewPadding.bottom),
],
),
);
}
return CommonScaffold(
appBar: appBar,
backgroundColor: backgroundColor,
body: widget.body,
);
}
}