custom directory

This commit is contained in:
randogoth 2025-02-21 22:02:06 +02:00
parent 185a1c62e9
commit 7e82ddcbb7
12 changed files with 396 additions and 56 deletions

View file

@ -9,6 +9,7 @@ android {
namespace = "com.example.muzzlevelocity" namespace = "com.example.muzzlevelocity"
compileSdk = flutter.compileSdkVersion compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion ndkVersion = flutter.ndkVersion
compileSdkVersion 34
compileOptions { compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8 sourceCompatibility = JavaVersion.VERSION_1_8
@ -28,6 +29,7 @@ android {
targetSdk = flutter.targetSdkVersion targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode versionCode = flutter.versionCode
versionName = flutter.versionName versionName = flutter.versionName
targetSdkVersion 34
} }
buildTypes { buildTypes {

View file

@ -1,8 +1,14 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
<application <application
android:label="muzzlevelocity" android:label="muzzlevelocity"
android:name="${applicationName}" android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"> android:icon="@mipmap/ic_launcher"
android:requestLegacyExternalStorage="true">
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:exported="true" android:exported="true"
@ -42,5 +48,14 @@
<action android:name="android.intent.action.PROCESS_TEXT"/> <action android:name="android.intent.action.PROCESS_TEXT"/>
<data android:mimeType="text/plain"/> <data android:mimeType="text/plain"/>
</intent> </intent>
<intent>
<action android:name="android.intent.action.MAIN" />
</intent>
<intent>
<action android:name="android.intent.action.OPEN_DOCUMENT_TREE" />
</intent>
<intent>
<action android:name="android.settings.MANAGE_ALL_FILES_ACCESS_PERMISSION"/>
</intent>
</queries> </queries>
</manifest> </manifest>

View file

@ -1,5 +1,11 @@
package com.example.muzzlevelocity package com.example.muzzlevelocity
import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.android.FlutterFragmentActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugins.GeneratedPluginRegistrant
class MainActivity: FlutterActivity() class MainActivity: FlutterFragmentActivity() {
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine)
}
}

View file

@ -1,11 +1,19 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart'; import 'package:google_fonts/google_fonts.dart';
import 'package:muzzlevelocity/themes/day.dart';
import 'package:muzzlevelocity/themes/night.dart';
import 'dart:io'; import 'dart:io';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'note_editor.dart'; import 'note_editor.dart';
import 'search_service.dart'; import 'package:file_picker/file_picker.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:path/path.dart' as path;
void main() {
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Permission.storage.request();
runApp(MuzzleVelocityApp()); runApp(MuzzleVelocityApp());
} }
@ -44,8 +52,9 @@ class _MuzzleVelocityAppState extends State<MuzzleVelocityApp> {
} }
Future<void> _emptyTrash() async { Future<void> _emptyTrash() async {
final dir = await getApplicationDocumentsDirectory(); final prefs = await SharedPreferences.getInstance();
final trashDir = Directory('${dir.path}/notes/trash'); final notesPath = prefs.getString('notes_directory') ?? (await getApplicationDocumentsDirectory()).path;
final trashDir = Directory('$notesPath/trash');
if (trashDir.existsSync()) { if (trashDir.existsSync()) {
trashDir.deleteSync(recursive: true); trashDir.deleteSync(recursive: true);
trashDir.createSync(); trashDir.createSync();
@ -60,13 +69,13 @@ class _MuzzleVelocityAppState extends State<MuzzleVelocityApp> {
brightness: Brightness.light, brightness: Brightness.light,
scaffoldBackgroundColor: Color(0xffF6F4F1), scaffoldBackgroundColor: Color(0xffF6F4F1),
appBarTheme: AppBarTheme(backgroundColor: Color(0xffF6F4F1)), appBarTheme: AppBarTheme(backgroundColor: Color(0xffF6F4F1)),
textTheme: GoogleFonts.ibmPlexMonoTextTheme(), textTheme: GoogleFonts.ibmPlexMonoTextTheme(dayText),
), ),
darkTheme: ThemeData( darkTheme: ThemeData(
brightness: Brightness.dark, brightness: Brightness.dark,
scaffoldBackgroundColor: Color(0xFF121212), scaffoldBackgroundColor: Color(0xFF121212),
appBarTheme: AppBarTheme(backgroundColor: Color(0xFF121212)), appBarTheme: AppBarTheme(backgroundColor: Color(0xFF121212)),
textTheme: GoogleFonts.ibmPlexMonoTextTheme(), textTheme: GoogleFonts.ibmPlexMonoTextTheme(nightText),
), ),
themeMode: _themeMode, themeMode: _themeMode,
home: NoteListScreen( home: NoteListScreen(
@ -103,55 +112,65 @@ class _NoteListScreenState extends State<NoteListScreen> {
TextEditingController searchController = TextEditingController(); TextEditingController searchController = TextEditingController();
List<File> notes = []; List<File> notes = [];
List<File> filteredNotes = []; List<File> filteredNotes = [];
String? notesDirectoryPath;
String? defaultDir;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_loadNotes(); _initializeApp(); // Call an async method to handle initialization
searchController.addListener(() { searchController.addListener(() {
final text = searchController.text.trim(); final text = searchController.text.trim();
if (text.startsWith(':')) { if (text.startsWith(':')) {
_handleCommand(text); _handleCommand(text);
} else { } else {
_filterNotes(); // Only filter, do NOT create a note here! _filterNotes();
} }
}); });
} }
Future<void> _initializeApp() async {
final dir = await getApplicationDocumentsDirectory();
setState(() {
defaultDir = dir.path; // Update state with defaultDir
});
await _loadNotes(); // Load notes after setting defaultDir
}
bool _isNewNote(String title) { bool _isNewNote(String title) {
final safeTitle = _sanitizeFilename(title); final safeTitle = _sanitizeFilename(title);
return !notes.any((note) => note.uri.pathSegments.last == '$safeTitle.md'); return !notes.any((note) => note.uri.pathSegments.last == '$safeTitle.md');
} }
Future<void> _createNewNote(String title) async { void _createNewNote(String title) async {
final dir = await getApplicationDocumentsDirectory(); try {
final notesDir = Directory('${dir.path}/notes');
if (!notesDir.existsSync()) {
notesDir.createSync(recursive: true);
}
final safeTitle = _sanitizeFilename(title); final safeTitle = _sanitizeFilename(title);
final newNote = File('${notesDir.path}/$safeTitle.md'); final newNote = File(path.join(notesDirectoryPath!, '$safeTitle.md'));
if (!newNote.existsSync()) { if (!newNote.existsSync()) {
newNote.writeAsStringSync('# $title\n\n'); // Pre-fill with title newNote.writeAsStringSync('# $title\n\n');
setState(() { setState(() {
notes.add(newNote); notes.add(newNote);
filteredNotes.add(newNote); filteredNotes.add(newNote);
}); });
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => NoteEditorScreen( builder: (context) => NoteEditorScreen(
note: newNote, note: newNote,
isDarkMode: widget.isDarkMode, isDarkMode: widget.isDarkMode,
notesDirectoryPath: notesDirectoryPath,
), ),
), ),
); );
} }
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Failed to create note: $e'),
),
);
}
} }
String _sanitizeFilename(String title) { String _sanitizeFilename(String title) {
@ -210,20 +229,57 @@ class _NoteListScreenState extends State<NoteListScreen> {
Future<void> _loadNotes() async { Future<void> _loadNotes() async {
final dir = await getApplicationDocumentsDirectory(); final prefs = await SharedPreferences.getInstance();
final notesDir = Directory('${dir.path}/notes'); notesDirectoryPath = prefs.getString('notes_directory') ?? defaultDir; // Use custom or default directory
final notesDir = Directory(notesDirectoryPath!);
if (!notesDir.existsSync()) { if (!notesDir.existsSync()) {
notesDir.createSync(recursive: true); notesDir.createSync(recursive: true);
} }
final files = notesDir.listSync().whereType<File>().where((file) => file.path.endsWith('.md')).toList(); final files = notesDir.listSync().whereType<File>().where((file) => file.path.endsWith('.md')).toList();
if (mounted) {
setState(() { setState(() {
notes = files; notes = files;
filteredNotes = files; filteredNotes = files;
}); });
} }
void _showDirectoryChoiceDialog(String defaultPath) {
showDialog(
context: context,
barrierDismissible: false, // Force user to make a choice
builder: (BuildContext context) {
return AlertDialog(
title: Text("Choose Notes Directory"),
content: Text("Your notes will be saved in:\n\n📂 $defaultPath\n\nWould you like to use a different folder?"),
actions: [
TextButton(
onPressed: () async {
// Reset to default directory
final prefs = await SharedPreferences.getInstance();
await prefs.remove('notes_directory'); // Remove custom directory from preferences
setState(() {
notesDirectoryPath = defaultDir; // Reset to default directory
});
Navigator.pop(context); // Close dialog
_loadNotes(); // Reload notes from the default directory
},
child: Text("Use Default"),
),
TextButton(
onPressed: () async {
Navigator.pop(context); // Close the current dialog
await _selectNotesDirectory(); // Allow user to pick a folder
},
child: Text("Choose Folder"),
),
],
);
},
);
} }
void _filterNotes() { void _filterNotes() {
final query = searchController.text.toLowerCase(); final query = searchController.text.toLowerCase();
setState(() { setState(() {
@ -279,6 +335,38 @@ class _NoteListScreenState extends State<NoteListScreen> {
return spans; return spans;
} }
Future<void> _selectNotesDirectory() async {
String? selectedDirectory = await FilePicker.platform.getDirectoryPath();
if (selectedDirectory != null) {
final prefs = await SharedPreferences.getInstance();
await prefs.setString('notes_directory', selectedDirectory); // Save custom directory
setState(() {
notesDirectoryPath = selectedDirectory; // Update state
});
_loadNotes(); // Reload notes from the new directory
}
}
Future<bool> _requestStoragePermission() async {
if (Platform.isAndroid) {
if (await Permission.storage.request().isGranted) {
print("✅ Basic storage permission granted.");
return true;
}
// For Android 11+ (Scoped Storage)
if (await Permission.manageExternalStorage.request().isGranted) {
print("✅ Full file access granted.");
return true;
}
print("❌ Storage permission denied. Opening settings...");
await openAppSettings(); // Open settings if denied
return false;
}
return true; // No permissions needed for other platforms
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
@ -322,6 +410,9 @@ class _NoteListScreenState extends State<NoteListScreen> {
widget.toggleTheme(!widget.isDarkMode); widget.toggleTheme(!widget.isDarkMode);
}); });
break; break;
case 'Select Folder':
_showDirectoryChoiceDialog(notesDirectoryPath ?? ''); // Re-trigger popup
break;
case 'Empty Trash': case 'Empty Trash':
_confirmEmptyTrash(); _confirmEmptyTrash();
break; break;
@ -346,6 +437,13 @@ class _NoteListScreenState extends State<NoteListScreen> {
), ),
), ),
), ),
PopupMenuItem<String>(
value: 'Select Folder',
child: ListTile(
leading: Icon(Icons.folder),
title: Text('Set Notes Directory'),
),
),
PopupMenuItem<String>( PopupMenuItem<String>(
value: 'Empty Trash', value: 'Empty Trash',
child: ListTile( child: ListTile(
@ -386,14 +484,16 @@ class _NoteListScreenState extends State<NoteListScreen> {
await Navigator.push( await Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => NoteEditorScreen( builder: (context) =>
note: noteFile, NoteEditorScreen(
note: File(path.join(notesDirectoryPath!, noteFile.uri.pathSegments.last)),
isDarkMode: widget.isDarkMode, isDarkMode: widget.isDarkMode,
notesDirectoryPath: notesDirectoryPath,
), ),
), ),
); );
_loadNotes(); // 🔄 Reload notes after returning _loadNotes(); // Reload notes after returning
setState(() {}); // 🔄 Force UI refresh setState(() {}); // Force UI refresh
}, },
), ),
); );

