结构初始化
This commit is contained in:
34
lib/core/analysis.dart
Normal file
34
lib/core/analysis.dart
Normal 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
25
lib/core/build_mode.dart
Normal 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);
|
||||
}
|
||||
78
lib/core/persistant_store.dart
Normal file
78
lib/core/persistant_store.dart
Normal 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);
|
||||
}
|
||||
36
lib/core/provider_base.dart
Normal file
36
lib/core/provider_base.dart
Normal 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
14
lib/core/route.dart
Normal 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
42
lib/core/update.dart
Normal 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
42
lib/core/utils.dart
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user