Merge branch 'master' of https://gitee.com/starlock-cn/app-starlock
This commit is contained in:
commit
02419fca61
@ -57,7 +57,6 @@ class _MyAppState extends State<MyApp> with WidgetsBindingObserver, BaseWidget {
|
||||
locale = const Locale('zh', 'CN');
|
||||
}
|
||||
}
|
||||
// print("localelocalelocalelocalelocale locale:${locale} locale.languageCode:${locale.languageCode} locale.countryCode:${locale.countryCode} supportedLocales:${supportedLocales}");
|
||||
AppManager()
|
||||
.setLanCode(code: '${locale!.languageCode}_${locale.countryCode}');
|
||||
return locale;
|
||||
@ -93,7 +92,8 @@ class _MyAppState extends State<MyApp> with WidgetsBindingObserver, BaseWidget {
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 36.sp),
|
||||
),
|
||||
splashColor: Colors.transparent, // 点击时的高亮效果设置为透明
|
||||
splashColor: Colors.transparent,
|
||||
// 点击时的高亮效果设置为透明
|
||||
highlightColor: Colors.transparent,
|
||||
),
|
||||
debugShowCheckedModeBanner: false,
|
||||
|
||||
43
star_lock/lib/debug/controller.dart
Normal file
43
star_lock/lib/debug/controller.dart
Normal file
@ -0,0 +1,43 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:rxdart/rxdart.dart';
|
||||
import 'package:star_lock/debug/log.dart';
|
||||
|
||||
|
||||
class DebugConsoleController {
|
||||
final List<DebugConsoleLog> logs;
|
||||
final _streamController = BehaviorSubject<List<DebugConsoleLog>>();
|
||||
|
||||
DebugConsoleController({ List<DebugConsoleLog>? logs }) : logs = logs ?? [];
|
||||
|
||||
Stream<List<DebugConsoleLog>> get stream => _streamController.stream;
|
||||
|
||||
void close() => _streamController.close();
|
||||
void notifyUpdate() => _streamController.add(logs);
|
||||
|
||||
void log(
|
||||
Object? message, {
|
||||
DebugConsoleLevel level = DebugConsoleLevel.normal,
|
||||
DateTime? timestamp,
|
||||
StackTrace? stackTrace,
|
||||
}) {
|
||||
logs.add(
|
||||
DebugConsoleLog(
|
||||
message: message,
|
||||
level: level,
|
||||
timestamp: timestamp,
|
||||
stackTrace: stackTrace,
|
||||
)
|
||||
);
|
||||
notifyUpdate();
|
||||
}
|
||||
|
||||
List<DebugConsoleLog> getLogsByLevel(DebugConsoleLevel level) {
|
||||
return logs.where((log) => log.level == level).toList();
|
||||
}
|
||||
|
||||
void clear() {
|
||||
logs.clear();
|
||||
notifyUpdate();
|
||||
}
|
||||
}
|
||||
426
star_lock/lib/debug/debug_console.dart
Normal file
426
star_lock/lib/debug/debug_console.dart
Normal file
@ -0,0 +1,426 @@
|
||||
library debug_console;
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:star_lock/debug/controller.dart';
|
||||
import 'package:star_lock/debug/log.dart';
|
||||
import 'package:star_lock/debug/tile.dart';
|
||||
import 'package:star_lock/debug/utils/scrollable.dart';
|
||||
|
||||
/// # Debug Console
|
||||
///
|
||||
/// A console for debugging Flutter apps, and displaying console messages on the widget.
|
||||
///
|
||||
/// Check the console for prints and errors, while you're testing it, all within your app. Make your own logging or watch for console prints.
|
||||
///
|
||||
/// ## Features
|
||||
///
|
||||
/// * Log your messages
|
||||
/// * Display console messages and errors
|
||||
/// * Use different levels for better emphasis
|
||||
/// * Filter the logs
|
||||
/// * Add extra actions to execute from the Debug Console menu
|
||||
/// * Check StackTrace of errors
|
||||
class DebugConsole extends StatefulWidget {
|
||||
final DebugConsoleController controller;
|
||||
final List<PopupMenuItem<void>> actions;
|
||||
|
||||
final bool expandStackTrace;
|
||||
final String? savePath;
|
||||
|
||||
/// # Debug Console
|
||||
///
|
||||
/// A console for debugging Flutter apps, and displaying console messages on the widget.
|
||||
///
|
||||
/// Check the console for prints and errors, while you're testing it, all within your app. Make your own logging or watch for console prints.
|
||||
///
|
||||
/// ## Features
|
||||
///
|
||||
/// * Log your messages
|
||||
/// * Display console messages and errors
|
||||
/// * Use different levels for better emphasis
|
||||
/// * Filter the logs
|
||||
/// * Add extra actions to execute from the Debug Console menu
|
||||
/// * Check StackTrace of errors
|
||||
DebugConsole({
|
||||
key,
|
||||
DebugConsoleController? controller,
|
||||
this.actions = const [],
|
||||
this.expandStackTrace = false,
|
||||
this.savePath,
|
||||
}) : controller = controller ?? DebugConsole.instance;
|
||||
|
||||
@override
|
||||
State<DebugConsole> createState() => _DebugConsoleState();
|
||||
|
||||
static DebugConsoleController? _instance;
|
||||
|
||||
static DebugConsoleController get instance {
|
||||
_instance ??= DebugConsoleController(
|
||||
logs: DebugConsoleLog.fromFile(DebugConsole.loadPath));
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
static String loadPath = 'debug_console.log';
|
||||
|
||||
/// Adds a log to the root controller, attached with a message, level, timestamp and stack trace.
|
||||
///
|
||||
/// The default level is `DebugConsoleLogLevel.normal`.
|
||||
///
|
||||
/// Same as:
|
||||
/// ```dart
|
||||
/// DebugConsole.instance.log( ... );
|
||||
/// ```
|
||||
static void log(
|
||||
Object? message, {
|
||||
DebugConsoleLevel level = DebugConsoleLevel.normal,
|
||||
DateTime? timestamp,
|
||||
StackTrace? stackTrace,
|
||||
}) =>
|
||||
instance.log(
|
||||
message,
|
||||
level: level,
|
||||
timestamp: timestamp,
|
||||
stackTrace: stackTrace,
|
||||
);
|
||||
|
||||
/// Clears the logs of the root controller.
|
||||
///
|
||||
/// Same as:
|
||||
/// ```dart
|
||||
/// DebugConsole.instance.clear();
|
||||
/// ```
|
||||
static void clear() => instance.clear();
|
||||
|
||||
/// Adds a log to the root controller, with the level `DebugConsoleLevel.info`.
|
||||
///
|
||||
/// Same as:
|
||||
/// ```dart
|
||||
/// DebugConsole.log(message, level: DebugConsoleLevel.info, ... );
|
||||
/// ```
|
||||
static void info(
|
||||
Object? message, {
|
||||
DateTime? timestamp,
|
||||
StackTrace? stackTrace,
|
||||
}) =>
|
||||
log(
|
||||
message,
|
||||
level: DebugConsoleLevel.info,
|
||||
timestamp: timestamp,
|
||||
stackTrace: stackTrace,
|
||||
);
|
||||
|
||||
/// Adds a log to the root controller, with the level `DebugConsoleLevel.warning`.
|
||||
///
|
||||
/// Same as:
|
||||
/// ```dart
|
||||
/// DebugConsole.log(message, level: DebugConsoleLevel.warning, ... );
|
||||
/// ```
|
||||
static void warning(
|
||||
Object? message, {
|
||||
DateTime? timestamp,
|
||||
StackTrace? stackTrace,
|
||||
}) =>
|
||||
log(
|
||||
message,
|
||||
level: DebugConsoleLevel.warning,
|
||||
timestamp: timestamp,
|
||||
stackTrace: stackTrace,
|
||||
);
|
||||
|
||||
/// Adds a log to the root controller, with the level `DebugConsoleLevel.error`.
|
||||
///
|
||||
/// Same as:
|
||||
/// ```dart
|
||||
/// DebugConsole.log(message, level: DebugConsoleLevel.error, ... );
|
||||
/// ```
|
||||
static void error(
|
||||
Object? message, {
|
||||
DateTime? timestamp,
|
||||
StackTrace? stackTrace,
|
||||
}) =>
|
||||
log(
|
||||
message,
|
||||
level: DebugConsoleLevel.error,
|
||||
timestamp: timestamp,
|
||||
stackTrace: stackTrace,
|
||||
);
|
||||
|
||||
/// Adds a log to the root controller, with the level `DebugConsoleLevel.fatal`.
|
||||
///
|
||||
/// Same as:
|
||||
/// ```dart
|
||||
/// DebugConsole.log(message, level: DebugConsoleLevel.fatal, ... );
|
||||
/// ```
|
||||
static void fatal(
|
||||
Object? message, {
|
||||
DateTime? timestamp,
|
||||
StackTrace? stackTrace,
|
||||
}) =>
|
||||
log(
|
||||
message,
|
||||
level: DebugConsoleLevel.fatal,
|
||||
timestamp: timestamp,
|
||||
stackTrace: stackTrace,
|
||||
);
|
||||
|
||||
/// Adds a log to the root controller, with the level `DebugConsoleLevel.debug`.
|
||||
///
|
||||
/// Same as:
|
||||
/// ```dart
|
||||
/// DebugConsole.log(message, level: DebugConsoleLevel.debug, ... );
|
||||
/// ```
|
||||
static void debug(
|
||||
Object? message, {
|
||||
DateTime? timestamp,
|
||||
StackTrace? stackTrace,
|
||||
}) =>
|
||||
log(
|
||||
message,
|
||||
level: DebugConsoleLevel.debug,
|
||||
timestamp: timestamp,
|
||||
stackTrace: stackTrace,
|
||||
);
|
||||
|
||||
/// Listen for prints and errors, to catch all messages in your app.
|
||||
///
|
||||
/// Everything inside that function will be automatically logged.
|
||||
///
|
||||
/// ```dart
|
||||
/// DebugConsole.listen(() {
|
||||
/// runApp(const MyApp());
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// * A controller can be given, instead of logging to the root.
|
||||
static void listen(void Function() body,
|
||||
{DebugConsoleController? controller}) {
|
||||
controller ??= DebugConsole.instance;
|
||||
runZoned(body,
|
||||
zoneSpecification: ZoneSpecification(
|
||||
print: (Zone self, ZoneDelegate parent, Zone zone, String line) {
|
||||
controller!.log(line);
|
||||
parent.print(zone, line);
|
||||
},
|
||||
handleUncaughtError: (Zone self, ZoneDelegate parent, Zone zone,
|
||||
Object error, StackTrace stackTrace) {
|
||||
controller!.log(error,
|
||||
level: DebugConsoleLevel.error, stackTrace: stackTrace);
|
||||
parent.handleUncaughtError(zone, error, stackTrace);
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
class _DebugConsoleState extends State<DebugConsole> {
|
||||
StreamSubscription<List<DebugConsoleLog>>? subscription;
|
||||
List<DebugConsoleLog> logs = [];
|
||||
|
||||
bool expandStackTrace = false;
|
||||
bool save = true;
|
||||
String filter = '';
|
||||
|
||||
TextEditingController? textController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
textController = TextEditingController();
|
||||
expandStackTrace = widget.expandStackTrace;
|
||||
logs = widget.controller.logs;
|
||||
subscription = widget.controller.stream.listen((logs) {
|
||||
if (widget.savePath != null && save) saveToFile(logs: logs);
|
||||
logs.sort((a, b) => a.timestamp.compareTo(b.timestamp));
|
||||
logs = logs.reversed.toList();
|
||||
setState(() => this.logs = logs);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
subscription?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final filteredLogs = filter.isEmpty
|
||||
? logs
|
||||
: logs
|
||||
.where((log) => filter.split(',').any((filter) {
|
||||
filter = filter.trim();
|
||||
return filter.isNotEmpty &&
|
||||
log.message.toLowerCase().contains(filter.toLowerCase());
|
||||
}))
|
||||
.toList();
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('日志记录'),
|
||||
actions: [
|
||||
PopupMenuButton<void>(
|
||||
icon: const Icon(Icons.more_vert),
|
||||
itemBuilder: (context) => [
|
||||
...widget.actions,
|
||||
if (widget.actions.isNotEmpty) const PopupMenuDivider(),
|
||||
PopupMenuItem(
|
||||
child: StatefulBuilder(
|
||||
builder: (context, setCheckboxState) => Row(
|
||||
children: [
|
||||
const Expanded(child: Text('暂停日志记录')),
|
||||
Checkbox(
|
||||
value: subscription!.isPaused,
|
||||
onChanged: (value) =>
|
||||
setCheckboxState(() => toggleLogging(!value!)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
onTap: () => toggleLogging(),
|
||||
),
|
||||
PopupMenuItem(
|
||||
child: StatefulBuilder(
|
||||
builder: (context, setCheckboxState) => Row(
|
||||
children: [
|
||||
const Expanded(child: Text('展开 StackTrace')),
|
||||
Checkbox(
|
||||
value: expandStackTrace,
|
||||
onChanged: (value) => setCheckboxState(
|
||||
() => setState(() => expandStackTrace = value!)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
onTap: () =>
|
||||
setState(() => expandStackTrace = !expandStackTrace),
|
||||
),
|
||||
if (widget.savePath != null)
|
||||
PopupMenuItem(
|
||||
child: StatefulBuilder(
|
||||
builder: (context, setCheckboxState) => Row(
|
||||
children: [
|
||||
const Expanded(child: Text('保存')),
|
||||
Checkbox(
|
||||
value: save,
|
||||
onChanged: (value) {
|
||||
if (value!) saveToFile();
|
||||
setCheckboxState(
|
||||
() => setState(() => save = value));
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
if (!save) saveToFile();
|
||||
setState(() => save = !save);
|
||||
},
|
||||
),
|
||||
PopupMenuItem(
|
||||
onTap: () => widget.controller.clear(),
|
||||
child: const Text('清除日志'),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
body: Stack(
|
||||
children: [
|
||||
logs.isEmpty
|
||||
? const Padding(
|
||||
padding: EdgeInsets.only(bottom: 50),
|
||||
child: Center(child: Text('没有日志')),
|
||||
)
|
||||
: ListView.builder(
|
||||
padding: const EdgeInsets.only(bottom: 75),
|
||||
reverse: true,
|
||||
itemCount: filteredLogs.length,
|
||||
itemBuilder: (context, index) {
|
||||
final log = filteredLogs[index];
|
||||
return DebugConsoleTile(log,
|
||||
key: ValueKey(log.timestamp),
|
||||
expanded: expandStackTrace);
|
||||
},
|
||||
),
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: Container(
|
||||
margin: const EdgeInsets.all(15),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: textController,
|
||||
decoration: InputDecoration(
|
||||
filled: true,
|
||||
fillColor: Colors.white,
|
||||
contentPadding:
|
||||
const EdgeInsets.symmetric(horizontal: 15),
|
||||
hintText: 'Filter logs',
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
),
|
||||
),
|
||||
onChanged: (value) => setState(() => filter = value),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 5),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.close),
|
||||
onPressed: () {
|
||||
textController!.clear();
|
||||
setState(() => filter = '');
|
||||
},
|
||||
),
|
||||
const SizedBox(width: 5),
|
||||
FloatingActionButton(
|
||||
tooltip: subscription!.isPaused
|
||||
? 'Resume logging'
|
||||
: 'Pause logging',
|
||||
onPressed: () => toggleLogging(),
|
||||
child: Icon(subscription!.isPaused
|
||||
? Icons.play_arrow
|
||||
: Icons.pause),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void saveToFile({List<DebugConsoleLog>? logs, String? path}) {
|
||||
path ??= widget.savePath;
|
||||
if (path == null) return;
|
||||
logs ??= this.logs;
|
||||
|
||||
logs.sort((a, b) => a.timestamp.compareTo(b.timestamp));
|
||||
logs = logs.reversed.toList();
|
||||
|
||||
final file = File(path);
|
||||
if (logs.isEmpty) {
|
||||
file.delete();
|
||||
} else {
|
||||
file.writeAsString(
|
||||
logs.map((log) => log.toString()).join('\n'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void toggleLogging([bool? paused]) {
|
||||
paused ??= subscription!.isPaused;
|
||||
setState(() {
|
||||
if (paused!) {
|
||||
subscription!.resume();
|
||||
} else {
|
||||
subscription!.pause();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
58
star_lock/lib/debug/debug_tool.dart
Normal file
58
star_lock/lib/debug/debug_tool.dart
Normal file
@ -0,0 +1,58 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:star_lock/debug/controller.dart';
|
||||
import 'package:star_lock/debug/debug_console.dart';
|
||||
|
||||
class DeBug {
|
||||
static void log(String message, {String? tag}) {
|
||||
DebugConsole.info(message);
|
||||
Get.log(message);
|
||||
}
|
||||
|
||||
static bool open = false;
|
||||
static bool openInfo = false;
|
||||
|
||||
static void showFloatWidget() {
|
||||
Rx<Offset> offset = Offset(20, 100).obs; // 初始位置
|
||||
OverlayEntry overlayEntry = OverlayEntry(builder: (context) {
|
||||
return Obx(() => Positioned(
|
||||
left: offset.value.dx,
|
||||
top: offset.value.dy,
|
||||
child: GestureDetector(
|
||||
onPanUpdate: (details) {
|
||||
offset.value += details.delta;
|
||||
},
|
||||
child: FloatingActionButton(
|
||||
child: Icon(Icons.bug_report),
|
||||
onPressed: () {
|
||||
if (openInfo) {
|
||||
Get.back();
|
||||
return;
|
||||
}
|
||||
if (open) {
|
||||
open = false;
|
||||
Get.back();
|
||||
} else {
|
||||
open = true;
|
||||
Get.to(WillPopScope(
|
||||
onWillPop: () async {
|
||||
open = false;
|
||||
return true;
|
||||
},
|
||||
child: DebugConsole(
|
||||
controller: DebugConsole.instance,
|
||||
actions: [],
|
||||
expandStackTrace: false,
|
||||
savePath: null,
|
||||
),
|
||||
));
|
||||
}
|
||||
},
|
||||
),
|
||||
)));
|
||||
});
|
||||
Get.key.currentState?.overlay?.insert(overlayEntry);
|
||||
}
|
||||
|
||||
DeBug();
|
||||
}
|
||||
92
star_lock/lib/debug/log.dart
Normal file
92
star_lock/lib/debug/log.dart
Normal file
@ -0,0 +1,92 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class DebugConsoleLog {
|
||||
/// Message of the log.
|
||||
final String message;
|
||||
|
||||
/// Log level, used to determine the color and importance of the log.
|
||||
final DebugConsoleLevel level;
|
||||
|
||||
/// Time of when the log was created.
|
||||
final DateTime timestamp;
|
||||
|
||||
final StackTrace? stackTrace;
|
||||
|
||||
DebugConsoleLog({
|
||||
Object? message,
|
||||
this.level = DebugConsoleLevel.normal,
|
||||
DateTime? timestamp,
|
||||
this.stackTrace,
|
||||
}) : message = '$message',
|
||||
timestamp = timestamp ?? DateTime.now();
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '[$level] $timestamp\n$message\n---${stackTrace != null ? ':' : ''}\n${stackTrace != null ? '${stackTrace ?? ''}---\n' : ''}';
|
||||
}
|
||||
|
||||
static List<DebugConsoleLog> fromFile(String filePath) {
|
||||
final List<DebugConsoleLog> logs = [];
|
||||
|
||||
final File file = File(filePath);
|
||||
if (!file.existsSync()) return logs;
|
||||
|
||||
final RegExp regex = RegExp(
|
||||
r'\[(?<level>.*)\] (?<timestamp>.*)\n(?<message>(?:.|\n)*?)\n---(?::\n(?<stackTrace>(?:.|\n)*?)---\n)?',
|
||||
multiLine: true,
|
||||
);
|
||||
final matches = regex.allMatches(file.readAsStringSync());
|
||||
for (final match in matches) {
|
||||
final String level = match.namedGroup('level') ?? 'normal';
|
||||
final String timestamp = match.namedGroup('timestamp') ?? '';
|
||||
final String message = match.namedGroup('message') ?? '';
|
||||
final String stackTrace = match.namedGroup('stackTrace') ?? '';
|
||||
|
||||
logs.add(DebugConsoleLog(
|
||||
message: message,
|
||||
level: DebugConsoleLevel.values.firstWhere(
|
||||
(DebugConsoleLevel findingLevel) =>
|
||||
level.toString() == findingLevel.toString(),
|
||||
orElse: () => DebugConsoleLevel.normal,
|
||||
),
|
||||
timestamp: DateTime.parse(timestamp),
|
||||
stackTrace:
|
||||
stackTrace.isNotEmpty ? StackTrace.fromString(stackTrace) : null,
|
||||
));
|
||||
}
|
||||
|
||||
return logs;
|
||||
}
|
||||
}
|
||||
|
||||
class DebugConsoleLevel {
|
||||
static const DebugConsoleLevel normal = DebugConsoleLevel('Normal');
|
||||
static const DebugConsoleLevel info =
|
||||
DebugConsoleLevel('Info', Colors.lightBlueAccent);
|
||||
static const DebugConsoleLevel warning =
|
||||
DebugConsoleLevel('Warning', Colors.deepOrange);
|
||||
static const DebugConsoleLevel error =
|
||||
DebugConsoleLevel('Error', Colors.redAccent);
|
||||
static const DebugConsoleLevel fatal = DebugConsoleLevel('Fatal', Colors.red);
|
||||
static const DebugConsoleLevel debug =
|
||||
DebugConsoleLevel('Debug', Colors.blue);
|
||||
|
||||
static const List<DebugConsoleLevel> values = [
|
||||
normal,
|
||||
info,
|
||||
warning,
|
||||
error,
|
||||
fatal,
|
||||
debug,
|
||||
];
|
||||
|
||||
final String name;
|
||||
final Color? color;
|
||||
|
||||
const DebugConsoleLevel(this.name, [this.color]);
|
||||
|
||||
@override
|
||||
String toString() => name;
|
||||
}
|
||||
81
star_lock/lib/debug/popup.dart
Normal file
81
star_lock/lib/debug/popup.dart
Normal file
@ -0,0 +1,81 @@
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:star_lock/debug/controller.dart';
|
||||
import 'package:star_lock/debug/debug_console.dart';
|
||||
|
||||
/// A widget that adds a floating button for debugging purposes.
|
||||
class DebugConsolePopup extends StatefulWidget {
|
||||
final Widget child;
|
||||
|
||||
final bool showButton;
|
||||
|
||||
final DebugConsoleController controller;
|
||||
final List<PopupMenuItem<void>> actions;
|
||||
final bool expandStackTrace;
|
||||
final String? savePath;
|
||||
|
||||
DebugConsolePopup({
|
||||
key,
|
||||
required this.child,
|
||||
|
||||
this.showButton = true,
|
||||
|
||||
DebugConsoleController? controller,
|
||||
this.actions = const [],
|
||||
this.expandStackTrace = false,
|
||||
this.savePath,
|
||||
}) :
|
||||
controller = controller ?? DebugConsole.instance;
|
||||
|
||||
@override
|
||||
State<DebugConsolePopup> createState() => _DebugConsolePopupState();
|
||||
}
|
||||
|
||||
class _DebugConsolePopupState extends State<DebugConsolePopup> {
|
||||
bool isOpen = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
children: [
|
||||
widget.child,
|
||||
if (widget.showButton)
|
||||
Positioned(
|
||||
right: 15,
|
||||
bottom: 15,
|
||||
child: FloatingActionButton(
|
||||
child: const Icon(Icons.bug_report),
|
||||
onPressed: () => popup(true),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
void popup([ bool? open ]) {
|
||||
open ??= !isOpen;
|
||||
if (open == isOpen) return;
|
||||
if (open) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => WillPopScope(
|
||||
onWillPop: () async {
|
||||
isOpen = false;
|
||||
return true;
|
||||
},
|
||||
child: DebugConsole(
|
||||
controller: widget.controller,
|
||||
actions: widget.actions,
|
||||
expandStackTrace: widget.expandStackTrace,
|
||||
savePath: widget.savePath,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
isOpen = open;
|
||||
}
|
||||
}
|
||||
134
star_lock/lib/debug/tile.dart
Normal file
134
star_lock/lib/debug/tile.dart
Normal file
@ -0,0 +1,134 @@
|
||||
import 'package:expandable/expandable.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:star_lock/debug/debug_tool.dart';
|
||||
import 'package:star_lock/debug/log.dart';
|
||||
|
||||
class DebugConsoleTile extends StatefulWidget {
|
||||
final DebugConsoleLog log;
|
||||
final bool expanded;
|
||||
|
||||
const DebugConsoleTile(this.log, {key, this.expanded = false});
|
||||
|
||||
@override
|
||||
State<DebugConsoleTile> createState() => _DebugConsoleTileState();
|
||||
}
|
||||
|
||||
class _DebugConsoleTileState extends State<DebugConsoleTile> {
|
||||
ExpandableController? controller;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
controller = ExpandableController(initialExpanded: widget.expanded);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
color: widget.log.level.color?.withOpacity(0.05),
|
||||
child: Container(
|
||||
color: widget.log.level.color?.withOpacity(0.05),
|
||||
child: InkWell(
|
||||
onTap: widget.log.stackTrace != null
|
||||
? () {
|
||||
controller!.toggle();
|
||||
setState(() {});
|
||||
}
|
||||
: () async {
|
||||
DeBug.openInfo = true;
|
||||
await Get.to(DeBugText(widget.log));
|
||||
DeBug.openInfo = false;
|
||||
},
|
||||
child: ExpandablePanel(
|
||||
controller: controller,
|
||||
theme: const ExpandableThemeData(
|
||||
tapBodyToCollapse: false,
|
||||
tapHeaderToExpand: false,
|
||||
hasIcon: false,
|
||||
),
|
||||
header: ListTile(
|
||||
textColor: widget.log.level.color,
|
||||
title: Text(
|
||||
widget.log.message,
|
||||
maxLines: 5,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
trailing: Column(
|
||||
children: [
|
||||
SizedBox(height: widget.log.stackTrace != null ? 4 : 15),
|
||||
Opacity(
|
||||
opacity: 0.5,
|
||||
child: widget.log.timestamp
|
||||
.difference(DateTime.now())
|
||||
.inDays >
|
||||
1
|
||||
? Text(
|
||||
'${widget.log.timestamp.toIso8601String().substring(0, 10)} ${widget.log.timestamp.toIso8601String().substring(11, 19)}')
|
||||
: Text(widget.log.timestamp
|
||||
.toIso8601String()
|
||||
.substring(11, 19)),
|
||||
),
|
||||
if (widget.log.stackTrace != null)
|
||||
AnimatedRotation(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
curve: Curves.easeInOut,
|
||||
turns: controller!.expanded ? -0.5 : 0,
|
||||
child: Icon(Icons.keyboard_arrow_down,
|
||||
color: widget.log.level.color),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
collapsed: Container(),
|
||||
expanded: widget.log.stackTrace != null
|
||||
? Opacity(
|
||||
opacity: 0.75,
|
||||
child: ListTile(
|
||||
textColor: widget.log.level.color,
|
||||
title: Text(widget.log.stackTrace.toString()),
|
||||
),
|
||||
)
|
||||
: Container(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DeBugText extends StatefulWidget {
|
||||
final DebugConsoleLog log;
|
||||
|
||||
DeBugText(this.log, {key});
|
||||
|
||||
@override
|
||||
State<DeBugText> createState() => _DeBugTextState();
|
||||
}
|
||||
|
||||
class _DeBugTextState extends State<DeBugText> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('详细日志'),
|
||||
),
|
||||
body: SafeArea(
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
Clipboard.setData(ClipboardData(text: widget.log.message));
|
||||
EasyLoading.showToast('已复制到剪切板');
|
||||
},
|
||||
child: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(18.w),
|
||||
child: Text(widget.log.message),
|
||||
)),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
35
star_lock/lib/debug/utils/scrollable.dart
Normal file
35
star_lock/lib/debug/utils/scrollable.dart
Normal file
@ -0,0 +1,35 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
// https://github.com/flutter/flutter/issues/63946#issuecomment-1000442747
|
||||
// - By: https://github.com/PaulBout1
|
||||
|
||||
class AllwaysScrollableFixedPositionScrollPhysics extends ScrollPhysics {
|
||||
const AllwaysScrollableFixedPositionScrollPhysics({ScrollPhysics? parent})
|
||||
: super(parent: parent);
|
||||
|
||||
@override
|
||||
AllwaysScrollableFixedPositionScrollPhysics applyTo(ScrollPhysics? ancestor) {
|
||||
return AllwaysScrollableFixedPositionScrollPhysics(parent: buildParent(ancestor));
|
||||
}
|
||||
|
||||
@override
|
||||
double adjustPositionForNewDimensions({
|
||||
required ScrollMetrics oldPosition,
|
||||
required ScrollMetrics newPosition,
|
||||
required bool isScrolling,
|
||||
required double velocity,
|
||||
}) {
|
||||
if (newPosition.extentBefore == 0) {
|
||||
return super.adjustPositionForNewDimensions(
|
||||
oldPosition: oldPosition,
|
||||
newPosition: newPosition,
|
||||
isScrolling: isScrolling,
|
||||
velocity: velocity,
|
||||
);
|
||||
}
|
||||
return newPosition.maxScrollExtent - oldPosition.extentAfter;
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldAcceptUserOffset(ScrollMetrics position) => true;
|
||||
}
|
||||
@ -26,6 +26,23 @@ class F {
|
||||
|
||||
static String get name => appFlavor?.name ?? '';
|
||||
|
||||
static bool get debug {
|
||||
switch (appFlavor) {
|
||||
case Flavor.local:
|
||||
return true;
|
||||
case Flavor.dev:
|
||||
return true;
|
||||
case Flavor.pre:
|
||||
return true;
|
||||
case Flavor.sky:
|
||||
return false;
|
||||
case Flavor.xhj:
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static String get title {
|
||||
switch (appFlavor) {
|
||||
case Flavor.local:
|
||||
@ -85,13 +102,16 @@ class F {
|
||||
case Flavor.local:
|
||||
case Flavor.dev:
|
||||
return const StarLockAMapKey(
|
||||
androidKey: 'b56b681ee89f4db43a5aa1879ae8cbfe', iosKey: 'bd4496e6598ef49796e3a80715035b4d');
|
||||
androidKey: 'b56b681ee89f4db43a5aa1879ae8cbfe',
|
||||
iosKey: 'bd4496e6598ef49796e3a80715035b4d');
|
||||
case Flavor.pre:
|
||||
return const StarLockAMapKey(
|
||||
androidKey: '11d49b3f4fc09c04a02bbb7500925ba2', iosKey: '883a3355d2d77c2fdc2667030dc97ffe');
|
||||
androidKey: '11d49b3f4fc09c04a02bbb7500925ba2',
|
||||
iosKey: '883a3355d2d77c2fdc2667030dc97ffe');
|
||||
case Flavor.sky:
|
||||
return const StarLockAMapKey(
|
||||
androidKey: 'fb0d2a3e4208b36452cf636aa025a24f', iosKey: '86ca725a12a629c280e116a317aaba19');
|
||||
androidKey: 'fb0d2a3e4208b36452cf636aa025a24f',
|
||||
iosKey: '86ca725a12a629c280e116a317aaba19');
|
||||
// case Flavor.xhj:
|
||||
// return const StarLockAMapKey(
|
||||
// androidKey: 'todo',
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:star_lock/debug/debug_console.dart';
|
||||
import 'package:star_lock/flavors.dart';
|
||||
import 'package:star_lock/translations/trans_lib.dart';
|
||||
import 'app.dart';
|
||||
@ -17,7 +18,13 @@ FutureOr<void> main() async {
|
||||
// 设置国际化信息
|
||||
await _initTranslation();
|
||||
|
||||
runApp(MyApp());
|
||||
if(F.debug){
|
||||
DebugConsole.listen(() {
|
||||
runApp(const MyApp());
|
||||
});
|
||||
}else{
|
||||
runApp(const MyApp());
|
||||
}
|
||||
|
||||
if (AppPlatform.isAndroid) {
|
||||
SystemUiOverlayStyle systemUiOverlayStyle =
|
||||
|
||||
@ -2,6 +2,7 @@ import 'dart:async';
|
||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:get/get_connect/http/src/request/request.dart';
|
||||
import 'package:star_lock/debug/log.dart';
|
||||
|
||||
FutureOr<Request> requestLogInterceptor(Request request) async {
|
||||
Get.log(
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:star_lock/login/login/starLock_login_page.dart';
|
||||
import 'package:star_lock/tools/appFirstEnterHandle.dart';
|
||||
import 'package:star_lock/debug/debug_tool.dart';
|
||||
import 'package:star_lock/tools/storage.dart';
|
||||
|
||||
import '../main/lockMian/lockMain/lockMain_page.dart';
|
||||
@ -15,9 +17,11 @@ class StarLockApplication extends StatefulWidget {
|
||||
class _StarLockApplicationState extends State<StarLockApplication> {
|
||||
@override
|
||||
void initState() {
|
||||
// TODO: implement initState
|
||||
super.initState();
|
||||
print("StarLockApplication initState");
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
DeBug.showFloatWidget();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@ -29,8 +29,11 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||
# 1.0.24+20240407:1、打包预发布环境给欧阳测试
|
||||
# 1.0.25+2024040702:1、打包预发布环境给欧阳测试
|
||||
# 1.0.26+20240408:打包预发布环境给欧阳测试
|
||||
# 1.0.27+2024041101:打包提审
|
||||
# 1.0.27+2024041102:打包预发布环境给田总测试
|
||||
|
||||
version: 1.0.26+20240408
|
||||
|
||||
version: 1.0.27+2024041102
|
||||
|
||||
environment:
|
||||
sdk: '>=2.12.0 <3.0.0'
|
||||
@ -156,8 +159,10 @@ dependencies:
|
||||
app_settings: ^5.1.1
|
||||
flutter_local_notifications: ^17.0.0
|
||||
fluwx: ^4.5.5
|
||||
|
||||
system_settings: ^2.0.0
|
||||
expandable: ^5.0.1
|
||||
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user