import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:nomo/config/theme/theme_extensions/theme_extension.dart'; import 'package:pull_to_refresh_flutter3/pull_to_refresh_flutter3.dart'; import '../config/theme/ix_theme.dart'; import '../config/translations/localization_service.dart'; import '../config/translations/strings_enum.dart'; import '../utils/developer/ix_developer_tools.dart'; import '../utils/device_manager.dart'; import '../utils/ix_back_button_dispatcher.dart'; import '../utils/log/logger.dart'; import 'components/ix_snackbar.dart'; import 'constants/configs.dart'; import 'data/sp/ix_sp.dart'; import 'routes/app_pages.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'widgets/click_opacity.dart'; import 'widgets/gradient_circle_header.dart'; import 'widgets/triple_tap_detector.dart'; class App extends StatelessWidget { const App({super.key}); @override Widget build(BuildContext context) { return ScreenUtilInit( designSize: const Size(375, 812), minTextAdapt: true, splitScreenMode: true, useInheritedMediaQuery: true, rebuildFactor: (old, data) => true, builder: (context, widget) => RefreshConfiguration( headerBuilder: () => GradientCircleHeader(), // Configure the default header indicator. If you have the same header indicator for each page, you need to set this footerBuilder: () => ClassicFooter(), // Configure default bottom indicator headerTriggerDistance: 100.0, // header trigger refresh trigger distance springDescription: SpringDescription( mass: 1, stiffness: 1000, damping: 100, ), // custom spring back animate,the props meaning see the flutter api maxOverScrollExtent: 100, //The maximum dragging range of the head. Set this property if a rush out of the view area occurs maxUnderScrollExtent: 0, // Maximum dragging range at the bottom enableScrollWhenRefreshCompleted: true, //This property is incompatible with PageView and TabBarView. If you need TabBarView to slide left and right, you need to set it to true. enableLoadingWhenFailed: true, //In the case of load failure, users can still trigger more loads by gesture pull-up. hideFooterWhenNotFull: false, // Disable pull-up to load more functionality when Viewport is less than one screen enableBallisticLoad: true, // trigger load more by BallisticScrollActivity child: _buildMaterialApp(), ), ); } Widget _buildMaterialApp() { bool isLightTheme = IXSP.getThemeIsLight(); log('App', 'isLightTheme: $isLightTheme'); return GetMaterialApp.router( title: 'ixVPN', useInheritedMediaQuery: true, debugShowCheckedModeBanner: Configs.debug, backButtonDispatcher: IXBackButtonDispatcher(), getPages: AppPages.routes, theme: IXTheme.getThemeData(isLight: true), darkTheme: IXTheme.getThemeData(isLight: false), themeMode: isLightTheme ? ThemeMode.light : ThemeMode.dark, fallbackLocale: LocalizationService.defaultLanguage, locale: IXSP.getCurrentLocal(), translations: LocalizationService.getInstance(), builder: (context, widget) => _buildThemedApp(context, widget), ); } Widget _buildThemedApp(BuildContext context, Widget? widget) { return MediaQuery( data: MediaQuery.of(context).copyWith(textScaler: TextScaler.noScaling), child: Stack( children: [ widget!, Positioned( top: 0, right: 0, width: 100, height: 100, child: TripleTapDetector( requiredTaps: 10, onTripleTap: () async { Clipboard.setData( ClipboardData(text: await DeviceManager.getDeviceId()), ); IXSnackBar.showIXSnackBar( title: Strings.copied.tr, message: await DeviceManager.getDeviceId(), ); }, child: const SizedBox.shrink(), // 不显示任何东西 ), ), Positioned( top: 0, left: 0, width: 100, height: 100, child: TripleTapDetector( requiredTaps: 10, onTripleTap: () async { IXDeveloperTools.show(); }, child: const SizedBox.shrink(), // 不显示任何东西 ), ), if (Configs.debug) Positioned( bottom: 200, right: 10, child: ClickOpacity( child: Container( width: 40, height: 40, decoration: BoxDecoration( color: Get.reactiveTheme.primaryColor.withValues( alpha: 0.5, ), borderRadius: BorderRadius.circular(40), ), child: Icon(Icons.adb, color: Get.reactiveTheme.primaryColor), ), onTap: () { // 使用简化版开发者工具 IXDeveloperTools.show(); }, ), ), Positioned( bottom: 300, right: 10, child: ClickOpacity( child: Container( width: 40, height: 40, decoration: BoxDecoration( color: Get.reactiveTheme.primaryColor.withValues(alpha: 0.5), borderRadius: BorderRadius.circular(40), ), child: Icon( ReactiveTheme.isLightTheme ? Icons.sunny : Icons.nightlight, color: Get.reactiveTheme.primaryColor, ), ), onTap: () { // 切换主题 IXTheme.changeTheme(); }, ), ), ], ), ); } }