comic source page

This commit is contained in:
nyne
2024-09-30 17:06:58 +08:00
parent a8782b5ce0
commit fdb3901fd1
20 changed files with 695 additions and 27 deletions

View File

@@ -1,5 +1,67 @@
part of "components.dart"; part of "components.dart";
void showToast({required String message, required BuildContext context, Widget? icon, Widget? trailing,}) {
var newEntry = OverlayEntry(
builder: (context) => _ToastOverlay(
message: message,
icon: icon,
trailing: trailing,
));
var state = context.findAncestorStateOfType<OverlayWidgetState>();
state?.addOverlay(newEntry);
Timer(const Duration(seconds: 2), () => state?.remove(newEntry));
}
class _ToastOverlay extends StatelessWidget {
const _ToastOverlay({required this.message, this.icon, this.trailing});
final String message;
final Widget? icon;
final Widget? trailing;
@override
Widget build(BuildContext context) {
return Positioned(
bottom: 24 + MediaQuery.of(context).viewInsets.bottom,
left: 0,
right: 0,
child: Align(
alignment: Alignment.bottomCenter,
child: Material(
color: Theme.of(context).colorScheme.inverseSurface,
borderRadius: BorderRadius.circular(8),
elevation: 2,
textStyle: ts.withColor(Theme.of(context).colorScheme.onInverseSurface),
child: IconTheme(
data: IconThemeData(color: Theme.of(context).colorScheme.onInverseSurface),
child: Container(
padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 16),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (icon != null) icon!.paddingRight(8),
Text(
message,
style: const TextStyle(
fontSize: 16, fontWeight: FontWeight.w500),
maxLines: 3,
),
if (trailing != null) trailing!.paddingLeft(8)
],
),
),
),
),
),
);
}
}
class OverlayWidget extends StatefulWidget { class OverlayWidget extends StatefulWidget {
const OverlayWidget(this.child, {super.key}); const OverlayWidget(this.child, {super.key});
@@ -67,8 +129,7 @@ void showConfirmDialog(BuildContext context, String title, String content,
title: Text(title), title: Text(title),
content: Text(content), content: Text(content),
actions: [ actions: [
TextButton( TextButton(onPressed: context.pop, child: Text("Cancel".tl)),
onPressed: context.pop, child: Text("Cancel".tl)),
TextButton( TextButton(
onPressed: () { onPressed: () {
context.pop(); context.pop();
@@ -194,6 +255,9 @@ class ContentDialog extends StatelessWidget {
return Dialog( return Dialog(
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
side: context.brightness == Brightness.dark
? BorderSide(color: context.colorScheme.outlineVariant)
: BorderSide.none,
), ),
insetPadding: context.width < 400 insetPadding: context.width < 400
? const EdgeInsets.symmetric(horizontal: 4) ? const EdgeInsets.symmetric(horizontal: 4)

View File

@@ -129,7 +129,7 @@ class _PopUpWidgetScaffoldState extends State<PopUpWidgetScaffold> {
icon: const Icon(Icons.arrow_back_sharp), icon: const Icon(Icons.arrow_back_sharp),
onPressed: () => context.canPop() onPressed: () => context.canPop()
? context.pop() ? context.pop()
: App.rootNavigatorKey.currentContext?.pop(), : App.pop(),
), ),
), ),
const SizedBox( const SizedBox(

View File

@@ -67,6 +67,16 @@ class _App {
_ => Colors.blue, _ => Colors.blue,
}; };
} }
Function? _forceRebuildHandler;
void registerForceRebuild(Function handler) {
_forceRebuildHandler = handler;
}
void forceRebuild() {
_forceRebuildHandler?.call();
}
} }
// ignore: non_constant_identifier_names // ignore: non_constant_identifier_names

View File

@@ -1,5 +1,16 @@
import 'dart:convert';
import 'package:venera/foundation/app.dart';
import 'package:venera/utils/io.dart';
class _Appdata { class _Appdata {
final _Settings settings = _Settings(); final _Settings settings = _Settings();
void saveSettings() async {
var data = jsonEncode(settings._data);
var file = File(FilePath.join(App.dataPath, 'settings.json'));
await file.writeAsString(data);
}
} }
final appdata = _Appdata(); final appdata = _Appdata();
@@ -15,9 +26,16 @@ class _Settings {
'newFavoriteAddTo': 'end', // start, end 'newFavoriteAddTo': 'end', // start, end
'moveFavoriteAfterRead': 'none', // none, end, start 'moveFavoriteAfterRead': 'none', // none, end, start
'proxy': 'direct', // direct, system, proxy string 'proxy': 'direct', // direct, system, proxy string
'explore_pages': [],
'categories': [],
'favorites': [],
}; };
operator[](String key) { operator[](String key) {
return _data[key]; return _data[key];
} }
operator[]=(String key, dynamic value) {
_data[key] = value;
}
} }

View File

@@ -11,6 +11,7 @@ import 'package:venera/foundation/app.dart';
import 'package:venera/foundation/history.dart'; import 'package:venera/foundation/history.dart';
import 'package:venera/foundation/res.dart'; import 'package:venera/foundation/res.dart';
import 'package:venera/utils/ext.dart'; import 'package:venera/utils/ext.dart';
import 'package:venera/utils/io.dart';
import '../js_engine.dart'; import '../js_engine.dart';
import '../log.dart'; import '../log.dart';

View File

@@ -59,11 +59,11 @@ class ComicSourceParser {
if(!fileName.endsWith("js")){ if(!fileName.endsWith("js")){
fileName = "$fileName.js"; fileName = "$fileName.js";
} }
var file = File("${App.dataPath}/comic_source/$fileName"); var file = File(FilePath.join(App.dataPath, "comic_source", fileName));
if(file.existsSync()){ if(file.existsSync()){
int i = 0; int i = 0;
while(file.existsSync()){ while(file.existsSync()){
file = File("${App.dataPath}/comic_source/$fileName($i).js"); file = File(FilePath.join(App.dataPath, "comic_source", "${fileName.split('.').first}($i).js"));
i++; i++;
} }
} }

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:venera/components/components.dart';
import 'app_page_route.dart'; import 'app_page_route.dart';
@@ -31,6 +32,6 @@ extension Navigation on BuildContext {
Brightness get brightness => Theme.of(this).brightness; Brightness get brightness => Theme.of(this).brightness;
void showMessage({required String message}) { void showMessage({required String message}) {
// TODO: show message showToast(message: message, context: this);
} }
} }

View File

@@ -1,5 +1,6 @@
import 'package:venera/foundation/app.dart'; import 'package:venera/foundation/app.dart';
import 'package:venera/foundation/cache_manager.dart'; import 'package:venera/foundation/cache_manager.dart';
import 'package:venera/foundation/comic_source/comic_source.dart';
import 'package:venera/foundation/favorites.dart'; import 'package:venera/foundation/favorites.dart';
import 'package:venera/foundation/history.dart'; import 'package:venera/foundation/history.dart';
import 'package:venera/foundation/js_engine.dart'; import 'package:venera/foundation/js_engine.dart';
@@ -15,5 +16,6 @@ Future<void> init() async {
await LocalFavoritesManager().init(); await LocalFavoritesManager().init();
SingleInstanceCookieJar("${App.dataPath}/cookie.db"); SingleInstanceCookieJar("${App.dataPath}/cookie.db");
await JsEngine().init(); await JsEngine().init();
await ComicSource.init();
CacheManager(); CacheManager();
} }

