Compare commits

..

1 Commits

Author SHA1 Message Date
chen08209
d670f13cbc Add sqlite store
Optimize android quick action

Optimize backup and restore

Optimize more details
2026-01-24 10:40:42 +08:00
8 changed files with 42 additions and 177 deletions

View File

@@ -19,19 +19,10 @@ class Migration {
required Future<Config> Function(MigrationData data) sync,
}) async {
_oldVersion = await preferences.getVersion();
if (_oldVersion == currentVersion) {
try {
return Config.realFromJson(configMap);
} catch (_) {
final isV0 = configMap?['proxiesStyle'] != null;
if (isV0) {
_oldVersion = 0;
} else {
throw 'Local data is damaged. A reset is required to fix this issue.';
}
}
}
MigrationData data = MigrationData(configMap: configMap);
if (_oldVersion == currentVersion) {
return Config.realFromJson(configMap);
}
if (_oldVersion == 0 && configMap != null) {
final clashConfigMap = await preferences.getClashConfigMap();
if (clashConfigMap != null) {

View File

@@ -41,28 +41,26 @@ class AppController {
extension InitControllerExt on AppController {
Future<void> _init() async {
FlutterError.onError = (details) {
commonPrint.log(
'exception: ${details.exception} stack: ${details.stack}',
logLevel: LogLevel.warning,
);
};
updateTray();
autoUpdateProfiles();
autoCheckUpdate();
autoLaunch?.updateStatus(_ref.read(appSettingProvider).autoLaunch);
if (!_ref.read(appSettingProvider).silentLaunch) {
window?.show();
} else {
window?.hide();
try {
updateTray();
autoUpdateProfiles();
autoCheckUpdate();
autoLaunch?.updateStatus(_ref.read(appSettingProvider).autoLaunch);
if (!_ref.read(appSettingProvider).silentLaunch) {
window?.show();
} else {
window?.hide();
}
await _handleFailedPreference();
await _handlerDisclaimer();
await _showCrashlyticsTip();
await _connectCore();
await _initCore();
await _initStatus();
_ref.read(initProvider.notifier).value = true;
} catch (e) {
commonPrint.log('init error: $e');
}
await _handleFailedPreference();
await _handlerDisclaimer();
await _showCrashlyticsTip();
await _connectCore();
await _initCore();
await _initStatus();
_ref.read(initProvider.notifier).value = true;
}
Future<void> _handleFailedPreference() async {

View File

@@ -1,7 +1,6 @@
import 'dart:async';
import 'dart:io';
import 'package:fl_clash/pages/error.dart';
import 'package:fl_clash/state.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@@ -10,22 +9,11 @@ import 'application.dart';
import 'common/common.dart';
Future<void> main() async {
try {
WidgetsFlutterBinding.ensureInitialized();
final version = await system.version;
final container = await globalState.init(version);
HttpOverrides.global = FlClashHttpOverrides();
runApp(
UncontrolledProviderScope(
container: container,
child: const Application(),
),
);
} catch (e, s) {
return runApp(
MaterialApp(
home: InitErrorScreen(error: e, stack: s),
),
);
}
WidgetsFlutterBinding.ensureInitialized();
final version = await system.version;
final container = await globalState.init(version);
HttpOverrides.global = FlClashHttpOverrides();
runApp(
UncontrolledProviderScope(container: container, child: const Application()),
);
}

View File

@@ -1,120 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class InitErrorScreen extends StatelessWidget {
final Object error;
final StackTrace stack;
const InitErrorScreen({super.key, required this.error, required this.stack});
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
return Scaffold(
appBar: AppBar(
title: const Text('Init Failed'),
backgroundColor: colorScheme.error,
foregroundColor: colorScheme.onError,
elevation: 0,
),
body: SafeArea(
child: SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(
Icons.report_problem,
color: colorScheme.error,
size: 32,
),
const SizedBox(width: 12),
const Expanded(
child: Text(
'The application encountered a critical error during startup and cannot continue.',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
),
],
),
const SizedBox(height: 24),
_buildSectionLabel('Error Details:'),
Container(
width: double.infinity,
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: colorScheme.errorContainer.withOpacity(0.5),
borderRadius: BorderRadius.circular(8),
border: Border.all(color: colorScheme.error.withOpacity(0.5)),
),
child: SelectableText(
error.toString(),
style: TextStyle(
color: colorScheme.onErrorContainer,
fontWeight: FontWeight.w600,
),
),
),
const SizedBox(height: 24),
_buildSectionLabel('Stack Trace:'),
Container(
width: double.infinity,
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Theme.of(context).brightness == Brightness.dark
? Colors.grey[900]
: Colors.grey[200],
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.grey.withOpacity(0.5)),
),
child: SelectableText(
stack.toString(),
style: const TextStyle(
fontFamily: 'monospace', // Makes code easier to read
fontSize: 12,
),
),
),
const SizedBox(height: 80),
],
),
),
),
floatingActionButton: FloatingActionButton.extended(
onPressed: () => _copyToClipboard(context),
label: const Text('Copy Details'),
icon: const Icon(Icons.copy),
backgroundColor: colorScheme.error,
foregroundColor: colorScheme.onError,
),
);
}
Widget _buildSectionLabel(String text) {
return Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: Text(
text,
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 15),
),
);
}
void _copyToClipboard(BuildContext context) {
final text = '=== ERROR ===\n$error\n\n=== STACK TRACE ===\n$stack';
Clipboard.setData(ClipboardData(text: text));
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Error details copied to clipboard'),
duration: Duration(seconds: 2),
),
);
}
}

View File

@@ -1,4 +1,3 @@
export 'editor.dart';
export 'error.dart';
export 'home.dart';
export 'scan.dart';
export 'editor.dart';

View File

@@ -22,6 +22,7 @@ import 'package:url_launcher/url_launcher.dart';
import 'common/common.dart';
import 'database/database.dart';
import 'enum/enum.dart';
import 'l10n/l10n.dart';
import 'models/models.dart';
@@ -55,6 +56,12 @@ class GlobalState {
}
Future<ProviderContainer> init(int version) async {
FlutterError.onError = (details) {
commonPrint.log(
'exception: ${details.exception} stack: ${details.stack}',
logLevel: LogLevel.warning,
);
};
coreSHA256 = const String.fromEnvironment('CORE_SHA256');
isPre = const String.fromEnvironment('APP_ENV') != 'stable';
await _initDynamicColor();
@@ -88,7 +95,9 @@ class GlobalState {
configMap,
sync: (data) async {
final newConfigMap = data.configMap;
final config = Config.realFromJson(newConfigMap);
final config = newConfigMap != null
? Config.fromJson(newConfigMap)
: Config(themeProps: defaultThemeProps);
await Future.wait([
database.restore(data.profiles, data.scripts, data.rules, data.links),
preferences.saveConfig(config),

View File

@@ -30,7 +30,7 @@ class _EditProfileViewState extends State<EditProfileView> {
late final TextEditingController _labelController;
late final TextEditingController _urlController;
late final TextEditingController _autoUpdateDurationController;
late bool _autoUpdate;
late final bool _autoUpdate;
String? _rawText;
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final _fileInfoNotifier = ValueNotifier<FileInfo?>(null);

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.92+2026012403
version: 0.8.92+2026012301
environment:
sdk: '>=3.8.0 <4.0.0'