Files
MWClash/lib/pages/home.dart
chen08209 2fbb96f5c1 Add sqlite store
Optimize android quick action

Optimize backup and restore

Optimize more details
2026-02-02 10:15:42 +08:00

290 lines
8.8 KiB
Dart

import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/controller.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/manager/app_manager.dart';
import 'package:fl_clash/models/common.dart';
import 'package:fl_clash/providers/providers.dart';
import 'package:fl_clash/widgets/widgets.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:intl/intl.dart';
typedef OnSelected = void Function(int index);
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return HomeBackScopeContainer(
child: AppSidebarContainer(
child: Material(
color: context.colorScheme.surface,
child: Consumer(
builder: (context, ref, child) {
final state = ref.watch(navigationStateProvider);
final systemUiOverlayStyle = ref.read(
systemUiOverlayStyleStateProvider,
);
final isMobile = state.viewMode == ViewMode.mobile;
final navigationItems = state.navigationItems;
final currentIndex = state.currentIndex;
final bottomNavigationBar = NavigationBarTheme(
data: _NavigationBarDefaultsM3(context),
child: NavigationBar(
destinations: navigationItems
.map(
(e) => NavigationDestination(
icon: e.icon,
label: Intl.message(e.label.name),
),
)
.toList(),
onDestinationSelected: (index) {
appController.toPage(navigationItems[index].label);
},
selectedIndex: currentIndex,
),
);
if (isMobile) {
return AnnotatedRegion<SystemUiOverlayStyle>(
value: systemUiOverlayStyle.copyWith(
systemNavigationBarColor:
context.colorScheme.surfaceContainer,
),
child: Column(
children: [
Flexible(
flex: 1,
child: MediaQuery.removePadding(
removeTop: false,
removeBottom: true,
removeLeft: true,
removeRight: true,
context: context,
child: child!,
),
),
MediaQuery.removePadding(
removeTop: true,
removeBottom: false,
removeLeft: true,
removeRight: true,
context: context,
child: bottomNavigationBar,
),
],
),
);
} else {
return child!;
}
},
child: Consumer(
builder: (_, ref, _) {
final navigationItems = ref
.watch(currentNavigationItemsStateProvider)
.value;
final isMobile = ref.watch(isMobileViewProvider);
return _HomePageView(
navigationItems: navigationItems,
pageBuilder: (_, index) {
final navigationItem = navigationItems[index];
final navigationView = navigationItem.builder(context);
final view = KeepScope(
keep: navigationItem.keep,
child: isMobile
? navigationView
: Navigator(
pages: [MaterialPage(child: navigationView)],
onDidRemovePage: (_) {},
),
);
return view;
},
);
},
),
),
),
),
);
}
}
class _HomePageView extends ConsumerStatefulWidget {
final IndexedWidgetBuilder pageBuilder;
final List<NavigationItem> navigationItems;
const _HomePageView({
required this.pageBuilder,
required this.navigationItems,
});
@override
ConsumerState createState() => _HomePageViewState();
}
class _HomePageViewState extends ConsumerState<_HomePageView> {
late PageController _pageController;
@override
initState() {
super.initState();
_pageController = PageController(initialPage: _pageIndex);
ref.listenManual(currentPageLabelProvider, (prev, next) {
if (prev != next) {
_toPage(next);
}
});
}
@override
void didUpdateWidget(covariant _HomePageView oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.navigationItems.length != widget.navigationItems.length) {
_updatePageController();
}
}
int get _pageIndex {
final pageLabel = ref.read(currentPageLabelProvider);
return widget.navigationItems.indexWhere((item) => item.label == pageLabel);
}
Future<void> _toPage(
PageLabel pageLabel, [
bool ignoreAnimateTo = false,
]) async {
if (!mounted) {
return;
}
final index = widget.navigationItems.indexWhere(
(item) => item.label == pageLabel,
);
if (index == -1) {
return;
}
final isAnimateToPage = ref.read(appSettingProvider).isAnimateToPage;
final isMobile = ref.read(isMobileViewProvider);
if (isAnimateToPage && isMobile && !ignoreAnimateTo) {
await _pageController.animateToPage(
index,
duration: kTabScrollDuration,
curve: Curves.easeOut,
);
} else {
_pageController.jumpToPage(index);
}
}
void _updatePageController() {
final pageLabel = ref.read(currentPageLabelProvider);
_toPage(pageLabel, true);
}
@override
void dispose() {
_pageController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final itemCount = ref.watch(
currentNavigationItemsStateProvider.select((state) => state.value.length),
);
return PageView.builder(
controller: _pageController,
physics: const NeverScrollableScrollPhysics(),
itemCount: itemCount,
itemBuilder: (context, index) {
return widget.pageBuilder(context, index);
},
);
}
}
class _NavigationBarDefaultsM3 extends NavigationBarThemeData {
_NavigationBarDefaultsM3(this.context)
: super(
height: 80.0,
elevation: 3.0,
labelBehavior: NavigationDestinationLabelBehavior.alwaysShow,
);
final BuildContext context;
late final ColorScheme _colors = Theme.of(context).colorScheme;
late final TextTheme _textTheme = Theme.of(context).textTheme;
@override
Color? get backgroundColor => _colors.surfaceContainer;
@override
Color? get shadowColor => Colors.transparent;
@override
Color? get surfaceTintColor => Colors.transparent;
@override
WidgetStateProperty<IconThemeData?>? get iconTheme {
return WidgetStateProperty.resolveWith((Set<WidgetState> states) {
return IconThemeData(
size: 24.0,
color: states.contains(WidgetState.disabled)
? _colors.onSurfaceVariant.opacity38
: states.contains(WidgetState.selected)
? _colors.onSecondaryContainer
: _colors.onSurfaceVariant,
);
});
}
@override
Color? get indicatorColor => _colors.secondaryContainer;
@override
ShapeBorder? get indicatorShape => const StadiumBorder();
@override
WidgetStateProperty<TextStyle?>? get labelTextStyle {
return WidgetStateProperty.resolveWith((Set<WidgetState> states) {
final TextStyle style = _textTheme.labelMedium!;
return style.apply(
overflow: TextOverflow.ellipsis,
color: states.contains(WidgetState.disabled)
? _colors.onSurfaceVariant.opacity38
: states.contains(WidgetState.selected)
? _colors.onSurface
: _colors.onSurfaceVariant,
);
});
}
}
class HomeBackScopeContainer extends ConsumerWidget {
final Widget child;
const HomeBackScopeContainer({super.key, required this.child});
@override
Widget build(BuildContext context, ref) {
return CommonPopScope(
onPop: (context) async {
final pageLabel = ref.read(currentPageLabelProvider);
final realContext =
GlobalObjectKey(pageLabel).currentContext ?? context;
final canPop = Navigator.canPop(realContext);
if (canPop) {
Navigator.of(realContext).pop();
} else {
await appController.handleBackOrExit();
}
return false;
},
child: child,
);
}
}