View File

@@ -53,6 +53,21 @@ class MyApp extends StatefulWidget {
} }
class _MyAppState extends State<MyApp> { class _MyAppState extends State<MyApp> {
@override
void initState() {
App.registerForceRebuild(forceRebuild);
super.initState();
}
void forceRebuild() {
void rebuild(Element el) {
el.markNeedsBuild();
el.visitChildren(rebuild);
}
(context as Element).visitChildren(rebuild);
setState(() {});
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MaterialApp( return MaterialApp(

View File

@@ -10,6 +10,8 @@ import 'package:venera/utils/ext.dart';
import '../foundation/app.dart'; import '../foundation/app.dart';
export 'package:dio/dio.dart';
class MyLogInterceptor implements Interceptor { class MyLogInterceptor implements Interceptor {
@override @override
void onError(DioException err, ErrorInterceptorHandler handler) { void onError(DioException err, ErrorInterceptorHandler handler) {
@@ -101,8 +103,8 @@ class MyLogInterceptor implements Interceptor {
class AppDio with DioMixin { class AppDio with DioMixin {
String? _proxy = proxy; String? _proxy = proxy;
AppDio(BaseOptions options) { AppDio([BaseOptions? options]) {
this.options = options; this.options = options ?? BaseOptions();
interceptors.add(MyLogInterceptor()); interceptors.add(MyLogInterceptor());
httpClientAdapter = IOHttpClientAdapter(createHttpClient: createHttpClient); httpClientAdapter = IOHttpClientAdapter(createHttpClient: createHttpClient);
} }
@@ -127,7 +129,7 @@ class AppDio with DioMixin {
static String? proxy; static String? proxy;
static Future<String?> getProxy() async { static Future<String?> getProxy() async {
if (appdata.settings['proxy'].removeAllBlank == "direct") return null; if ((appdata.settings['proxy'] as String).removeAllBlank == "direct") return null;
if (appdata.settings['proxy'] != "system") return appdata.settings['proxy']; if (appdata.settings['proxy'] != "system") return appdata.settings['proxy'];
String res; String res;

View File

@@ -0,0 +1,461 @@
import 'dart:convert';
import 'dart:io';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher_string.dart';
import 'package:venera/components/components.dart';
import 'package:venera/foundation/app.dart';
import 'package:venera/foundation/appdata.dart';
import 'package:venera/foundation/comic_source/comic_source.dart';
import 'package:venera/foundation/log.dart';
import 'package:venera/network/app_dio.dart';
import 'package:venera/utils/ext.dart';
import 'package:venera/utils/translations.dart';
class ComicSourcePage extends StatefulWidget {
const ComicSourcePage({super.key});
static void checkComicSourceUpdate([bool showLoading = false]) async {
if (ComicSource.all().isEmpty) {
return;
}
var controller = showLoading ? showLoadingDialog(App.rootContext) : null;
var dio = AppDio();
var res = await dio.get<String>(
"https://raw.githubusercontent.com/venera-app/venera-configs/master/index.json");
if (res.statusCode != 200) {
App.rootContext.showMessage(message: "Network error".tl);
return;
}
var list = jsonDecode(res.data!) as List;
var versions = <String, String>{};
for (var source in list) {
versions[source['key']] = source['version'];
}
var shouldUpdate = <String>[];
for (var source in ComicSource.all()) {
if (versions.containsKey(source.key) &&
versions[source.key] != source.version) {
shouldUpdate.add(source.key);
}
}
controller?.close();
if (shouldUpdate.isEmpty) {
return;
}
var msg = "";
for (var key in shouldUpdate) {
msg += "${ComicSource.find(key)?.name}: v${versions[key]}\n";
}
msg = msg.trim();
showConfirmDialog(App.rootContext, "Updates Available".tl, msg, () {
for (var key in shouldUpdate) {
var source = ComicSource.find(key);
_BodyState.update(source!);
}
});
}
@override
State<ComicSourcePage> createState() => _ComicSourcePageState();
}
class _ComicSourcePageState extends State<ComicSourcePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: Appbar(
title: Text('Comic Source'.tl),
),
body: const _Body(),
);
}
}
class _Body extends StatefulWidget {
const _Body();
@override
State<_Body> createState() => _BodyState();
}
class _BodyState extends State<_Body> {
var url = "";
@override
Widget build(BuildContext context) {
return SmoothCustomScrollView(
slivers: [
buildCard(context),
buildSettings(),
for (var source in ComicSource.all()) buildSource(context, source),
SliverPadding(padding: EdgeInsets.only(bottom: context.padding.bottom)),
],
);
}
Widget buildSettings() {
return SliverToBoxAdapter(
child: ListTile(
leading: const Icon(Icons.update_outlined),
title: Text("Check updates".tl),
onTap: () => ComicSourcePage.checkComicSourceUpdate(true),
trailing: const Icon(Icons.arrow_right),
),
);
}
Widget buildSource(BuildContext context, ComicSource source) {
return SliverToBoxAdapter(
child: Column(
children: [
const Divider(),
ListTile(
title: Text(source.name),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (App.isDesktop)
Tooltip(
message: "Edit".tl,
child: IconButton(
onPressed: () => edit(source),
icon: const Icon(Icons.edit_note)),
),
Tooltip(
message: "Update".tl,
child: IconButton(
onPressed: () => update(source),
icon: const Icon(Icons.update)),
),
Tooltip(
message: "Delete".tl,
child: IconButton(
onPressed: () => delete(source),
icon: const Icon(Icons.delete)),
),
],
),
),
ListTile(
title: const Text("Version"),
subtitle: Text(source.version),
)
],
),
);
}
void delete(ComicSource source) {
showConfirmDialog(
App.rootContext,
"Delete".tl,
"Are you sure you want to delete it?".tl,
() {
var file = File(source.filePath);
file.delete();
ComicSource.all().remove(source);
_validatePages();
App.forceRebuild();
},
);
}
void edit(ComicSource source) async {
try {
await Process.run("code", [source.filePath], runInShell: true);
await showDialog(
context: App.rootContext,
builder: (context) => AlertDialog(
title: const Text("Reload Configs"),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text("cancel")),
TextButton(
onPressed: () async {
await ComicSource.reload();
App.forceRebuild();
},
child: const Text("continue")),
],
));
} catch (e) {
context.showMessage(message: "Failed to launch vscode");
}
}
static void update(ComicSource source) async {
if (!source.url.isURL) {
App.rootContext.showMessage(message: "Invalid url config");
return;
}
ComicSource.remove(source.key);
bool cancel = false;
var controller = showLoadingDialog(
App.rootContext,
onCancel: () => cancel = true,
barrierDismissible: false,
);
try {
var res = await AppDio().get<String>(source.url,
options: Options(responseType: ResponseType.plain));
if (cancel) return;
controller.close();
await ComicSourceParser().parse(res.data!, source.filePath);
await File(source.filePath).writeAsString(res.data!);
} catch (e) {
if (cancel) return;
App.rootContext.showMessage(message: e.toString());
}
await ComicSource.reload();
App.forceRebuild();
}
Widget buildCard(BuildContext context) {
return SliverToBoxAdapter(
child: Card.outlined(
child: SizedBox(
width: double.infinity,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
title: Text("Add comic source".tl),
leading: const Icon(Icons.dashboard_customize),
),
TextField(
decoration: InputDecoration(
hintText: "URL",
border: const UnderlineInputBorder(),
contentPadding:
const EdgeInsets.symmetric(horizontal: 12),
suffix: IconButton(
onPressed: () => handleAddSource(url),
icon: const Icon(Icons.check))),
onChanged: (value) {
url = value;
},
onSubmitted: handleAddSource)
.paddingHorizontal(16)
.paddingBottom(32),
Row(
children: [
TextButton(onPressed: chooseFile, child: Text("Choose file".tl))
.paddingLeft(8),
const Spacer(),
TextButton(
onPressed: () {
showPopUpWidget(
App.rootContext, _ComicSourceList(handleAddSource));
},
child: Text("View list".tl)),
const Spacer(),
TextButton(onPressed: help, child: Text("Open help".tl))
.paddingRight(8),
],
),
const SizedBox(height: 8),
],
),
),
).paddingHorizontal(12),
);
}
void chooseFile() async {
final result = await FilePicker.platform.pickFiles(
type: FileType.custom,
allowedExtensions: ['js'],
);
final file = result?.files.first;
if (file == null) return;
try {
var fileName = file.name;
var bytes = file.bytes!;
var content = utf8.decode(bytes);
await addSource(content, fileName);
} catch (e) {
App.rootContext.showMessage(message: e.toString());
}
}
void help() {
launchUrlString(
"https://github.com/venera-app/venera/blob/master/doc/comic_source.md");
}
Future<void> handleAddSource(String url) async {
if (url.isEmpty) {
return;
}
var splits = url.split("/");
splits.removeWhere((element) => element == "");
var fileName = splits.last;
bool cancel = false;
var controller = showLoadingDialog(App.rootContext,
onCancel: () => cancel = true, barrierDismissible: false);
try {
var res = await AppDio()
.get<String>(url, options: Options(responseType: ResponseType.plain));
if (cancel) return;
controller.close();
await addSource(res.data!, fileName);
} catch (e, s) {
if (cancel) return;
context.showMessage(message: e.toString());
Log.error("Add comic source", "$e\n$s");
}
}
Future<void> addSource(String js, String fileName) async {
var comicSource = await ComicSourceParser().createAndParse(js, fileName);
ComicSource.add(comicSource);
_addAllPagesWithComicSource(comicSource);
appdata.saveSettings();
App.forceRebuild();
}
}
class _ComicSourceList extends StatefulWidget {
const _ComicSourceList(this.onAdd);
final Future<void> Function(String) onAdd;
@override
State<_ComicSourceList> createState() => _ComicSourceListState();
}
class _ComicSourceListState extends State<_ComicSourceList> {
bool loading = true;
List? json;
void load() async {
var dio = AppDio();
var res = await dio.get<String>(
"https://raw.githubusercontent.com/venera-app/venera-configs/master/index.json");
if (res.statusCode != 200) {
context.showMessage(message: "Network error".tl);
return;
}
setState(() {
json = jsonDecode(res.data!);
loading = false;
});
}
@override
Widget build(BuildContext context) {
return PopUpWidgetScaffold(
title: "Comic Source".tl,
body: buildBody(),
);
}
Widget buildBody() {
if (loading) {
load();
return const Center(child: CircularProgressIndicator());
} else {
var currentKey = ComicSource.all().map((e) => e.key).toList();
return ListView.builder(
itemCount: json!.length,
itemBuilder: (context, index) {
var key = json![index]["key"];
var action = currentKey.contains(key)
? const Icon(Icons.check)
: Tooltip(
message: "Add",
child: IconButton(
icon: const Icon(Icons.add),
onPressed: () async {
await widget.onAdd(
"https://raw.githubusercontent.com/venera-app/venera-configs/master/${json![index]["fileName"]}");
setState(() {});
},
),
);
return ListTile(
title: Text(json![index]["name"]),
subtitle: Text(json![index]["version"]),
trailing: action,
);
},
);
}
}
}
void _validatePages() {
List explorePages = appdata.settings['explore_pages'];
List categoryPages = appdata.settings['categories'];
List networkFavorites = appdata.settings['favorites'];
var totalExplorePages = ComicSource.all()
.map((e) => e.explorePages.map((e) => e.title))
.expand((element) => element)
.toList();
var totalCategoryPages = ComicSource.all()
.map((e) => e.categoryData?.key)
.where((element) => element != null)
.map((e) => e!)
.toList();
var totalNetworkFavorites = ComicSource.all()
.map((e) => e.favoriteData?.key)
.where((element) => element != null)
.map((e) => e!)
.toList();
for (var page in List.from(explorePages)) {
if (!totalExplorePages.contains(page)) {
explorePages.remove(page);
}
}
for (var page in List.from(categoryPages)) {
if (!totalCategoryPages.contains(page)) {
categoryPages.remove(page);
}
}
for (var page in List.from(networkFavorites)) {
if (!totalNetworkFavorites.contains(page)) {
networkFavorites.remove(page);
}
}
appdata.settings['explore_pages'] = explorePages.toSet().toList();
appdata.settings['categories'] = categoryPages.toSet().toList();
appdata.settings['favorites'] = networkFavorites.toSet().toList();
appdata.saveSettings();
}
void _addAllPagesWithComicSource(ComicSource source) {
var explorePages = appdata.settings['explore_pages'];
var categoryPages = appdata.settings['categories'];
var networkFavorites = appdata.settings['favorites'];
if (source.explorePages.isNotEmpty) {
for (var page in source.explorePages) {
if (!explorePages.contains(page.title)) {
explorePages.add(page.title);
}
}
}
if (source.categoryData != null &&
!categoryPages.contains(source.categoryData!.key)) {
categoryPages.add(source.categoryData!.key);
}
if (source.favoriteData != null &&
!networkFavorites.contains(source.favoriteData!.key)) {
networkFavorites.add(source.favoriteData!.key);
}
appdata.settings['explore_pages'] = explorePages.toSet().toList();
appdata.settings['categories'] = categoryPages.toSet().toList();
appdata.settings['favorites'] = networkFavorites.toSet().toList();
appdata.saveSettings();
}

View File

@@ -10,6 +10,7 @@ import 'package:venera/foundation/history.dart';
import 'package:venera/foundation/image_provider/cached_image.dart'; import 'package:venera/foundation/image_provider/cached_image.dart';
import 'package:venera/foundation/local.dart'; import 'package:venera/foundation/local.dart';
import 'package:venera/foundation/log.dart'; import 'package:venera/foundation/log.dart';
import 'package:venera/pages/comic_source_page.dart';
import 'package:venera/utils/io.dart'; import 'package:venera/utils/io.dart';
import 'package:venera/utils/translations.dart'; import 'package:venera/utils/translations.dart';
@@ -584,7 +585,9 @@ class _ComicSourceWidgetState extends State<_ComicSourceWidget> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return SliverToBoxAdapter( return SliverToBoxAdapter(
child: InkWell( child: InkWell(
onTap: () {}, onTap: () {
context.to(() => const ComicSourcePage());
},
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border( border: Border(
@@ -619,12 +622,14 @@ class _ComicSourceWidgetState extends State<_ComicSourceWidget> {
], ],
), ),
).paddingHorizontal(16), ).paddingHorizontal(16),
if(comicSources.isNotEmpty)
SizedBox( SizedBox(
width: double.infinity, width: double.infinity,
child: Wrap( child: Wrap(
runSpacing: 8,
spacing: 8,
children: comicSources.map((e) { children: comicSources.map((e) {
return Container( return Container(
margin: const EdgeInsets.symmetric(horizontal: 8),
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 8, vertical: 2), horizontal: 8, vertical: 2),
decoration: BoxDecoration( decoration: BoxDecoration(
@@ -634,7 +639,7 @@ class _ComicSourceWidgetState extends State<_ComicSourceWidget> {
child: Text(e), child: Text(e),
); );
}).toList(), }).toList(),
), ).paddingHorizontal(16).paddingBottom(16),
), ),
], ],
), ),

View File

@@ -5,6 +5,18 @@ import 'package:file_picker/file_picker.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:venera/foundation/app.dart'; import 'package:venera/foundation/app.dart';
import 'package:venera/utils/ext.dart'; import 'package:venera/utils/ext.dart';
import 'package:path/path.dart' as p;
export 'dart:io';
class FilePath {
const FilePath._();
static String join(String path1, String path2, [String? path3, String? path4, String? path5]) {
return p.join(path1, path2, path3, path4, path5);
}
}
extension FileSystemEntityExt on FileSystemEntity { extension FileSystemEntityExt on FileSystemEntity {
String get name { String get name {

View File

@@ -9,6 +9,7 @@
#include <flutter_qjs/flutter_qjs_plugin.h> #include <flutter_qjs/flutter_qjs_plugin.h>
#include <screen_retriever/screen_retriever_plugin.h> #include <screen_retriever/screen_retriever_plugin.h>
#include <sqlite3_flutter_libs/sqlite3_flutter_libs_plugin.h> #include <sqlite3_flutter_libs/sqlite3_flutter_libs_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h>
#include <window_manager/window_manager_plugin.h> #include <window_manager/window_manager_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) { void fl_register_plugins(FlPluginRegistry* registry) {
@@ -21,6 +22,9 @@ void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) sqlite3_flutter_libs_registrar = g_autoptr(FlPluginRegistrar) sqlite3_flutter_libs_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "Sqlite3FlutterLibsPlugin"); fl_plugin_registry_get_registrar_for_plugin(registry, "Sqlite3FlutterLibsPlugin");
sqlite3_flutter_libs_plugin_register_with_registrar(sqlite3_flutter_libs_registrar); sqlite3_flutter_libs_plugin_register_with_registrar(sqlite3_flutter_libs_registrar);
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
g_autoptr(FlPluginRegistrar) window_manager_registrar = g_autoptr(FlPluginRegistrar) window_manager_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "WindowManagerPlugin"); fl_plugin_registry_get_registrar_for_plugin(registry, "WindowManagerPlugin");
window_manager_plugin_register_with_registrar(window_manager_registrar); window_manager_plugin_register_with_registrar(window_manager_registrar);

View File

@@ -6,6 +6,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
flutter_qjs flutter_qjs
screen_retriever screen_retriever
sqlite3_flutter_libs sqlite3_flutter_libs
url_launcher_linux
window_manager window_manager
) )

View File

@@ -8,11 +8,13 @@ import Foundation
import path_provider_foundation import path_provider_foundation
import screen_retriever import screen_retriever
import sqlite3_flutter_libs import sqlite3_flutter_libs
import url_launcher_macos
import window_manager import window_manager
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin")) ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin"))
Sqlite3FlutterLibsPlugin.register(with: registry.registrar(forPlugin: "Sqlite3FlutterLibsPlugin")) Sqlite3FlutterLibsPlugin.register(with: registry.registrar(forPlugin: "Sqlite3FlutterLibsPlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin")) WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin"))
} }