View file

@ -7,18 +7,21 @@ import 'themes/day.dart';
import 'themes/night.dart'; import 'themes/night.dart';
import 'themes/markdown.dart'; import 'themes/markdown.dart';
import 'dart:io'; import 'dart:io';
import 'package:path/path.dart' as path;
class NoteEditorScreen extends StatefulWidget { class NoteEditorScreen extends StatefulWidget {
final File note; final File note;
final String? notesDirectoryPath; // Add this
bool isDarkMode; bool isDarkMode;
NoteEditorScreen({required this.note, required this.isDarkMode}); NoteEditorScreen({required this.note, required this.isDarkMode, required this.notesDirectoryPath});
@override @override
_NoteEditorScreenState createState() => _NoteEditorScreenState(); _NoteEditorScreenState createState() => _NoteEditorScreenState();
} }
class _NoteEditorScreenState extends State<NoteEditorScreen> { class _NoteEditorScreenState extends State<NoteEditorScreen> {
bool isLoading = true;
bool _isPreviewMode = false; // Default: Editing mode bool _isPreviewMode = false; // Default: Editing mode
late CodeController _controller; late CodeController _controller;
double _fontSize = 16.0; // Default font size double _fontSize = 16.0; // Default font size
@ -37,7 +40,13 @@ class _NoteEditorScreenState extends State<NoteEditorScreen> {
} }
Future<void> _loadNoteContent() async { Future<void> _loadNoteContent() async {
final content = await widget.note.readAsString(); String content = "";
try {
content = await widget.note.readAsString();
} catch (e) {
print("❌ Failed to read file: ${widget.note.path}");
}
setState(() { setState(() {
_controller = CodeController( _controller = CodeController(
text: content, text: content,
@ -45,11 +54,26 @@ class _NoteEditorScreenState extends State<NoteEditorScreen> {
); );
_controller.addListener(_saveNote); _controller.addListener(_saveNote);
_controller.popupController.enabled = false; _controller.popupController.enabled = false;
isLoading = false;
}); });
} }
Future<void> _saveNote() async { Future<void> _saveNote() async {
await widget.note.writeAsString(_controller.text); try {
final notesDirectory = widget.notesDirectoryPath ?? widget.note.parent.path; // Use correct directory
final newFilePath = path.join(notesDirectory, widget.note.uri.pathSegments.last);
print("📝 Attempting to save note at: $newFilePath");
final newFile = File(newFilePath);
await newFile.writeAsString(_controller.text);
print("✅ Successfully saved note at: $newFilePath");
} catch (e, stackTrace) {
print("❌ Error saving note: ${widget.note.path}");
print("⚠️ Exception: $e");
print("🛠 Stack trace: $stackTrace");
}
} }
@override @override
@ -118,7 +142,9 @@ class _NoteEditorScreenState extends State<NoteEditorScreen> {
), ),
], ],
), ),
body: _controller == null body: isLoading
? Center(child: CircularProgressIndicator()) // Show loader while initializing
: _controller == null
? Center(child: CircularProgressIndicator()) ? Center(child: CircularProgressIndicator())
: _isPreviewMode : _isPreviewMode
? Padding( ? Padding(

View file

@ -3,6 +3,24 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/painting.dart'; import 'package:flutter/painting.dart';
const TextTheme dayText = TextTheme(
titleLarge: TextStyle(color: Color(0xff333333)),
bodyLarge: TextStyle(color: Color(0xff333333)),
bodyMedium: TextStyle(color: Color(0xff333333)),
bodySmall: TextStyle(color: Color(0xff333333)),
displayLarge: TextStyle(color: Color(0xff333333)),
displayMedium: TextStyle(color: Color(0xff333333)),
displaySmall: TextStyle(color: Color(0xff333333)),
headlineLarge: TextStyle(color: Color(0xff333333)),
headlineMedium: TextStyle(color: Color(0xff333333)),
headlineSmall: TextStyle(color: Color(0xff333333)),
labelLarge: TextStyle(color: Color(0xff333333)),
labelMedium: TextStyle(color: Color(0xff333333)),
labelSmall: TextStyle(color: Color(0xff333333)),
titleMedium: TextStyle(color: Color(0xff333333)),
titleSmall: TextStyle(color: Color(0xff333333)),
);
const dayTheme = { const dayTheme = {
'root': 'root':
TextStyle(backgroundColor: Color(0xffF6F4F1), color: Color(0xff333333)), TextStyle(backgroundColor: Color(0xffF6F4F1), color: Color(0xff333333)),

View file

@ -3,6 +3,24 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/painting.dart'; import 'package:flutter/painting.dart';
const TextTheme nightText = TextTheme(
titleLarge: TextStyle(color: Color(0xffc5c8c6)),
bodyLarge: TextStyle(color: Color(0xffc5c8c6)),
bodyMedium: TextStyle(color: Color(0xffc5c8c6)),
bodySmall: TextStyle(color: Color(0xffc5c8c6)),
displayLarge: TextStyle(color: Color(0xffc5c8c6)),
displayMedium: TextStyle(color: Color(0xffc5c8c6)),
displaySmall: TextStyle(color: Color(0xffc5c8c6)),
headlineLarge: TextStyle(color: Color(0xffc5c8c6)),
headlineMedium: TextStyle(color: Color(0xffc5c8c6)),
headlineSmall: TextStyle(color: Color(0xffc5c8c6)),
labelLarge: TextStyle(color: Color(0xffc5c8c6)),
labelMedium: TextStyle(color: Color(0xffc5c8c6)),
labelSmall: TextStyle(color: Color(0xffc5c8c6)),
titleMedium: TextStyle(color: Color(0xffc5c8c6)),
titleSmall: TextStyle(color: Color(0xffc5c8c6)),
);
const nightTheme = { const nightTheme = {
'comment': TextStyle(color: Color(0xff969896)), 'comment': TextStyle(color: Color(0xff969896)),
'quote': TextStyle(color: Color(0xff969896)), 'quote': TextStyle(color: Color(0xff969896)),

View file

@ -5,10 +5,14 @@
import FlutterMacOS import FlutterMacOS
import Foundation import Foundation
import file_picker
import path_provider_foundation import path_provider_foundation
import shared_preferences_foundation
import url_launcher_macos import url_launcher_macos
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
} }

View file

@ -65,6 +65,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.19.0" version: "1.19.0"
cross_file:
dependency: transitive
description:
name: cross_file
sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670"
url: "https://pub.dev"
source: hosted
version: "0.3.4+2"
crypto: crypto:
dependency: transitive dependency: transitive
description: description:
@ -105,6 +113,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.3" version: "2.1.3"
file:
dependency: transitive
description:
name: file
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
url: "https://pub.dev"
source: hosted
version: "7.0.1"
file_picker:
dependency: "direct main"
description:
name: file_picker
sha256: "6f6bfa8797f296965bdc3e1f702574ab49a540c19b9237b401e7c2b25dfe594c"
url: "https://pub.dev"
source: hosted
version: "9.0.0"
flutter: flutter:
dependency: "direct main" dependency: "direct main"
description: flutter description: flutter
@ -158,6 +182,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.3" version: "0.7.3"
flutter_plugin_android_lifecycle:
dependency: transitive
description:
name: flutter_plugin_android_lifecycle
sha256: "615a505aef59b151b46bbeef55b36ce2b6ed299d160c51d84281946f0aa0ce0e"
url: "https://pub.dev"
source: hosted
version: "2.0.24"
flutter_svg: flutter_svg:
dependency: transitive dependency: transitive
description: description:
@ -368,6 +400,54 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.3.0" version: "2.3.0"
permission_handler:
dependency: "direct main"
description:
name: permission_handler
sha256: "59adad729136f01ea9e35a48f5d1395e25cba6cea552249ddbe9cf950f5d7849"
url: "https://pub.dev"
source: hosted
version: "11.4.0"
permission_handler_android:
dependency: transitive
description:
name: permission_handler_android
sha256: d3971dcdd76182a0c198c096b5db2f0884b0d4196723d21a866fc4cdea057ebc
url: "https://pub.dev"
source: hosted
version: "12.1.0"
permission_handler_apple:
dependency: transitive
description:
name: permission_handler_apple
sha256: f84a188e79a35c687c132a0a0556c254747a08561e99ab933f12f6ca71ef3c98
url: "https://pub.dev"
source: hosted
version: "9.4.6"
permission_handler_html:
dependency: transitive
description:
name: permission_handler_html
sha256: "38f000e83355abb3392140f6bc3030660cfaef189e1f87824facb76300b4ff24"
url: "https://pub.dev"
source: hosted
version: "0.1.3+5"
permission_handler_platform_interface:
dependency: transitive
description:
name: permission_handler_platform_interface
sha256: eb99b295153abce5d683cac8c02e22faab63e50679b937fa1bf67d58bb282878
url: "https://pub.dev"
source: hosted
version: "4.3.0"
permission_handler_windows:
dependency: transitive
description:
name: permission_handler_windows
sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e"
url: "https://pub.dev"
source: hosted
version: "0.2.1"
petitparser: petitparser:
dependency: transitive dependency: transitive
description: description:
@ -408,6 +488,62 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.3.8" version: "0.3.8"
shared_preferences:
dependency: "direct main"
description:
name: shared_preferences
sha256: "846849e3e9b68f3ef4b60c60cf4b3e02e9321bc7f4d8c4692cf87ffa82fc8a3a"
url: "https://pub.dev"
source: hosted
version: "2.5.2"
shared_preferences_android:
dependency: transitive
description:
name: shared_preferences_android
sha256: a768fc8ede5f0c8e6150476e14f38e2417c0864ca36bb4582be8e21925a03c22
url: "https://pub.dev"
source: hosted
version: "2.4.6"
shared_preferences_foundation:
dependency: transitive
description:
name: shared_preferences_foundation
sha256: "6a52cfcdaeac77cad8c97b539ff688ccfc458c007b4db12be584fbe5c0e49e03"
url: "https://pub.dev"
source: hosted
version: "2.5.4"
shared_preferences_linux:
dependency: transitive
description:
name: shared_preferences_linux
sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
shared_preferences_platform_interface:
dependency: transitive
description:
name: shared_preferences_platform_interface
sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
shared_preferences_web:
dependency: transitive
description:
name: shared_preferences_web
sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019
url: "https://pub.dev"
source: hosted
version: "2.4.3"
shared_preferences_windows:
dependency: transitive
description:
name: shared_preferences_windows
sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
sky_engine: sky_engine:
dependency: transitive dependency: transitive
description: flutter description: flutter
@ -589,6 +725,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.0" version: "1.1.0"
win32:
dependency: transitive
description:
name: win32
sha256: daf97c9d80197ed7b619040e86c8ab9a9dad285e7671ee7390f9180cc828a51e
url: "https://pub.dev"
source: hosted
version: "5.10.1"
xdg_directories: xdg_directories:
dependency: transitive dependency: transitive
description: description:

View file

@ -40,6 +40,9 @@ dependencies:
flutter_code_editor: ^0.3.2 flutter_code_editor: ^0.3.2
flutter_markdown: ^0.7.6+2 flutter_markdown: ^0.7.6+2
flutter_markdown_latex: ^0.3.4 flutter_markdown_latex: ^0.3.4
permission_handler: ^11.4.0
shared_preferences: ^2.5.2
file_picker: ^9.0.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:

View file

@ -6,9 +6,12 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <permission_handler_windows/permission_handler_windows_plugin.h>
#include <url_launcher_windows/url_launcher_windows.h> #include <url_launcher_windows/url_launcher_windows.h>
void RegisterPlugins(flutter::PluginRegistry* registry) { void RegisterPlugins(flutter::PluginRegistry* registry) {
PermissionHandlerWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
UrlLauncherWindowsRegisterWithRegistrar( UrlLauncherWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("UrlLauncherWindows")); registry->GetRegistrarForPlugin("UrlLauncherWindows"));
} }

View file

@ -3,6 +3,7 @@
# #
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
permission_handler_windows
url_launcher_windows url_launcher_windows
) )