diff --git a/lib/components/message.dart b/lib/components/message.dart index 4cc05a3..7b5a562 100644 --- a/lib/components/message.dart +++ b/lib/components/message.dart @@ -1,5 +1,67 @@ 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(); + + 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 { const OverlayWidget(this.child, {super.key}); @@ -67,8 +129,7 @@ void showConfirmDialog(BuildContext context, String title, String content, title: Text(title), content: Text(content), actions: [ - TextButton( - onPressed: context.pop, child: Text("Cancel".tl)), + TextButton(onPressed: context.pop, child: Text("Cancel".tl)), TextButton( onPressed: () { context.pop(); @@ -194,6 +255,9 @@ class ContentDialog extends StatelessWidget { return Dialog( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), + side: context.brightness == Brightness.dark + ? BorderSide(color: context.colorScheme.outlineVariant) + : BorderSide.none, ), insetPadding: context.width < 400 ? const EdgeInsets.symmetric(horizontal: 4) diff --git a/lib/components/pop_up_widget.dart b/lib/components/pop_up_widget.dart index 772c9d0..2052501 100644 --- a/lib/components/pop_up_widget.dart +++ b/lib/components/pop_up_widget.dart @@ -129,7 +129,7 @@ class _PopUpWidgetScaffoldState extends State { icon: const Icon(Icons.arrow_back_sharp), onPressed: () => context.canPop() ? context.pop() - : App.rootNavigatorKey.currentContext?.pop(), + : App.pop(), ), ), const SizedBox( diff --git a/lib/foundation/app.dart b/lib/foundation/app.dart index e144f8e..e49fcfb 100644 --- a/lib/foundation/app.dart +++ b/lib/foundation/app.dart @@ -67,6 +67,16 @@ class _App { _ => Colors.blue, }; } + + Function? _forceRebuildHandler; + + void registerForceRebuild(Function handler) { + _forceRebuildHandler = handler; + } + + void forceRebuild() { + _forceRebuildHandler?.call(); + } } // ignore: non_constant_identifier_names diff --git a/lib/foundation/appdata.dart b/lib/foundation/appdata.dart index 1e5b01c..7b124a5 100644 --- a/lib/foundation/appdata.dart +++ b/lib/foundation/appdata.dart @@ -1,5 +1,16 @@ +import 'dart:convert'; + +import 'package:venera/foundation/app.dart'; +import 'package:venera/utils/io.dart'; + class _Appdata { 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(); @@ -15,9 +26,16 @@ class _Settings { 'newFavoriteAddTo': 'end', // start, end 'moveFavoriteAfterRead': 'none', // none, end, start 'proxy': 'direct', // direct, system, proxy string + 'explore_pages': [], + 'categories': [], + 'favorites': [], }; operator[](String key) { return _data[key]; } + + operator[]=(String key, dynamic value) { + _data[key] = value; + } } \ No newline at end of file diff --git a/lib/foundation/comic_source/comic_source.dart b/lib/foundation/comic_source/comic_source.dart index ee951d9..2419e8f 100644 --- a/lib/foundation/comic_source/comic_source.dart +++ b/lib/foundation/comic_source/comic_source.dart @@ -11,6 +11,7 @@ import 'package:venera/foundation/app.dart'; import 'package:venera/foundation/history.dart'; import 'package:venera/foundation/res.dart'; import 'package:venera/utils/ext.dart'; +import 'package:venera/utils/io.dart'; import '../js_engine.dart'; import '../log.dart'; diff --git a/lib/foundation/comic_source/parser.dart b/lib/foundation/comic_source/parser.dart index 456d63c..6e2a25b 100644 --- a/lib/foundation/comic_source/parser.dart +++ b/lib/foundation/comic_source/parser.dart @@ -59,11 +59,11 @@ class ComicSourceParser { if(!fileName.endsWith("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()){ int i = 0; 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++; } } diff --git a/lib/foundation/context.dart b/lib/foundation/context.dart index 76dc552..41ec81a 100644 --- a/lib/foundation/context.dart +++ b/lib/foundation/context.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:venera/components/components.dart'; import 'app_page_route.dart'; @@ -31,6 +32,6 @@ extension Navigation on BuildContext { Brightness get brightness => Theme.of(this).brightness; void showMessage({required String message}) { - // TODO: show message + showToast(message: message, context: this); } } diff --git a/lib/init.dart b/lib/init.dart index 30d2cf9..2106699 100644 --- a/lib/init.dart +++ b/lib/init.dart @@ -1,5 +1,6 @@ import 'package:venera/foundation/app.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/history.dart'; import 'package:venera/foundation/js_engine.dart'; @@ -15,5 +16,6 @@ Future init() async { await LocalFavoritesManager().init(); SingleInstanceCookieJar("${App.dataPath}/cookie.db"); await JsEngine().init(); + await ComicSource.init(); CacheManager(); } \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index b6dce9f..d9e6e33 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -53,6 +53,21 @@ class MyApp extends StatefulWidget { } class _MyAppState extends State { + @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 Widget build(BuildContext context) { return MaterialApp( diff --git a/lib/network/app_dio.dart b/lib/network/app_dio.dart index 6743211..e0e9d40 100644 --- a/lib/network/app_dio.dart +++ b/lib/network/app_dio.dart @@ -10,6 +10,8 @@ import 'package:venera/utils/ext.dart'; import '../foundation/app.dart'; +export 'package:dio/dio.dart'; + class MyLogInterceptor implements Interceptor { @override void onError(DioException err, ErrorInterceptorHandler handler) { @@ -101,8 +103,8 @@ class MyLogInterceptor implements Interceptor { class AppDio with DioMixin { String? _proxy = proxy; - AppDio(BaseOptions options) { - this.options = options; + AppDio([BaseOptions? options]) { + this.options = options ?? BaseOptions(); interceptors.add(MyLogInterceptor()); httpClientAdapter = IOHttpClientAdapter(createHttpClient: createHttpClient); } @@ -127,7 +129,7 @@ class AppDio with DioMixin { static String? proxy; static Future 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']; String res; diff --git a/lib/pages/comic_source_page.dart b/lib/pages/comic_source_page.dart new file mode 100644 index 0000000..f3b51a8 --- /dev/null +++ b/lib/pages/comic_source_page.dart @@ -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( + "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 = {}; + for (var source in list) { + versions[source['key']] = source['version']; + } + var shouldUpdate = []; + 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 createState() => _ComicSourcePageState(); +} + +class _ComicSourcePageState extends State { + @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(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 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(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 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 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( + "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(); +} diff --git a/lib/pages/home_page.dart b/lib/pages/home_page.dart index 1c91bbb..8514b00 100644 --- a/lib/pages/home_page.dart +++ b/lib/pages/home_page.dart @@ -10,6 +10,7 @@ import 'package:venera/foundation/history.dart'; import 'package:venera/foundation/image_provider/cached_image.dart'; import 'package:venera/foundation/local.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/translations.dart'; @@ -584,7 +585,9 @@ class _ComicSourceWidgetState extends State<_ComicSourceWidget> { Widget build(BuildContext context) { return SliverToBoxAdapter( child: InkWell( - onTap: () {}, + onTap: () { + context.to(() => const ComicSourcePage()); + }, child: Container( decoration: BoxDecoration( border: Border( @@ -619,23 +622,25 @@ class _ComicSourceWidgetState extends State<_ComicSourceWidget> { ], ), ).paddingHorizontal(16), - SizedBox( - width: double.infinity, - child: Wrap( - children: comicSources.map((e) { - return Container( - margin: const EdgeInsets.symmetric(horizontal: 8), - padding: const EdgeInsets.symmetric( - horizontal: 8, vertical: 2), - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.secondaryContainer, - borderRadius: BorderRadius.circular(8), - ), - child: Text(e), - ); - }).toList(), + if(comicSources.isNotEmpty) + SizedBox( + width: double.infinity, + child: Wrap( + runSpacing: 8, + spacing: 8, + children: comicSources.map((e) { + return Container( + padding: const EdgeInsets.symmetric( + horizontal: 8, vertical: 2), + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.secondaryContainer, + borderRadius: BorderRadius.circular(8), + ), + child: Text(e), + ); + }).toList(), + ).paddingHorizontal(16).paddingBottom(16), ), - ), ], ), ), diff --git a/lib/utils/io.dart b/lib/utils/io.dart index 45c050a..3c7a5db 100644 --- a/lib/utils/io.dart +++ b/lib/utils/io.dart @@ -5,6 +5,18 @@ import 'package:file_picker/file_picker.dart'; import 'package:flutter/services.dart'; import 'package:venera/foundation/app.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 { String get name { diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index f80c132..71de411 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -9,6 +9,7 @@ #include #include #include +#include #include void fl_register_plugins(FlPluginRegistry* registry) { @@ -21,6 +22,9 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) sqlite3_flutter_libs_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "Sqlite3FlutterLibsPlugin"); 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 = fl_plugin_registry_get_registrar_for_plugin(registry, "WindowManagerPlugin"); window_manager_plugin_register_with_registrar(window_manager_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 68a1cae..0dfc8a9 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -6,6 +6,7 @@ list(APPEND FLUTTER_PLUGIN_LIST flutter_qjs screen_retriever sqlite3_flutter_libs + url_launcher_linux window_manager ) diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 37e6349..afbcf04 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -8,11 +8,13 @@ import Foundation import path_provider_foundation import screen_retriever import sqlite3_flutter_libs +import url_launcher_macos import window_manager func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin")) Sqlite3FlutterLibsPlugin.register(with: registry.registrar(forPlugin: "Sqlite3FlutterLibsPlugin")) + UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin")) } diff --git a/pubspec.lock b/pubspec.lock index c5cb0cf..8de9128 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -247,7 +247,7 @@ packages: source: hosted version: "1.12.0" path: - dependency: transitive + dependency: "direct main" description: name: path sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" @@ -411,6 +411,70 @@ packages: url: "https://pub.dev" source: hosted 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: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 0857ad2..9a012b5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -27,6 +27,8 @@ dependencies: html: any pointycastle: any file_picker: ^8.1.2 + url_launcher: ^6.3.0 + path: ^1.9.0 dev_dependencies: flutter_test: diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 6709000..8dc57f9 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -9,6 +9,7 @@ #include #include #include +#include #include void RegisterPlugins(flutter::PluginRegistry* registry) { @@ -18,6 +19,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("ScreenRetrieverPlugin")); Sqlite3FlutterLibsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("Sqlite3FlutterLibsPlugin")); + UrlLauncherWindowsRegisterWithRegistrar( + registry->GetRegistrarForPlugin("UrlLauncherWindows")); WindowManagerPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("WindowManagerPlugin")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index d79a81c..9c08e60 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -6,6 +6,7 @@ list(APPEND FLUTTER_PLUGIN_LIST flutter_qjs screen_retriever sqlite3_flutter_libs + url_launcher_windows window_manager )