View File

@@ -247,7 +247,7 @@ packages:
source: hosted source: hosted
version: "1.12.0" version: "1.12.0"
path: path:
dependency: transitive dependency: "direct main"
description: description:
name: path name: path
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
@@ -411,6 +411,70 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.2" version: "1.3.2"
url_launcher:
dependency: "direct main"
description:
name: url_launcher
sha256: "21b704ce5fa560ea9f3b525b43601c678728ba46725bab9b01187b4831377ed3"
url: "https://pub.dev"
source: hosted
version: "6.3.0"
url_launcher_android:
dependency: transitive
description:
name: url_launcher_android
sha256: f0c73347dfcfa5b3db8bc06e1502668265d39c08f310c29bff4e28eea9699f79
url: "https://pub.dev"
source: hosted
version: "6.3.9"
url_launcher_ios:
dependency: transitive
description:
name: url_launcher_ios
sha256: e43b677296fadce447e987a2f519dcf5f6d1e527dc35d01ffab4fff5b8a7063e
url: "https://pub.dev"
source: hosted
version: "6.3.1"
url_launcher_linux:
dependency: transitive
description:
name: url_launcher_linux
sha256: e2b9622b4007f97f504cd64c0128309dfb978ae66adbe944125ed9e1750f06af
url: "https://pub.dev"
source: hosted
version: "3.2.0"
url_launcher_macos:
dependency: transitive
description:
name: url_launcher_macos
sha256: "769549c999acdb42b8bcfa7c43d72bf79a382ca7441ab18a808e101149daf672"
url: "https://pub.dev"
source: hosted
version: "3.2.1"
url_launcher_platform_interface:
dependency: transitive
description:
name: url_launcher_platform_interface
sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029"
url: "https://pub.dev"
source: hosted
version: "2.3.2"
url_launcher_web:
dependency: transitive
description:
name: url_launcher_web
sha256: "772638d3b34c779ede05ba3d38af34657a05ac55b06279ea6edd409e323dca8e"
url: "https://pub.dev"
source: hosted
version: "2.3.3"
url_launcher_windows:
dependency: transitive
description:
name: url_launcher_windows
sha256: "49c10f879746271804767cb45551ec5592cdab00ee105c06dddde1a98f73b185"
url: "https://pub.dev"
source: hosted
version: "3.1.2"
vector_math: vector_math:
dependency: transitive dependency: transitive
description: description:

View File

@@ -27,6 +27,8 @@ dependencies:
html: any html: any
pointycastle: any pointycastle: any
file_picker: ^8.1.2 file_picker: ^8.1.2
url_launcher: ^6.3.0
path: ^1.9.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:

View File

@@ -9,6 +9,7 @@
#include <flutter_qjs/flutter_qjs_plugin.h> #include <flutter_qjs/flutter_qjs_plugin.h>
#include <screen_retriever/screen_retriever_plugin.h> #include <screen_retriever/screen_retriever_plugin.h>
#include <sqlite3_flutter_libs/sqlite3_flutter_libs_plugin.h> #include <sqlite3_flutter_libs/sqlite3_flutter_libs_plugin.h>
#include <url_launcher_windows/url_launcher_windows.h>
#include <window_manager/window_manager_plugin.h> #include <window_manager/window_manager_plugin.h>
void RegisterPlugins(flutter::PluginRegistry* registry) { void RegisterPlugins(flutter::PluginRegistry* registry) {
@@ -18,6 +19,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
registry->GetRegistrarForPlugin("ScreenRetrieverPlugin")); registry->GetRegistrarForPlugin("ScreenRetrieverPlugin"));
Sqlite3FlutterLibsPluginRegisterWithRegistrar( Sqlite3FlutterLibsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("Sqlite3FlutterLibsPlugin")); registry->GetRegistrarForPlugin("Sqlite3FlutterLibsPlugin"));
UrlLauncherWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
WindowManagerPluginRegisterWithRegistrar( WindowManagerPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("WindowManagerPlugin")); registry->GetRegistrarForPlugin("WindowManagerPlugin"));
} }

View File

@@ -6,6 +6,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
flutter_qjs flutter_qjs
screen_retriever screen_retriever
sqlite3_flutter_libs sqlite3_flutter_libs
url_launcher_windows
window_manager window_manager
) )