结构初始化

This commit is contained in:
LollipopKit
2021-09-13 14:25:54 +08:00
commit 4dd509a1d9
90 changed files with 2715 additions and 0 deletions

34
lib/core/analysis.dart Normal file
View File

@@ -0,0 +1,34 @@
import 'dart:async';
import 'package:countly_flutter/countly_flutter.dart';
class Analysis {
static const _url = 'https://countly.xuty.cc';
static const _key = '80372a2a66424b32d0ac8991bfa1ef058bd36b1f';
static bool _enabled = false;
static Future<void> init(bool debug) async {
if (_url.isEmpty || _key.isEmpty) {
return;
}
_enabled = true;
await Countly.setLoggingEnabled(debug);
await Countly.init(_url, _key);
await Countly.start();
await Countly.enableCrashReporting();
await Countly.giveAllConsent();
print('Countly init successfully.');
}
static void recordView(String view) {
if (!_enabled) return;
Countly.recordView(view);
}
static void recordException(Object exception, [bool fatal = false]) {
if (!_enabled) return;
Countly.logException(exception.toString(), !fatal, null);
}
}

25
lib/core/build_mode.dart Normal file
View File

@@ -0,0 +1,25 @@
/// See: https://github.com/flutter/flutter/issues/11392
///
enum _BuildMode {
release,
debug,
profile,
}
_BuildMode _buildMode = (() {
if (const bool.fromEnvironment('dart.vm.product')) {
return _BuildMode.release;
}
var result = _BuildMode.profile;
assert(() {
result = _BuildMode.debug;
return true;
}());
return result;
}());
class BuildMode {
static bool isDebug = (_buildMode == _BuildMode.debug);
static bool isProfile = (_buildMode == _BuildMode.profile);
static bool isRelease = (_buildMode == _BuildMode.release);
}

View File

@@ -0,0 +1,78 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:hive/hive.dart';
class PersistentStore<E> {
late Box<E> box;
Future<PersistentStore<E>> init({String boxName = 'defaultBox'}) async {
box = await Hive.openBox(boxName);
return this;
}
StoreProperty<T> property<T>(String key, {T? defaultValue}) {
return StoreProperty<T>(box, key, defaultValue);
}
}
class StoreProperty<T> {
StoreProperty(this._box, this._key, this.defaultValue);
final Box _box;
final String _key;
T? defaultValue;
ValueListenable<T> listenable() {
return PropertyListenable<T>(_box, _key, defaultValue);
}
T? fetch() {
return _box.get(_key, defaultValue: defaultValue);
}
Future<void> put(T value) {
return _box.put(_key, value);
}
Future<void> delete() {
return _box.delete(_key);
}
}
class PropertyListenable<T> extends ValueListenable<T> {
PropertyListenable(this.box, this.key, this.defaultValue);
final Box box;
final String key;
T? defaultValue;
final List<VoidCallback> _listeners = [];
StreamSubscription? _subscription;
@override
void addListener(VoidCallback listener) {
_subscription ??= box.watch().listen((event) {
if (key == event.key) {
for (var listener in _listeners) {
listener();
}
}
});
_listeners.add(listener);
}
@override
void removeListener(VoidCallback listener) {
_listeners.remove(listener);
if (_listeners.isEmpty) {
_subscription?.cancel();
_subscription = null;
}
}
@override
T get value => box.get(key, defaultValue: defaultValue);
}

View File

@@ -0,0 +1,36 @@
import 'dart:async';
import 'package:flutter/widgets.dart';
class ProviderBase with ChangeNotifier {
void setState(void Function() callback) {
callback();
notifyListeners();
}
}
enum ProviderState {
idle,
busy,
}
class BusyProvider extends ProviderBase {
bool _isBusy = false;
bool get isBusy => _isBusy;
setBusyState([bool isBusy = true]) {
_isBusy = isBusy;
notifyListeners();
}
FutureOr<T> busyRun<T>(FutureOr<T> Function() func) async {
setBusyState(true);
try {
return await Future.sync(func);
} catch (e) {
rethrow;
} finally {
setBusyState(false);
}
}
}

14
lib/core/route.dart Normal file
View File

@@ -0,0 +1,14 @@
import 'package:flutter/material.dart';
import 'package:toolbox/core/analysis.dart';
class AppRoute {
final Widget page;
final String title;
AppRoute(this.page, this.title);
void go(BuildContext context) {
Analysis.recordView(title);
Navigator.push(context, MaterialPageRoute(builder: (context) => page));
}
}

42
lib/core/update.dart Normal file
View File

@@ -0,0 +1,42 @@
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:toolbox/core/utils.dart';
import 'package:toolbox/data/provider/app.dart';
import 'package:toolbox/data/res/build_data.dart';
import 'package:toolbox/data/service/app.dart';
import 'package:toolbox/locator.dart';
Future<bool> isFileAvailable(String url) async {
try {
final resp = await Dio().head(url);
return resp.statusCode == 200;
} catch (e) {
print('update file not available: $e');
return false;
}
}
Future<void> doUpdate(BuildContext context, {bool force = false}) async {
final update = await locator<AppService>().getUpdate();
locator<AppProvider>().setNewestBuild(update.newest);
if (!force && update.newest <= BuildData.build) {
print('Update ignored due to current: ${BuildData.build}, '
'update: ${update.newest}');
return;
}
print('Update available: ${update.newest}');
if (Platform.isAndroid && !await isFileAvailable(update.android)) {
return;
}
showSnackBarWithAction(
context,
'${BuildData.name}有更新啦Ver${update.newest}\n${update.changelog}',
'更新',
() => openUrl(Platform.isAndroid ? update.android : update.ios));
}

42
lib/core/utils.dart Normal file
View File

@@ -0,0 +1,42 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
void unawaited(Future<void> future) {}
bool isDarkMode(BuildContext context) =>
Theme.of(context).brightness == Brightness.dark;
void showSnackBar(BuildContext context, Widget child) =>
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: child));
void showSnackBarWithAction(
BuildContext context, String content, String action, Function onTap) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(content),
action: SnackBarAction(
label: action,
onPressed: () => onTap,
),
));
}
Future<bool> openUrl(String url) async {
print('openUrl $url');
if (!await canLaunch(url)) {
print('canLaunch false');
return false;
}
final ok = await launch(url, forceSafariVC: false);
if (ok == true) {
return true;
}
print('launch $url failed');
return false;
}