From cb1e6791e5fb2aa258194891ce4589ca9c896732 Mon Sep 17 00:00:00 2001 From: sivan22 Date: Tue, 6 Feb 2024 09:07:56 +0200 Subject: [PATCH] fix tab view and browser, add settings support --- TODO.txt | 13 +- devtools_options.yaml | 1 + lib/book_tabs_viewer.dart | 239 ++++++++++++++ lib/cache_provider.dart | 105 ++++++ lib/main.dart | 311 ++++++------------ lib/settings_screen.dart | 52 +++ lib/welcome.md | 1 + macos/Flutter/GeneratedPluginRegistrant.swift | 2 + pubspec.lock | 92 +++++- pubspec.yaml | 4 +- 10 files changed, 592 insertions(+), 228 deletions(-) create mode 100644 devtools_options.yaml create mode 100644 lib/book_tabs_viewer.dart create mode 100644 lib/cache_provider.dart create mode 100644 lib/settings_screen.dart create mode 100644 lib/welcome.md diff --git a/TODO.txt b/TODO.txt index cafcc883e..09190ab9e 100644 --- a/TODO.txt +++ b/TODO.txt @@ -1,13 +1,10 @@ -V add multi tabs support -V save tab state -close the current tab -V change markdown loading to async -V add book title search -move tabView to be home page -add setting screen -add option for styling the markdown +up folder in brwser +use html2markdown +add option for themes for the markdown add search in markdown content add PDF support add search for reference deploy with msix for sharing +להוסיף את הרמה האחרונה של כותרת לכל הרמות מעל 4 +להחזיר ספרים שנמחקו diff --git a/devtools_options.yaml b/devtools_options.yaml new file mode 100644 index 000000000..7e7e7f67d --- /dev/null +++ b/devtools_options.yaml @@ -0,0 +1 @@ +extensions: diff --git a/lib/book_tabs_viewer.dart b/lib/book_tabs_viewer.dart new file mode 100644 index 000000000..bfd4aaa79 --- /dev/null +++ b/lib/book_tabs_viewer.dart @@ -0,0 +1,239 @@ +import 'package:flutter_settings_screen_ex/flutter_settings_screen_ex.dart'; +import 'package:universal_io/io.dart'; +import 'package:flutter/material.dart'; +import 'package:markdown_widget/markdown_widget.dart'; +import 'custom_node.dart'; +import 'main.dart'; +import 'dart:math'; + +class MarkdownTabView extends StatefulWidget { + + + const MarkdownTabView({Key? key,}) + : super(key: key); + + @override + MarkdownTabViewState createState() => MarkdownTabViewState(); +} + +class MarkdownTabViewState extends State with TickerProviderStateMixin{ + int selectedIndex = 0; + List openedFiles = []; + late TabController tabController; + + + @override + void initState() { + super.initState(); + tabController = TabController(length: openedFiles.length, vsync:this); + + } + + + void enlargeText() { + setState(() { + Settings.setValue( 'key-font-size',min(Settings.getValue('key-font-size')!+5,50.0)); + + }); + + } + + void enSmallText() { + setState(() { + Settings.setValue( 'key-font-size',max(Settings.getValue('key-font-size')!-5,15.0)); + + }); + + } + + void closelastTab() { + setState(() { + if (openedFiles.isNotEmpty) { + openedFiles.removeAt(tabController.index); + + tabController = TabController(length: openedFiles.length, vsync:this,initialIndex: max(0,tabController.index-1)); + } + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Center(child: Text('אוצריא')), + actions: [ + + IconButton( + icon: const Icon( + Icons.font_download_outlined, + + ), + tooltip: 'הגדל טקסט', + onPressed: enlargeText, + ), + IconButton( + icon: const Icon( + Icons.font_download_off_outlined, + + ), + tooltip: 'הקטן טקסט', + onPressed: enSmallText, + ), + IconButton( + icon: const Icon(Icons.close), + tooltip: 'סגור ספר פתוח', + onPressed: closelastTab, + ), + ], + bottom: TabBar( + controller: tabController, + tabs: openedFiles + .map((file) => Tab(text: file.path.split('/').last)) + .toList(), + ), + ), + body: Row(children: [ + NavigationRail( + labelType: NavigationRailLabelType.all, + destinations: const [ + NavigationRailDestination( + icon: Icon(Icons.library_books), + label: Text('ספריה'), + ), + NavigationRailDestination( + icon: Icon(Icons.search), + label: Text('חיפוש'), + ), + NavigationRailDestination( + icon: Icon(Icons.settings), + label: Text('הגדרות'), + ), + ], + selectedIndex: selectedIndex, + onDestinationSelected: (int index) { + setState(() { + selectedIndex = index; + switch (index) { + case 0: + _openSelectedFile('browser'); + case 1: + _openSelectedFile('search'); + case 2: + Navigator.pushNamed(context, '/settings'); + } + }); + }), + Expanded( + child: TabBarView( + controller: tabController, + children: openedFiles.map((file) { + return BookViewer( + file: file, + ); + }).toList()), + ), + ]), + ); + } + + void _openSelectedFile(String how) async { + File? selectedFile; + + if (how == 'browser') { + selectedFile = await Navigator.push( + context, + MaterialPageRoute( + builder: (context) => DirectoryBrowser( + directory: Directory('./אוצריא'), + )), + ); + } else if (how == 'search') { + selectedFile = await Navigator.push( + context, + MaterialPageRoute(builder: (context) => BookSearchScreen()), + ); + } + if (selectedFile != null) { + setState(() { + openedFiles.add(selectedFile!); + tabController = TabController(length: openedFiles.length, vsync:this,initialIndex: openedFiles.length-1); + }); + } + } +} + + +class BookViewer extends StatefulWidget { + final File file; + late Future data; + + BookViewer({Key? key, required this.file}) : super(key: key) { + data = file.readAsString(); + } + + @override + State createState() => _BookViewerState(); +} + +class _BookViewerState extends State with AutomaticKeepAliveClientMixin { + //use value notifier + + ValueNotifier textFontSize = ValueNotifier(Settings.getValue('key-font-size')); + + +@override + void initState() { + super.initState(); + } + final tocController = TocController(); + + Widget buildTocWidget() => TocWidget(controller: tocController); + + Widget buildMarkdown() => FutureBuilder( + future: widget.data.then((value) => value), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + if (snapshot.hasError) { + return Center(child: Text('Error: ${snapshot.error}')); + } + + if (snapshot.hasData) { + return ValueListenableBuilder( + key: PageStorageKey(widget.key.toString()), + valueListenable: textFontSize, + builder: (context, value, child) => + MarkdownWidget( + padding: const EdgeInsets.all(50), + data: snapshot.data!, + tocController: tocController, + config: MarkdownConfig(configs: [ + PConfig( + textStyle: TextStyle( + fontSize: Settings.getValue('key-font-size'), + fontFamily: Settings.getValue('key-font-family'), + + )), + ]), + markdownGenerator: MarkdownGenerator( + textGenerator: (node, config, visitor) => + CustomTextNode(node.textContent, config, visitor)))); + } + } + return const Center(child: CircularProgressIndicator( + )); + }); + + @override + Widget build(BuildContext context) { + return Scaffold( + drawer: Drawer(child: buildTocWidget()), + appBar: AppBar( + actions: [], + title: Text('${widget.file.path.split('/').last}'), + ), + body: buildMarkdown(), + ); + } + @override + bool get wantKeepAlive => true; +} diff --git a/lib/cache_provider.dart b/lib/cache_provider.dart new file mode 100644 index 000000000..4a1340be3 --- /dev/null +++ b/lib/cache_provider.dart @@ -0,0 +1,105 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:hive/hive.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:flutter_settings_screen_ex/flutter_settings_screen_ex.dart'; + + +/// A cache access provider class for shared preferences using Hive library +class HiveCache extends CacheProvider { + Box? _preferences; + final String keyName = 'app_preferences'; + + @override + Future init() async { + WidgetsFlutterBinding.ensureInitialized(); + if (!kIsWeb) { + final defaultDirectory = await getApplicationDocumentsDirectory(); + Hive.init(defaultDirectory.path); + } + if (Hive.isBoxOpen(keyName)) { + _preferences = Hive.box(keyName); + } else { + _preferences = await Hive.openBox(keyName); + } + } + + Set get keys => getKeys(); + + @override + bool? getBool(String key, {bool? defaultValue}) { + return _preferences?.get(key); + } + + @override + double? getDouble(String key, {double? defaultValue}) { + return _preferences?.get(key); + } + + @override + int? getInt(String key, {int? defaultValue}) { + return _preferences?.get(key); + } + + @override + String? getString(String key, {String? defaultValue}) { + return _preferences?.get(key); + } + + @override + Future setBool(String key, bool? value) async { + await _preferences?.put(key, value); + } + + @override + Future setDouble(String key, double? value) async { + await _preferences?.put(key, value); + } + + @override + Future setInt(String key, int? value) async { + await _preferences?.put(key, value); + } + + @override + Future setString(String key, String? value) async { + await _preferences?.put(key, value); + } + + @override + Future setObject(String key, T? value) async { + await _preferences?.put(key, value); + } + + @override + bool containsKey(String key) { + return _preferences?.containsKey(key) ?? false; + } + + @override + Set getKeys() { + return _preferences?.keys.toSet() ?? {}; + } + + @override + Future remove(String key) async { + if (containsKey(key)) { + await _preferences?.delete(key); + } + } + + @override + Future removeAll() async { + final keys = getKeys(); + await _preferences?.deleteAll(keys); + } + + @override + T? getValue(String key, {T? defaultValue}) { + var value = _preferences?.get(key); + if (value is T) { + return value; + } + return defaultValue; + } +} diff --git a/lib/main.dart b/lib/main.dart index 0efc333db..669ac64aa 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,12 +1,18 @@ import 'package:universal_io/io.dart'; import 'package:flutter/material.dart'; -import 'package:markdown_widget/markdown_widget.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; -import 'custom_node.dart'; -import 'package:sidebarx/sidebarx.dart'; +import 'book_tabs_viewer.dart'; +import 'settings_screen.dart'; +import 'package:flutter_settings_screen_ex/flutter_settings_screen_ex.dart'; +import 'cache_provider.dart'; -void main() { - runApp(FileExplorerApp()); + + + +void main(){ + Settings.init(cacheProvider: HiveCache()); + //Settings.clearCache(); + runApp(const FileExplorerApp()); } class FileExplorerApp extends StatelessWidget { @@ -30,144 +36,118 @@ class FileExplorerApp extends StatelessWidget { primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, fontFamily: 'candara', - textTheme: const TextTheme( + textTheme: const TextTheme( bodyMedium: TextStyle(fontSize: 18.0, fontFamily: 'candara'), ), ), - home: DirectoryBrowser(directory: Directory('./אוצריא'), openedFiles: []), + routes: { + '/search': (context) => BookSearchScreen(), + '/browser': (context) => + DirectoryBrowser(directory: Directory('.')), + '/settings': (context) => mySettingsScreen(), + }, + home: const MarkdownTabView(), + // DirectoryBrowser(directory: Directory('./אוצריא'), openedFiles: []), ); } } + + class DirectoryBrowser extends StatefulWidget { - final Directory directory; - List openedFiles; - DirectoryBrowser( - {Key? key, required this.directory, required this.openedFiles}) - : super(key: key); + Directory directory; + DirectoryBrowser({ + Key? key, + required this.directory, + }) : super(key: key); @override - _DirectoryBrowserState createState() => _DirectoryBrowserState(); + DirectoryBrowserState createState() => DirectoryBrowserState(); } -class _DirectoryBrowserState extends State { - late Future> _fileList; +class DirectoryBrowserState extends State { + late Future> _fileList; late int selectedIndex; - + @override void initState() { - super.initState(); + super.initState(); _fileList = widget.directory.list().toList(); selectedIndex = 0; } - openHomePage() { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => DirectoryBrowser( - directory: widget.directory, - openedFiles: widget.openedFiles, - )), - ); - } - -//open the search page - openSearchPage() { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => BookSearchScreen( - openedFiles: widget.openedFiles, - ), - )); +void navigateUp() { + final currentPath = widget.directory.path; + final parentDirectory = Directory(currentPath).parent; + if (Directory(currentPath).path != parentDirectory.path && Directory(currentPath).path != "./אוצריא" ) { + setState(() { + _fileList = parentDirectory.list().toList(); + }); } - - +} @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: const Text('אוצריא'), - ), - body: Row(children: [ - NavigationRail( - destinations: [ - NavigationRailDestination( - icon: Icon(Icons.library_books), - label: Text('Home'), - ), - NavigationRailDestination( - icon: Icon(Icons.search), - label: Text('Search'), - ), - ], - selectedIndex: selectedIndex, - onDestinationSelected: (int index) { - setState(() { - if (index != selectedIndex){ - selectedIndex = index; - switch (index) { - case 0: - openHomePage(); - case 1: - openSearchPage(); - } - }}); - }), - Expanded( - child: FutureBuilder>( - future: _fileList, - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - if (snapshot.hasError) { - return Center(child: Text('Error: ${snapshot.error}')); - } - - if (snapshot.hasData) { - return ListView.builder( - itemCount: snapshot.data!.length, - itemBuilder: (context, index) { - FileSystemEntity entity = snapshot.data![index]; - return ListTile( - title: Text(entity.path.split('/').last), - leading: entity is Directory - ? const Icon(Icons.my_library_books) - : const Icon(Icons.book), - onTap: () { - if (entity is Directory) { - Navigator.of(context).push(MaterialPageRoute( - builder: (context) => DirectoryBrowser( - directory: entity, - openedFiles: widget.openedFiles), - )); - } else if (entity is File) { - widget.openedFiles.insert(0, entity); - Navigator.of(context).push(MaterialPageRoute( - builder: (context) => MarkdownTabView( - markdownFiles: widget.openedFiles), - )); - } - }, - ); - }, - ); - } - } - - return const Center(child: CircularProgressIndicator()); - }, + appBar: AppBar( + title: const Text('אוצריא'), + actions: [ + Align( + alignment: Alignment.centerRight, + child: IconButton( + icon: const Icon(Icons.arrow_upward), + tooltip: 'חזרה לתיקיה הקודמת', + onPressed: navigateUp, ), - ), - ])); + ) + ] + ), + body: FutureBuilder>( + future: _fileList, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + if (snapshot.hasError) { + return Center(child: Text('Error: ${snapshot.error}')); + } + + if (snapshot.hasData) { + return ListView.builder( + itemCount: snapshot.data!.length, + itemBuilder: (context, index) { + FileSystemEntity entity = snapshot.data![index]; + return ListTile( + title: Text(entity.path.split('/').last), + leading: entity is Directory + ? const Icon(Icons.my_library_books) + : const Icon(Icons.book), + onTap: () { + if (entity is Directory) { + setState(() { + //_fileList = Directory(entity.path).list().toList(); + widget.directory = entity; + _fileList = Directory(entity.path).list().toList(); + }); + } else if (entity is File) { + Navigator.pop(context, entity); + } + }, + ); + }, + ); + } + } + + return const Center(child: CircularProgressIndicator()); + }, + ), + ); } } class BookSearchScreen extends StatefulWidget { - List openedFiles; - - BookSearchScreen({Key? key, required this.openedFiles}) : super(key: key); + BookSearchScreen({ + Key? key, + }) : super(key: key); @override _BookSearchScreenState createState() => _BookSearchScreenState(); @@ -221,6 +201,7 @@ class _BookSearchScreenState extends State { child: Column( children: [ TextField( + autofocus: true, controller: _searchController, decoration: InputDecoration( labelText: 'הקלד שם ספר: ', @@ -234,15 +215,10 @@ class _BookSearchScreenState extends State { itemBuilder: (context, index) { final book = _searchResults[index]; return ListTile( - title: Text(book.path.split('/').last), - onTap: () { - widget.openedFiles.insert(0, book); - Navigator.of(context).push(MaterialPageRoute( - builder: (context) => MarkdownTabView( - markdownFiles: widget.openedFiles), - )); - }, - ); + title: Text(book.path.split('/').last), + onTap: () { + Navigator.of(context).pop(book); + }); }, ), ), @@ -252,92 +228,3 @@ class _BookSearchScreenState extends State { )); } } - -class MarkdownTabView extends StatefulWidget { - final List markdownFiles; - - const MarkdownTabView({Key? key, required this.markdownFiles}) - : super(key: key); - - @override - _MarkdownTabViewState createState() => _MarkdownTabViewState(); -} - -class _MarkdownTabViewState extends State - with SingleTickerProviderStateMixin { - void closeAllTabs() { - setState(() { - widget.markdownFiles.removeAt(widget.markdownFiles.length - 1); - }); - } - - @override - Widget build(BuildContext context) { - return DefaultTabController( - length: widget.markdownFiles.length, - child: Scaffold( - appBar: AppBar( - actions: [ - IconButton( - icon: Icon(Icons.close), - onPressed: closeAllTabs, - ) - ], - bottom: TabBar( - tabs: widget.markdownFiles - .map((file) => Tab(text: file.path.split('/').last)) - .toList(), - ), - ), - body: TabBarView( - children: widget.markdownFiles.map((file) { - return BookViewer( - file: file, - ); - }).toList(), - ), - ), - ); - } -} - -class BookViewer extends StatelessWidget { - final File file; - late Future data; - BookViewer({Key? key, required this.file}) : super(key: key) - { - data = file.readAsString(); - } - - final tocController = TocController(); - Widget buildTocWidget() => TocWidget(controller: tocController); - Widget buildMarkdown() => FutureBuilder( - future: data.then((value) => value), - builder:(context, snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - if (snapshot.hasError) { - return Center(child: Text('Error: ${snapshot.error}')); - } - - if (snapshot.hasData) { - return MarkdownWidget( - padding: const EdgeInsets.all(50), - data: snapshot.data!, - tocController: tocController, - markdownGenerator: MarkdownGenerator( - textGenerator: (node, config, visitor) => - CustomTextNode(node.textContent, config, visitor))); -}} -return const Center(child: CircularProgressIndicator());}); - - @override - Widget build(BuildContext context) { - return Scaffold( - drawer: Drawer(child: buildTocWidget()), - appBar: AppBar( - title: Text('${file.path.split('/').last}'), - ), - body: buildMarkdown(), - ); - } -} diff --git a/lib/settings_screen.dart b/lib/settings_screen.dart new file mode 100644 index 000000000..02bf95928 --- /dev/null +++ b/lib/settings_screen.dart @@ -0,0 +1,52 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_settings_screen_ex/flutter_settings_screen_ex.dart'; + + +class mySettingsScreen extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + body: Container( + child: SettingsScreen( + title: 'הגדרות', + children: [ + SettingsGroup( + title: 'הגדרות גופן', + children: [ + SliderSettingsTile( + title: 'גודל גופן בספר', + settingKey: 'key-font-size', + defaultValue: 20, + min: 15, + max: 50, + step: 1, + leading: Icon(Icons.font_download), + decimalPrecision: 0, + onChange: (value) { + + }, + ), + DropDownSettingsTile( + title: 'גופן', + settingKey: 'key-font-family', + values: { + 'David': 'דוד', + 'Arial': 'אריאל', + 'Candara': 'קנדרה', + 'roboto': 'רובוטו', + 'Calibri': 'קליברי', + }, + selected: 'David', + onChange: (value) { + debugPrint('key-dropdown-email-view: $value'); + }, +) + ], + ), + + ], + ), + ) + ); + } + } \ No newline at end of file diff --git a/lib/welcome.md b/lib/welcome.md new file mode 100644 index 000000000..73d17dc60 --- /dev/null +++ b/lib/welcome.md @@ -0,0 +1 @@ +# hello world \ No newline at end of file diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 98813a0c3..0cc8a4746 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -6,12 +6,14 @@ import FlutterMacOS import Foundation import path_provider_foundation +import shared_preferences_foundation import url_launcher_macos import video_player_avfoundation import wakelock_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) + SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) FVPVideoPlayerPlugin.register(with: registry.registrar(forPlugin: "FVPVideoPlayerPlugin")) WakelockMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockMacosPlugin")) diff --git a/pubspec.lock b/pubspec.lock index d0a57263b..26305d66f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -65,6 +65,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.18.0" + crypto: + dependency: transitive + description: + name: crypto + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab + url: "https://pub.dev" + source: hosted + version: "3.0.3" csslib: dependency: transitive description: @@ -97,6 +105,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.0" + file: + dependency: transitive + description: + name: file + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" + url: "https://pub.dev" + source: hosted + version: "7.0.0" flutter: dependency: "direct main" description: flutter @@ -147,6 +163,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.5.0" + flutter_settings_screen_ex: + dependency: "direct main" + description: + name: flutter_settings_screen_ex + sha256: "258f0ba2c2b5a9c40b60c219c29cc6183229fcd4e68f212bed1b9485397d44bd" + url: "https://pub.dev" + source: hosted + version: "0.0.1+5" flutter_svg: dependency: transitive description: @@ -181,6 +205,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.0" + hive: + dependency: "direct main" + description: + name: hive + sha256: "8dcf6db979d7933da8217edcec84e9df1bdb4e4edc7fc77dbd5aa74356d6d941" + url: "https://pub.dev" + source: hosted + version: "2.2.3" html: dependency: "direct main" description: @@ -389,14 +421,62 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.1" - sidebarx: - dependency: "direct main" + shared_preferences: + dependency: transitive description: - name: sidebarx - sha256: "7042d64844b8e64ca5c17e70d89b49df35b54a26c015b90000da9741eab70bc0" + name: shared_preferences + sha256: "81429e4481e1ccfb51ede496e916348668fd0921627779233bd24cc3ff6abd02" url: "https://pub.dev" source: hosted - version: "0.16.3" + version: "2.2.2" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "8568a389334b6e83415b6aae55378e158fbc2314e074983362d20c562780fb06" + url: "https://pub.dev" + source: hosted + version: "2.2.1" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "7708d83064f38060c7b39db12aefe449cb8cdc031d6062280087bc4cdb988f5c" + url: "https://pub.dev" + source: hosted + version: "2.3.5" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "22e2ecac9419b4246d7c22bfbbda589e3acf5c0351137d87dd2939d984d37c3b" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: "7b15ffb9387ea3e237bb7a66b8a23d2147663d391cafc5c8f37b2e7b4bde5d21" + url: "https://pub.dev" + source: hosted + version: "2.2.2" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59" + url: "https://pub.dev" + source: hosted + version: "2.3.2" sky_engine: dependency: transitive description: flutter @@ -475,7 +555,7 @@ packages: source: hosted version: "2.2.2" url_launcher: - dependency: "direct main" + dependency: transitive description: name: url_launcher sha256: c512655380d241a337521703af62d2c122bf7b77a46ff7dd750092aa9433499c diff --git a/pubspec.yaml b/pubspec.yaml index f763d7b95..e5f21843c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -39,9 +39,9 @@ dependencies: webview_flutter: ^2.8.0 markdown_widget: ^2.3.2+3 html: ^0.15.0 - url_launcher: ^6.1.8 markdown: ^7.1.1 - sidebarx: ^0.16.2 + flutter_settings_screen_ex: ^0.0.1+5 + hive: ^2.2.3