├── linux ├── .gitignore ├── main.cc ├── flutter │ ├── generated_plugin_registrant.h │ ├── generated_plugins.cmake │ ├── generated_plugin_registrant.cc │ └── CMakeLists.txt ├── my_application.h ├── my_application.cc └── CMakeLists.txt ├── assets ├── icon.png └── madebylasheen.png ├── snap ├── gui │ ├── v1.png │ └── dev.lasheen.qr.desktop └── snapcraft.yaml ├── screenshots ├── screenshot-mobile-1.png ├── screenshot-mobile-2.png ├── screenshot-mobile-3.png ├── screenshot-mobile-4.png ├── screenshot-desktop-1.png ├── screenshot-desktop-2.png └── screenshot-desktop-3.png ├── .gitignore ├── pubspec.yaml ├── README.md ├── .metadata ├── LICENSE ├── lib ├── main.dart └── screens │ ├── scanner_screen.dart │ ├── main_screen.dart │ └── generator_screen.dart ├── .github └── workflows │ └── main.yml ├── analysis_options.yaml └── pubspec.lock /linux/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral 2 | -------------------------------------------------------------------------------- /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YoussefLasheen/qr/HEAD/assets/icon.png -------------------------------------------------------------------------------- /snap/gui/v1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YoussefLasheen/qr/HEAD/snap/gui/v1.png -------------------------------------------------------------------------------- /assets/madebylasheen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YoussefLasheen/qr/HEAD/assets/madebylasheen.png -------------------------------------------------------------------------------- /screenshots/screenshot-mobile-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YoussefLasheen/qr/HEAD/screenshots/screenshot-mobile-1.png -------------------------------------------------------------------------------- /screenshots/screenshot-mobile-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YoussefLasheen/qr/HEAD/screenshots/screenshot-mobile-2.png -------------------------------------------------------------------------------- /screenshots/screenshot-mobile-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YoussefLasheen/qr/HEAD/screenshots/screenshot-mobile-3.png -------------------------------------------------------------------------------- /screenshots/screenshot-mobile-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YoussefLasheen/qr/HEAD/screenshots/screenshot-mobile-4.png -------------------------------------------------------------------------------- /screenshots/screenshot-desktop-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YoussefLasheen/qr/HEAD/screenshots/screenshot-desktop-1.png -------------------------------------------------------------------------------- /screenshots/screenshot-desktop-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YoussefLasheen/qr/HEAD/screenshots/screenshot-desktop-2.png -------------------------------------------------------------------------------- /screenshots/screenshot-desktop-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YoussefLasheen/qr/HEAD/screenshots/screenshot-desktop-3.png -------------------------------------------------------------------------------- /linux/main.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | int main(int argc, char** argv) { 4 | g_autoptr(MyApplication) app = my_application_new(); 5 | return g_application_run(G_APPLICATION(app), argc, argv); 6 | } 7 | -------------------------------------------------------------------------------- /snap/gui/dev.lasheen.qr.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Type=Application 3 | Name=Qr Scanner 4 | Comment=A Qrcode scanner/generator 5 | Categories=Utility; 6 | Icon=${SNAP}/meta/gui/v1.png 7 | Exec=qr-scanner 8 | Terminal=false -------------------------------------------------------------------------------- /linux/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void fl_register_plugins(FlPluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /linux/my_application.h: -------------------------------------------------------------------------------- 1 | #ifndef FLUTTER_MY_APPLICATION_H_ 2 | #define FLUTTER_MY_APPLICATION_H_ 3 | 4 | #include 5 | 6 | G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, 7 | GtkApplication) 8 | 9 | /** 10 | * my_application_new: 11 | * 12 | * Creates a new Flutter-based application. 13 | * 14 | * Returns: a new #MyApplication. 15 | */ 16 | MyApplication* my_application_new(); 17 | 18 | #endif // FLUTTER_MY_APPLICATION_H_ 19 | -------------------------------------------------------------------------------- /snap/snapcraft.yaml: -------------------------------------------------------------------------------- 1 | name: qr-scanner 2 | version: 1.0.0 3 | summary: A Qrcode scanner/generator 4 | description: An ad-free multi-platform Qr code scanner and generator 5 | 6 | confinement: strict 7 | base: core18 8 | grade: stable 9 | 10 | apps: 11 | qr-scanner: 12 | command: qr 13 | extensions: [flutter-master] # Where "master" defines which Flutter channel to use for the build 14 | plugs: 15 | - home 16 | parts: 17 | qr-scanner: 18 | source: . 19 | plugin: flutter 20 | flutter-target: lib/main.dart # The main entry-point file of the application 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | .vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | **/doc/api/ 26 | **/ios/Flutter/.last_build_id 27 | .dart_tool/ 28 | .flutter-plugins 29 | .flutter-plugins-dependencies 30 | .packages 31 | .pub-cache/ 32 | .pub/ 33 | /build/ 34 | 35 | # Symbolication related 36 | app.*.symbols 37 | 38 | # Obfuscation related 39 | app.*.map.json 40 | 41 | # Android Studio will place build artifacts here 42 | /android/app/debug 43 | /android/app/profile 44 | /android/app/release 45 | 46 | # Build files 47 | build-dir 48 | build_linux.sh 49 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: lasheen_qr 2 | 3 | # The following line prevents the package from being accidentally published to 4 | # pub.dev using `flutter pub publish`. This is preferred for private packages. 5 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 6 | 7 | version: 1.1.0+3 8 | 9 | environment: 10 | sdk: ^3.5.3 11 | 12 | dependencies: 13 | flutter: 14 | sdk: flutter 15 | path_provider: ^2.1.4 16 | path: ^1.9.0 17 | file_selector: ^1.0.3 18 | zxing2: ^0.2.3 19 | image: ^4.2.0 20 | handy_window: ^0.4.0 21 | pretty_qr_code: ^3.3.0 22 | url_launcher: ^6.3.0 23 | yaru: ^5.2.1 24 | flutter_spinbox: ^0.13.1 25 | screenshot: ^3.0.0 26 | package_info_plus: ^8.0.2 27 | screenshotx: ^1.0.0 28 | shadcn_ui: ^0.11.0 29 | dev_dependencies: 30 | flutter_test: 31 | sdk: flutter 32 | flutter_lints: ^2.0.0 33 | flutter: 34 | uses-material-design: true 35 | assets: 36 | - assets/madebylasheen.png 37 | - assets/icon.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | # QR Reader 4 | 5 | 6 | 7 | [](https://flathub.org/apps/details/dev.lasheen.qr) 8 | 9 | [Get it on Google Play](https://play.google.com/store/apps/details?id=dev.lasheen.qr.qr&pcampaignid=pcampaignidMKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1) 10 | 11 | [](https://snapcraft.io/qr-scanner) 12 | 13 | 14 | 15 | 16 | 17 | 18 | A beautiful material you qr scanner/generator app with absolutely no ads for Android, Linux, and MacOS. 19 | 20 | 21 | 22 | 23 |
24 | 25 | ### Privacy Policy 26 | 27 | This app does not collect any kind of personal data about the user. 28 | -------------------------------------------------------------------------------- /linux/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | file_selector_linux 7 | gtk 8 | handy_window 9 | screen_retriever 10 | url_launcher_linux 11 | window_manager 12 | yaru_window_linux 13 | ) 14 | 15 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 16 | ) 17 | 18 | set(PLUGIN_BUNDLED_LIBRARIES) 19 | 20 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 21 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) 22 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 23 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 24 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 25 | endforeach(plugin) 26 | 27 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) 28 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) 29 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 30 | endforeach(ffi_plugin) 31 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: "2663184aa79047d0a33a14a3b607954f8fdd8730" 8 | channel: "stable" 9 | 10 | project_type: app 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 17 | base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 18 | - platform: linux 19 | create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 20 | base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 21 | 22 | # User provided section 23 | 24 | # List of Local paths (relative to this file) that should be 25 | # ignored by the migrate tool. 26 | # 27 | # Files that are not part of the templates will be ignored by default. 28 | unmanaged_files: 29 | - 'lib/main.dart' 30 | - 'ios/Runner.xcodeproj/project.pbxproj' 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Youssef Lasheen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:shadcn_ui/shadcn_ui.dart'; 3 | import 'package:yaru/yaru.dart'; 4 | 5 | import 'screens/main_screen.dart'; 6 | 7 | Future main() async { 8 | WidgetsFlutterBinding.ensureInitialized(); 9 | await YaruWindowTitleBar.ensureInitialized(); 10 | 11 | runApp(const MyApp()); 12 | } 13 | 14 | class MyApp extends StatelessWidget { 15 | const MyApp({ 16 | super.key, 17 | }); 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | return ShadApp( 22 | debugShowCheckedModeBanner: false, 23 | builder: (context, child) { 24 | return YaruTitleBarTheme( 25 | data: YaruTitleBarThemeData( 26 | backgroundColor: WidgetStateColor.resolveWith( 27 | (states) { 28 | if (states.contains(WidgetState.focused)) { 29 | return Theme.of(context).colorScheme.secondary; 30 | } 31 | return Colors.transparent; 32 | }, 33 | ), 34 | ), 35 | child: child!, 36 | ); 37 | }, 38 | home: const MainScreen(), 39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: "Build" 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | build: 8 | name: Build & Release 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - name: Clone repository 13 | uses: actions/checkout@v4 14 | 15 | - name: Set Up Flutter 16 | uses: subosito/flutter-action@v2.14.0 17 | with: 18 | flutter-version: '3.24.3' 19 | 20 | - name: Install Flutter Dependencies 21 | run: flutter pub get 22 | 23 | - name: Install linux dependencies 24 | run: | 25 | sudo apt-get update -y 26 | sudo apt-get install clang cmake git ninja-build pkg-config libgtk-3-dev liblzma-dev libstdc++-12-dev 27 | 28 | 29 | - name: Build Linux App 30 | run: | 31 | # flutter build linux --release --target-platform=linux-arm64 32 | flutter build linux --release --target-platform=linux-x64 33 | 34 | - name: Archive Linux App 35 | run: | 36 | tar -czvf build/linux_build_x64.tar.gz build/linux/x64/release/bundle/qr 37 | 38 | - name: Upload Artifacts 39 | uses: actions/upload-artifact@v4 40 | with: 41 | name: release 42 | path: build/linux_build_x64.tar.gz 43 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at 17 | # https://dart-lang.github.io/linter/lints/index.html. 18 | # 19 | # Instead of disabling a lint rule for the entire project in the 20 | # section below, it can also be suppressed for a single line of code 21 | # or a specific dart file by using the `// ignore: name_of_lint` and 22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 23 | # producing the lint. 24 | rules: 25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 27 | 28 | # Additional information about this file can be found at 29 | # https://dart.dev/guides/language/analysis-options 30 | -------------------------------------------------------------------------------- /linux/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #include "generated_plugin_registrant.h" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | void fl_register_plugins(FlPluginRegistry* registry) { 18 | g_autoptr(FlPluginRegistrar) file_selector_linux_registrar = 19 | fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin"); 20 | file_selector_plugin_register_with_registrar(file_selector_linux_registrar); 21 | g_autoptr(FlPluginRegistrar) gtk_registrar = 22 | fl_plugin_registry_get_registrar_for_plugin(registry, "GtkPlugin"); 23 | gtk_plugin_register_with_registrar(gtk_registrar); 24 | g_autoptr(FlPluginRegistrar) handy_window_registrar = 25 | fl_plugin_registry_get_registrar_for_plugin(registry, "HandyWindowPlugin"); 26 | handy_window_plugin_register_with_registrar(handy_window_registrar); 27 | g_autoptr(FlPluginRegistrar) screen_retriever_registrar = 28 | fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverPlugin"); 29 | screen_retriever_plugin_register_with_registrar(screen_retriever_registrar); 30 | g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = 31 | fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); 32 | url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); 33 | g_autoptr(FlPluginRegistrar) window_manager_registrar = 34 | fl_plugin_registry_get_registrar_for_plugin(registry, "WindowManagerPlugin"); 35 | window_manager_plugin_register_with_registrar(window_manager_registrar); 36 | g_autoptr(FlPluginRegistrar) yaru_window_linux_registrar = 37 | fl_plugin_registry_get_registrar_for_plugin(registry, "YaruWindowLinuxPlugin"); 38 | yaru_window_linux_plugin_register_with_registrar(yaru_window_linux_registrar); 39 | } 40 | -------------------------------------------------------------------------------- /linux/flutter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file controls Flutter-level build steps. It should not be edited. 2 | cmake_minimum_required(VERSION 3.10) 3 | 4 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") 5 | 6 | # Configuration provided via flutter tool. 7 | include(${EPHEMERAL_DIR}/generated_config.cmake) 8 | 9 | # TODO: Move the rest of this into files in ephemeral. See 10 | # https://github.com/flutter/flutter/issues/57146. 11 | 12 | # Serves the same purpose as list(TRANSFORM ... PREPEND ...), 13 | # which isn't available in 3.10. 14 | function(list_prepend LIST_NAME PREFIX) 15 | set(NEW_LIST "") 16 | foreach(element ${${LIST_NAME}}) 17 | list(APPEND NEW_LIST "${PREFIX}${element}") 18 | endforeach(element) 19 | set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) 20 | endfunction() 21 | 22 | # === Flutter Library === 23 | # System-level dependencies. 24 | find_package(PkgConfig REQUIRED) 25 | pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) 26 | pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) 27 | pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) 28 | 29 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") 30 | 31 | # Published to parent scope for install step. 32 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) 33 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) 34 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) 35 | set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) 36 | 37 | list(APPEND FLUTTER_LIBRARY_HEADERS 38 | "fl_basic_message_channel.h" 39 | "fl_binary_codec.h" 40 | "fl_binary_messenger.h" 41 | "fl_dart_project.h" 42 | "fl_engine.h" 43 | "fl_json_message_codec.h" 44 | "fl_json_method_codec.h" 45 | "fl_message_codec.h" 46 | "fl_method_call.h" 47 | "fl_method_channel.h" 48 | "fl_method_codec.h" 49 | "fl_method_response.h" 50 | "fl_plugin_registrar.h" 51 | "fl_plugin_registry.h" 52 | "fl_standard_message_codec.h" 53 | "fl_standard_method_codec.h" 54 | "fl_string_codec.h" 55 | "fl_value.h" 56 | "fl_view.h" 57 | "flutter_linux.h" 58 | ) 59 | list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") 60 | add_library(flutter INTERFACE) 61 | target_include_directories(flutter INTERFACE 62 | "${EPHEMERAL_DIR}" 63 | ) 64 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") 65 | target_link_libraries(flutter INTERFACE 66 | PkgConfig::GTK 67 | PkgConfig::GLIB 68 | PkgConfig::GIO 69 | ) 70 | add_dependencies(flutter flutter_assemble) 71 | 72 | # === Flutter tool backend === 73 | # _phony_ is a non-existent file to force this command to run every time, 74 | # since currently there's no way to get a full input/output list from the 75 | # flutter tool. 76 | add_custom_command( 77 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} 78 | ${CMAKE_CURRENT_BINARY_DIR}/_phony_ 79 | COMMAND ${CMAKE_COMMAND} -E env 80 | ${FLUTTER_TOOL_ENVIRONMENT} 81 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" 82 | ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} 83 | VERBATIM 84 | ) 85 | add_custom_target(flutter_assemble DEPENDS 86 | "${FLUTTER_LIBRARY}" 87 | ${FLUTTER_LIBRARY_HEADERS} 88 | ) 89 | -------------------------------------------------------------------------------- /lib/screens/scanner_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | 4 | import 'package:pretty_qr_code/pretty_qr_code.dart'; 5 | import 'package:url_launcher/url_launcher.dart'; 6 | import 'package:yaru/yaru.dart'; 7 | import 'package:zxing2/qrcode.dart'; 8 | 9 | class ScannerScreen extends StatelessWidget { 10 | final Result result; 11 | 12 | const ScannerScreen({super.key, required this.result}); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return Scaffold( 17 | appBar: const YaruWindowTitleBar( 18 | title: Text('Scanner'), 19 | leading: YaruBackButton(), 20 | ), 21 | body: Center( 22 | child: SizedBox( 23 | width: 350, 24 | child: Column( 25 | mainAxisSize: MainAxisSize.max, 26 | mainAxisAlignment: MainAxisAlignment.center, 27 | children: [ 28 | Card( 29 | margin: EdgeInsets.zero, 30 | shape: RoundedRectangleBorder( 31 | borderRadius: BorderRadius.circular(15.0), 32 | ), 33 | child: Padding( 34 | padding: const EdgeInsets.all(25), 35 | child: PrettyQr( 36 | data: result.text, 37 | elementColor: Theme.of(context).colorScheme.onSurface, 38 | size: 300, 39 | roundEdges: true, 40 | ), 41 | ), 42 | ), 43 | const SizedBox( 44 | height: 10, 45 | ), 46 | SizedBox( 47 | width: double.infinity, 48 | child: Card( 49 | margin: EdgeInsets.zero, 50 | shape: RoundedRectangleBorder( 51 | borderRadius: BorderRadius.circular(15.0), 52 | ), 53 | child: Row( 54 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 55 | children: [ 56 | Expanded( 57 | child: Padding( 58 | padding: const EdgeInsets.only( 59 | left: 25, top: 25, bottom: 25), 60 | child: SelectableText(result.text, 61 | style: const TextStyle( 62 | fontSize: 20, 63 | fontWeight: FontWeight.bold, 64 | ), 65 | minLines: 1, 66 | maxLines: 3, onTap: () async { 67 | if (await canLaunchUrl(Uri.parse(result.text))) { 68 | launchUrl(Uri.parse(result.text)); 69 | } 70 | }), 71 | ), 72 | ), 73 | InkWell( 74 | borderRadius: BorderRadius.circular(15), 75 | onTap: () { 76 | Clipboard.setData(ClipboardData(text: result.text)); 77 | ScaffoldMessenger.of(context).showSnackBar( 78 | const SnackBar( 79 | content: Text('Copied to clipboard'), 80 | ), 81 | ); 82 | }, 83 | child: const Padding( 84 | padding: EdgeInsets.all(20), 85 | child: Icon( 86 | Icons.copy, 87 | size: 25, 88 | ), 89 | ), 90 | ), 91 | ], 92 | ), 93 | ), 94 | ), 95 | ], 96 | ), 97 | ), 98 | ), 99 | ); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /linux/my_application.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | #include 4 | #ifdef GDK_WINDOWING_X11 5 | #include 6 | #endif 7 | 8 | #include "flutter/generated_plugin_registrant.h" 9 | 10 | struct _MyApplication { 11 | GtkApplication parent_instance; 12 | char** dart_entrypoint_arguments; 13 | }; 14 | 15 | G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) 16 | 17 | // Implements GApplication::activate. 18 | static void my_application_activate(GApplication* application) { 19 | MyApplication* self = MY_APPLICATION(application); 20 | GtkWindow* window = 21 | GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); 22 | 23 | // Use a header bar when running in GNOME as this is the common style used 24 | // by applications and is the setup most users will be using (e.g. Ubuntu 25 | // desktop). 26 | // If running on X and not using GNOME then just use a traditional title bar 27 | // in case the window manager does more exotic layout, e.g. tiling. 28 | // If running on Wayland assume the header bar will work (may need changing 29 | // if future cases occur). 30 | gboolean use_header_bar = TRUE; 31 | #ifdef GDK_WINDOWING_X11 32 | GdkScreen* screen = gtk_window_get_screen(window); 33 | if (GDK_IS_X11_SCREEN(screen)) { 34 | const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); 35 | if (g_strcmp0(wm_name, "GNOME Shell") != 0) { 36 | use_header_bar = FALSE; 37 | } 38 | } 39 | #endif 40 | if (use_header_bar) { 41 | GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); 42 | gtk_widget_show(GTK_WIDGET(header_bar)); 43 | gtk_header_bar_set_title(header_bar, "qr"); 44 | gtk_header_bar_set_show_close_button(header_bar, TRUE); 45 | gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); 46 | } else { 47 | gtk_window_set_title(window, "qr"); 48 | } 49 | 50 | gtk_window_set_default_size(window, 650, 650); 51 | //Removed for handy_window 52 | //gtk_widget_show(GTK_WIDGET(window)); 53 | 54 | g_autoptr(FlDartProject) project = fl_dart_project_new(); 55 | fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); 56 | 57 | FlView* view = fl_view_new(project); 58 | //Removed for handy_window 59 | //gtk_widget_show(GTK_WIDGET(view)); 60 | gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); 61 | 62 | fl_register_plugins(FL_PLUGIN_REGISTRY(view)); 63 | 64 | //Added for handy_window 65 | gtk_widget_show(GTK_WIDGET(window)); 66 | gtk_widget_show(GTK_WIDGET(view)); 67 | //--end 68 | gtk_widget_grab_focus(GTK_WIDGET(view)); 69 | } 70 | 71 | // Implements GApplication::local_command_line. 72 | static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) { 73 | MyApplication* self = MY_APPLICATION(application); 74 | // Strip out the first argument as it is the binary name. 75 | self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); 76 | 77 | g_autoptr(GError) error = nullptr; 78 | if (!g_application_register(application, nullptr, &error)) { 79 | g_warning("Failed to register: %s", error->message); 80 | *exit_status = 1; 81 | return TRUE; 82 | } 83 | 84 | g_application_activate(application); 85 | *exit_status = 0; 86 | 87 | return TRUE; 88 | } 89 | 90 | // Implements GApplication::startup. 91 | static void my_application_startup(GApplication* application) { 92 | //MyApplication* self = MY_APPLICATION(object); 93 | 94 | // Perform any actions required at application startup. 95 | 96 | G_APPLICATION_CLASS(my_application_parent_class)->startup(application); 97 | } 98 | 99 | // Implements GApplication::shutdown. 100 | static void my_application_shutdown(GApplication* application) { 101 | //MyApplication* self = MY_APPLICATION(object); 102 | 103 | // Perform any actions required at application shutdown. 104 | 105 | G_APPLICATION_CLASS(my_application_parent_class)->shutdown(application); 106 | } 107 | 108 | // Implements GObject::dispose. 109 | static void my_application_dispose(GObject* object) { 110 | MyApplication* self = MY_APPLICATION(object); 111 | g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); 112 | G_OBJECT_CLASS(my_application_parent_class)->dispose(object); 113 | } 114 | 115 | static void my_application_class_init(MyApplicationClass* klass) { 116 | G_APPLICATION_CLASS(klass)->activate = my_application_activate; 117 | G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; 118 | G_APPLICATION_CLASS(klass)->startup = my_application_startup; 119 | G_APPLICATION_CLASS(klass)->shutdown = my_application_shutdown; 120 | G_OBJECT_CLASS(klass)->dispose = my_application_dispose; 121 | } 122 | 123 | static void my_application_init(MyApplication* self) {} 124 | 125 | MyApplication* my_application_new() { 126 | return MY_APPLICATION(g_object_new(my_application_get_type(), 127 | "application-id", APPLICATION_ID, 128 | "flags", G_APPLICATION_NON_UNIQUE, 129 | nullptr)); 130 | } 131 | -------------------------------------------------------------------------------- /linux/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Project-level configuration. 2 | cmake_minimum_required(VERSION 3.10) 3 | project(runner LANGUAGES CXX) 4 | 5 | # The name of the executable created for the application. Change this to change 6 | # the on-disk name of your application. 7 | set(BINARY_NAME "qr") 8 | # The unique GTK application identifier for this application. See: 9 | # https://wiki.gnome.org/HowDoI/ChooseApplicationID 10 | set(APPLICATION_ID "dev.lasheen.qr") 11 | 12 | # Explicitly opt in to modern CMake behaviors to avoid warnings with recent 13 | # versions of CMake. 14 | cmake_policy(SET CMP0063 NEW) 15 | 16 | # Load bundled libraries from the lib/ directory relative to the binary. 17 | set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") 18 | 19 | # Root filesystem for cross-building. 20 | if(FLUTTER_TARGET_PLATFORM_SYSROOT) 21 | set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) 22 | set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) 23 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 24 | set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) 25 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 26 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 27 | endif() 28 | 29 | # Define build configuration options. 30 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 31 | set(CMAKE_BUILD_TYPE "Debug" CACHE 32 | STRING "Flutter build mode" FORCE) 33 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS 34 | "Debug" "Profile" "Release") 35 | endif() 36 | 37 | # Compilation settings that should be applied to most targets. 38 | # 39 | # Be cautious about adding new options here, as plugins use this function by 40 | # default. In most cases, you should add new options to specific targets instead 41 | # of modifying this function. 42 | function(APPLY_STANDARD_SETTINGS TARGET) 43 | target_compile_features(${TARGET} PUBLIC cxx_std_14) 44 | target_compile_options(${TARGET} PRIVATE -Wall -Werror) 45 | target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") 46 | target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") 47 | endfunction() 48 | 49 | # Flutter library and tool build rules. 50 | set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") 51 | add_subdirectory(${FLUTTER_MANAGED_DIR}) 52 | 53 | # System-level dependencies. 54 | find_package(PkgConfig REQUIRED) 55 | pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) 56 | 57 | add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") 58 | 59 | # Define the application target. To change its name, change BINARY_NAME above, 60 | # not the value here, or `flutter run` will no longer work. 61 | # 62 | # Any new source files that you add to the application should be added here. 63 | add_executable(${BINARY_NAME} 64 | "main.cc" 65 | "my_application.cc" 66 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 67 | ) 68 | 69 | # Apply the standard set of build settings. This can be removed for applications 70 | # that need different build settings. 71 | apply_standard_settings(${BINARY_NAME}) 72 | 73 | # Add dependency libraries. Add any application-specific dependencies here. 74 | target_link_libraries(${BINARY_NAME} PRIVATE flutter) 75 | target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) 76 | 77 | # Run the Flutter tool portions of the build. This must not be removed. 78 | add_dependencies(${BINARY_NAME} flutter_assemble) 79 | 80 | # Only the install-generated bundle's copy of the executable will launch 81 | # correctly, since the resources must in the right relative locations. To avoid 82 | # people trying to run the unbundled copy, put it in a subdirectory instead of 83 | # the default top-level location. 84 | set_target_properties(${BINARY_NAME} 85 | PROPERTIES 86 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" 87 | ) 88 | 89 | 90 | # Generated plugin build rules, which manage building the plugins and adding 91 | # them to the application. 92 | include(flutter/generated_plugins.cmake) 93 | 94 | 95 | # === Installation === 96 | # By default, "installing" just makes a relocatable bundle in the build 97 | # directory. 98 | set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") 99 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 100 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) 101 | endif() 102 | 103 | # Start with a clean build bundle directory every time. 104 | install(CODE " 105 | file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") 106 | " COMPONENT Runtime) 107 | 108 | set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") 109 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") 110 | 111 | install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" 112 | COMPONENT Runtime) 113 | 114 | install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 115 | COMPONENT Runtime) 116 | 117 | install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 118 | COMPONENT Runtime) 119 | 120 | foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) 121 | install(FILES "${bundled_library}" 122 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 123 | COMPONENT Runtime) 124 | endforeach(bundled_library) 125 | 126 | # Copy the native assets provided by the build.dart from all packages. 127 | set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/linux/") 128 | install(DIRECTORY "${NATIVE_ASSETS_DIR}" 129 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 130 | COMPONENT Runtime) 131 | 132 | # Fully re-copy the assets directory on each build to avoid having stale files 133 | # from a previous install. 134 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets") 135 | install(CODE " 136 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") 137 | " COMPONENT Runtime) 138 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" 139 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) 140 | 141 | # Install the AOT library on non-Debug builds only. 142 | if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") 143 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 144 | COMPONENT Runtime) 145 | endif() 146 | -------------------------------------------------------------------------------- /lib/screens/main_screen.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | import 'dart:ui'; 3 | 4 | import 'package:flutter/material.dart'; 5 | import 'package:file_selector/file_selector.dart'; 6 | import 'package:lasheen_qr/screens/generator_screen.dart'; 7 | import 'package:package_info_plus/package_info_plus.dart'; 8 | import 'package:screenshotx/screenshotx.dart'; 9 | import 'package:shadcn_ui/shadcn_ui.dart'; 10 | import 'package:url_launcher/url_launcher_string.dart'; 11 | import 'package:yaru/yaru.dart'; 12 | import 'package:zxing2/qrcode.dart'; 13 | import 'package:image/image.dart' as img; 14 | 15 | import 'scanner_screen.dart'; 16 | 17 | class MainScreen extends StatefulWidget { 18 | const MainScreen({super.key}); 19 | 20 | @override 21 | State createState() => _MainScreenState(); 22 | } 23 | 24 | class _MainScreenState extends State { 25 | @override 26 | Widget build(BuildContext context) { 27 | return Scaffold( 28 | appBar: YaruWindowTitleBar( 29 | leading: YaruIconButton( 30 | icon: const Icon(Icons.help_outline_rounded), 31 | onPressed: () async { 32 | PackageInfo packageInfo = await PackageInfo.fromPlatform(); 33 | Future(() { 34 | showAboutDialog( 35 | context: context, 36 | applicationIcon: Image.asset( 37 | 'assets/icon.png', 38 | width: 50, 39 | height: 50, 40 | ), 41 | applicationName: 'QR Scanner', 42 | applicationVersion: 43 | '${packageInfo.version}+${packageInfo.buildNumber}', 44 | children: [ 45 | const Text( 46 | 'A QR Code Scanner and Generator app built with Flutter.', 47 | ), 48 | const SizedBox(height: 35), 49 | Align( 50 | alignment: Alignment.centerLeft, 51 | child: MouseRegion( 52 | cursor: SystemMouseCursors.click, 53 | child: GestureDetector( 54 | onTap: () => launchUrlString('https://lasheen.dev'), 55 | child: Image.asset( 56 | 'assets/madebylasheen.png', 57 | width: 250, 58 | ), 59 | ), 60 | ), 61 | ), 62 | ], 63 | ); 64 | }); 65 | }, 66 | ), 67 | title: const Text('QR Scanner'), 68 | ), 69 | body: Center( 70 | child: Table( 71 | defaultColumnWidth: const FixedColumnWidth(225), 72 | columnWidths: const { 73 | 0: FixedColumnWidth(225), 74 | 1: FixedColumnWidth(35), 75 | 2: FixedColumnWidth(225), 76 | }, 77 | children: [ 78 | TableRow(children: [ 79 | Text( 80 | 'SCAN', 81 | style: Theme.of(context).textTheme.displayLarge, 82 | ), 83 | const SizedBox.shrink(), 84 | Text( 85 | 'CREATE', 86 | style: Theme.of(context).textTheme.displayLarge, 87 | ), 88 | ]), 89 | const TableRow( 90 | children: [ 91 | ScanSection(), 92 | SizedBox.shrink(), 93 | GeneratorSection(), 94 | ], 95 | ), 96 | ], 97 | ), 98 | )); 99 | } 100 | } 101 | 102 | class ScanSection extends StatelessWidget { 103 | const ScanSection({super.key}); 104 | 105 | @override 106 | Widget build(BuildContext context) { 107 | return Column( 108 | mainAxisAlignment: MainAxisAlignment.center, 109 | crossAxisAlignment: CrossAxisAlignment.start, 110 | children: [ 111 | const SizedBox( 112 | height: 16, 113 | ), 114 | ShadButton( 115 | onPressed: () async { 116 | const XTypeGroup typeGroup = XTypeGroup( 117 | label: 'images', 118 | extensions: ['jpg', 'jpeg', 'png'], 119 | ); 120 | XFile? file = 121 | await openFile(acceptedTypeGroups: [typeGroup]); 122 | 123 | if (file == null) { 124 | // Operation was canceled by the user. 125 | return; 126 | } 127 | final imageBytes = await file.readAsBytes(); 128 | 129 | Result? result = scan(imageBytes); 130 | if (result == null) { 131 | Future(() { 132 | ScaffoldMessenger.of(context).showSnackBar( 133 | const SnackBar( 134 | content: Text('No QR Code found in the image.'), 135 | ), 136 | ); 137 | }); 138 | } else { 139 | Future( 140 | () { 141 | Navigator.push( 142 | context, 143 | MaterialPageRoute( 144 | builder: (context) => ScannerScreen( 145 | result: result, 146 | ), 147 | ), 148 | ); 149 | }, 150 | ); 151 | } 152 | }, 153 | icon: const Icon(Icons.file_copy_rounded), 154 | child: const Text('Pick from files'), 155 | ), 156 | const SizedBox(height: 10), 157 | ShadButton( 158 | onPressed: () async { 159 | final screenshotX = ScreenshotX(); 160 | var image = await screenshotX.captureFullScreen(); 161 | if (image != null) { 162 | final bytes = await image.toByteData(format: ImageByteFormat.png); 163 | final imageBytes = Uint8List.view(bytes!.buffer); 164 | 165 | Result? result = scan(imageBytes); 166 | if (result == null) { 167 | Future(() { 168 | ScaffoldMessenger.of(context).showSnackBar( 169 | const SnackBar( 170 | content: Text('No QR Code found in the screenshot.'), 171 | ), 172 | ); 173 | }); 174 | } else { 175 | Future( 176 | () { 177 | Navigator.push( 178 | context, 179 | MaterialPageRoute( 180 | builder: (context) => ScannerScreen( 181 | result: result, 182 | ), 183 | ), 184 | ); 185 | }, 186 | ); 187 | } 188 | } 189 | }, 190 | icon: const Icon(Icons.screenshot_monitor_rounded), 191 | child: const Text('Take a screenshot'), 192 | ), 193 | const SizedBox(height: 10), 194 | ], 195 | ); 196 | } 197 | } 198 | 199 | class GeneratorSection extends StatelessWidget { 200 | const GeneratorSection({super.key}); 201 | 202 | @override 203 | Widget build(BuildContext context) { 204 | return Column( 205 | mainAxisAlignment: MainAxisAlignment.center, 206 | crossAxisAlignment: CrossAxisAlignment.start, 207 | children: [ 208 | const SizedBox( 209 | height: 16, 210 | ), 211 | ShadButton( 212 | onPressed: () => Navigator.push( 213 | context, 214 | MaterialPageRoute(builder: (context) => const GeneratorScreen()), 215 | ), 216 | icon: const Icon(Icons.qr_code_rounded), 217 | child: const Text('Generate QR Code'), 218 | ), 219 | ], 220 | ); 221 | } 222 | } 223 | 224 | Result? scan(Uint8List bytes) { 225 | img.Image image = img.decodeImage(bytes)!; 226 | LuminanceSource source = RGBLuminanceSource( 227 | image.width, 228 | image.height, 229 | image 230 | .convert(numChannels: 4) 231 | .getBytes(order: img.ChannelOrder.abgr) 232 | .buffer 233 | .asInt32List()); 234 | var bitmap = BinaryBitmap(GlobalHistogramBinarizer(source)); 235 | 236 | var reader = QRCodeReader(); 237 | try { 238 | return reader.decode(bitmap); 239 | } catch (e) { 240 | return null; 241 | } 242 | } 243 | -------------------------------------------------------------------------------- /lib/screens/generator_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:file_selector/file_selector.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_spinbox/flutter_spinbox.dart'; 4 | import 'package:pretty_qr_code/pretty_qr_code.dart'; 5 | import 'package:screenshot/screenshot.dart'; 6 | import 'package:shadcn_ui/shadcn_ui.dart'; 7 | import 'package:yaru/yaru.dart'; 8 | import 'dart:io'; 9 | 10 | class GeneratorScreen extends StatefulWidget { 11 | const GeneratorScreen({super.key, this.initialContent = 'lasheen'}); 12 | final String initialContent; 13 | 14 | @override 15 | State createState() => _GeneratorScreenState(); 16 | } 17 | 18 | class _GeneratorScreenState extends State { 19 | //GlobalKey globalKey = GlobalKey(); 20 | 21 | String? content; 22 | int? version = 1; 23 | //bool calculateVersionAutomatically = true; 24 | bool roundEdges = true; 25 | int errorCorrectLevel = QrErrorCorrectLevel.M; 26 | 27 | ScreenshotController screenshotController = ScreenshotController(); 28 | @override 29 | Widget build(BuildContext context) { 30 | content ??= widget.initialContent; 31 | return Scaffold( 32 | appBar: const YaruWindowTitleBar( 33 | title: Text('QR code generator'), 34 | leading: YaruBackButton(), 35 | ), 36 | body: SizedBox( 37 | width: double.infinity, 38 | child: SingleChildScrollView( 39 | child: Center( 40 | child: SizedBox( 41 | width: 350, 42 | child: Column( 43 | mainAxisSize: MainAxisSize.max, 44 | mainAxisAlignment: MainAxisAlignment.center, 45 | children: [ 46 | const SizedBox( 47 | height: 30, 48 | ), 49 | ShadCard( 50 | padding: const EdgeInsets.all(25), 51 | child: PrettyQr( 52 | data: content!, 53 | elementColor: Theme.of(context).colorScheme.onSurface, 54 | size: 300, 55 | roundEdges: roundEdges, 56 | typeNumber: version, 57 | errorCorrectLevel: errorCorrectLevel, 58 | ), 59 | ), 60 | const SizedBox( 61 | height: 10, 62 | ), 63 | ShadCard( 64 | padding: const EdgeInsets.all(25), 65 | child: TextFormField( 66 | initialValue: widget.initialContent, 67 | maxLength: calculateMaxInput( 68 | version: version, 69 | errorCorrectLevel: errorCorrectLevel, 70 | ), 71 | onChanged: (value) { 72 | setState(() { 73 | content = value; 74 | }); 75 | }, 76 | style: const TextStyle( 77 | fontSize: 20, 78 | fontWeight: FontWeight.bold, 79 | ), 80 | decoration: const InputDecoration( 81 | border: InputBorder.none, 82 | labelText: 'Content', 83 | hintText: 'Enter a link to generate a QR code', 84 | ), 85 | ), 86 | ), 87 | const SizedBox( 88 | height: 10, 89 | ), 90 | ShadCard( 91 | padding: const EdgeInsets.all(25), 92 | child: Column( 93 | crossAxisAlignment: CrossAxisAlignment.stretch, 94 | children: [ 95 | Align( 96 | alignment: Alignment.centerLeft, 97 | child: Text( 98 | 'QR code settings', 99 | style: Theme.of(context).textTheme.displaySmall, 100 | ), 101 | ), 102 | SizedBox( 103 | height: 25, 104 | ), 105 | ShadSwitch( 106 | label: const Text('Rounded corners'), 107 | value: roundEdges, 108 | onChanged: (value) { 109 | setState(() { 110 | roundEdges = value; 111 | }); 112 | }, 113 | ), 114 | ShadSwitch( 115 | label: const Text('Calculate version automatically'), 116 | value: version == null, 117 | onChanged: (value) { 118 | if (!value) { 119 | for (var i = 1; i < 40; i++) { 120 | if (calculateMaxInput( 121 | version: i, 122 | errorCorrectLevel: errorCorrectLevel, 123 | ) > 124 | content!.length) { 125 | setState(() { 126 | version = i; 127 | //calculateVersionAutomatically = false; 128 | }); 129 | break; 130 | } 131 | } 132 | } else { 133 | setState(() { 134 | version = null; 135 | }); 136 | } 137 | }, 138 | ), 139 | ListTile( 140 | contentPadding: EdgeInsets.zero, 141 | enabled: version != null, 142 | title: const Text('Version'), 143 | subtitle: const Text( 144 | 'The higher the number the denser the QR code'), 145 | trailing: SizedBox( 146 | width: 100, 147 | height: 50, 148 | child: SpinBox( 149 | enabled: version != null, 150 | spacing: 0, 151 | enableInteractiveSelection: false, 152 | showCursor: false, 153 | min: 1, 154 | max: 40, 155 | value: (version ?? 0).toDouble(), 156 | direction: Axis.horizontal, 157 | canChange: (value) { 158 | if (value < 1) return false; 159 | int maxInputLength = calculateMaxInput( 160 | version: value.toInt(), 161 | errorCorrectLevel: errorCorrectLevel, 162 | ); 163 | if (maxInputLength >= content!.length) { 164 | setState(() { 165 | version = value.toInt(); 166 | }); 167 | return true; 168 | } 169 | return false; 170 | }, 171 | ), 172 | ), 173 | ), 174 | ListTile( 175 | contentPadding: EdgeInsets.zero, 176 | title: const Text('Error correction level'), 177 | subtitle: const Text('L is the ' 178 | 'lowest and H is the highest.'), 179 | trailing: DecoratedBox( 180 | decoration: BoxDecoration( 181 | borderRadius: BorderRadius.circular(5), 182 | border: Border.all( 183 | color: Theme.of(context).colorScheme.onSurface, 184 | ), 185 | ), 186 | child: Padding( 187 | padding: const EdgeInsets.all(8.0), 188 | child: PopupMenuButton( 189 | itemBuilder: (c) => [ 190 | for (final level 191 | in QrErrorCorrectLevel.levels) 192 | PopupMenuItem( 193 | enabled: calculateMaxInput( 194 | version: version, 195 | errorCorrectLevel: level, 196 | ) >= 197 | content!.length, 198 | child: Text(levelsNames[level]), 199 | onTap: () { 200 | setState(() { 201 | errorCorrectLevel = level; 202 | }); 203 | }, 204 | ), 205 | ], 206 | child: Text(levelsNames[errorCorrectLevel]), 207 | ), 208 | ), 209 | ), 210 | ), 211 | ], 212 | ), 213 | ), 214 | const SizedBox( 215 | height: 10, 216 | ), 217 | ShadButton( 218 | onPressed: () async { 219 | screenshotController 220 | .captureFromWidget( 221 | ColoredBox( 222 | color: Colors.white, 223 | child: Padding( 224 | padding: const EdgeInsets.all(8.0), 225 | child: PrettyQr( 226 | data: content!, 227 | // elementColor: Colors.black, 228 | size: 300, 229 | roundEdges: roundEdges, 230 | typeNumber: version, 231 | errorCorrectLevel: errorCorrectLevel, 232 | ), 233 | ), 234 | ), 235 | ) 236 | .then((imageBytes) async { 237 | String? outputFile; 238 | 239 | final location = await getSaveLocation( 240 | suggestedName: 'qrcode.png', 241 | acceptedTypeGroups: [ 242 | const XTypeGroup( 243 | label: 'PNGs', 244 | extensions: ['png'], 245 | ) 246 | ]); 247 | outputFile = location?.path; 248 | 249 | if (outputFile != null) { 250 | File image = File(outputFile); 251 | image.writeAsBytesSync(imageBytes); 252 | 253 | ScaffoldMessenger.of(context).showSnackBar( 254 | SnackBar( 255 | content: Text('Saved to $outputFile'), 256 | ), 257 | ); 258 | } else { 259 | ScaffoldMessenger.of(context).showSnackBar( 260 | const SnackBar( 261 | content: Text('Qr code not saved'), 262 | ), 263 | ); 264 | } 265 | }).catchError((onError) { 266 | ScaffoldMessenger.of(context).showSnackBar( 267 | SnackBar( 268 | backgroundColor: Colors.red, 269 | content: Text('Error: $onError'), 270 | ), 271 | ); 272 | }); 273 | }, 274 | icon: const Icon(Icons.save), 275 | child: const Text('Save QR code'), 276 | ), 277 | const SizedBox( 278 | height: 10, 279 | ), 280 | ], 281 | ), 282 | ), 283 | ), 284 | ), 285 | ), 286 | ); 287 | } 288 | } 289 | 290 | int calculateMaxInput({ 291 | required int errorCorrectLevel, 292 | required int? version, 293 | }) { 294 | version ??= 40; 295 | return capacities[(version * 4) + levels[errorCorrectLevel]]; 296 | } 297 | 298 | const List levels = [1, 0, 3, 2]; 299 | List levelsNames = ['M', 'L', 'H', 'Q']; 300 | const List capacities = [ 301 | 0, 302 | 0, 303 | 0, 304 | 0, 305 | 17, 306 | 14, 307 | 11, 308 | 7, 309 | 32, 310 | 26, 311 | 20, 312 | 14, 313 | 53, 314 | 42, 315 | 32, 316 | 24, 317 | 78, 318 | 62, 319 | 46, 320 | 34, 321 | 106, 322 | 84, 323 | 60, 324 | 44, 325 | 134, 326 | 106, 327 | 74, 328 | 58, 329 | 154, 330 | 122, 331 | 86, 332 | 64, 333 | 192, 334 | 152, 335 | 108, 336 | 84, 337 | 230, 338 | 180, 339 | 130, 340 | 98, 341 | 271, 342 | 213, 343 | 151, 344 | 119, 345 | 321, 346 | 251, 347 | 177, 348 | 137, 349 | 367, 350 | 287, 351 | 203, 352 | 155, 353 | 425, 354 | 331, 355 | 241, 356 | 177, 357 | 458, 358 | 362, 359 | 258, 360 | 194, 361 | 520, 362 | 412, 363 | 292, 364 | 220, 365 | 586, 366 | 450, 367 | 322, 368 | 250, 369 | 644, 370 | 504, 371 | 364, 372 | 280, 373 | 718, 374 | 560, 375 | 394, 376 | 310, 377 | 792, 378 | 624, 379 | 442, 380 | 338, 381 | 858, 382 | 666, 383 | 482, 384 | 382, 385 | 929, 386 | 711, 387 | 509, 388 | 403, 389 | 1003, 390 | 779, 391 | 565, 392 | 439, 393 | 1091, 394 | 857, 395 | 611, 396 | 461, 397 | 1171, 398 | 911, 399 | 661, 400 | 511, 401 | 1273, 402 | 997, 403 | 715, 404 | 535, 405 | 1367, 406 | 1059, 407 | 751, 408 | 593, 409 | 1465, 410 | 1125, 411 | 805, 412 | 625, 413 | 1528, 414 | 1190, 415 | 868, 416 | 658, 417 | 1628, 418 | 1264, 419 | 908, 420 | 698, 421 | 1732, 422 | 1370, 423 | 982, 424 | 742, 425 | 1840, 426 | 1452, 427 | 1030, 428 | 790, 429 | 1952, 430 | 1538, 431 | 1112, 432 | 842, 433 | 2068, 434 | 1628, 435 | 1168, 436 | 898, 437 | 2188, 438 | 1722, 439 | 1228, 440 | 958, 441 | 2303, 442 | 1809, 443 | 1283, 444 | 983, 445 | 2431, 446 | 1911, 447 | 1351, 448 | 1051, 449 | 2563, 450 | 1989, 451 | 1423, 452 | 1093, 453 | 2699, 454 | 2099, 455 | 1499, 456 | 1139, 457 | 2809, 458 | 2213, 459 | 1579, 460 | 1219, 461 | 2953, 462 | 2331, 463 | 1663, 464 | 1273 465 | ]; 466 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | animated_vector: 5 | dependency: transitive 6 | description: 7 | name: animated_vector 8 | sha256: e15c6596549ca6e2e7491c11fbe168a1dead87475a828a4bc81cf104feca0432 9 | url: "https://pub.dev" 10 | source: hosted 11 | version: "0.2.0" 12 | animated_vector_annotations: 13 | dependency: transitive 14 | description: 15 | name: animated_vector_annotations 16 | sha256: baa6b4ed98407220f2c9634f7da3cfa5eedb46798e090466f441e666e2f7c8c0 17 | url: "https://pub.dev" 18 | source: hosted 19 | version: "0.2.0" 20 | archive: 21 | dependency: transitive 22 | description: 23 | name: archive 24 | sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d 25 | url: "https://pub.dev" 26 | source: hosted 27 | version: "3.6.1" 28 | args: 29 | dependency: transitive 30 | description: 31 | name: args 32 | sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" 33 | url: "https://pub.dev" 34 | source: hosted 35 | version: "2.5.0" 36 | async: 37 | dependency: transitive 38 | description: 39 | name: async 40 | sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" 41 | url: "https://pub.dev" 42 | source: hosted 43 | version: "2.11.0" 44 | boolean_selector: 45 | dependency: transitive 46 | description: 47 | name: boolean_selector 48 | sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" 49 | url: "https://pub.dev" 50 | source: hosted 51 | version: "2.1.1" 52 | characters: 53 | dependency: transitive 54 | description: 55 | name: characters 56 | sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" 57 | url: "https://pub.dev" 58 | source: hosted 59 | version: "1.3.0" 60 | charcode: 61 | dependency: transitive 62 | description: 63 | name: charcode 64 | sha256: fb98c0f6d12c920a02ee2d998da788bca066ca5f148492b7085ee23372b12306 65 | url: "https://pub.dev" 66 | source: hosted 67 | version: "1.3.1" 68 | clock: 69 | dependency: transitive 70 | description: 71 | name: clock 72 | sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf 73 | url: "https://pub.dev" 74 | source: hosted 75 | version: "1.1.1" 76 | collection: 77 | dependency: transitive 78 | description: 79 | name: collection 80 | sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a 81 | url: "https://pub.dev" 82 | source: hosted 83 | version: "1.18.0" 84 | cross_file: 85 | dependency: transitive 86 | description: 87 | name: cross_file 88 | sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670" 89 | url: "https://pub.dev" 90 | source: hosted 91 | version: "0.3.4+2" 92 | crypto: 93 | dependency: transitive 94 | description: 95 | name: crypto 96 | sha256: ec30d999af904f33454ba22ed9a86162b35e52b44ac4807d1d93c288041d7d27 97 | url: "https://pub.dev" 98 | source: hosted 99 | version: "3.0.5" 100 | cupertino_icons: 101 | dependency: transitive 102 | description: 103 | name: cupertino_icons 104 | sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 105 | url: "https://pub.dev" 106 | source: hosted 107 | version: "1.0.8" 108 | dbus: 109 | dependency: transitive 110 | description: 111 | name: dbus 112 | sha256: "365c771ac3b0e58845f39ec6deebc76e3276aa9922b0cc60840712094d9047ac" 113 | url: "https://pub.dev" 114 | source: hosted 115 | version: "0.7.10" 116 | fake_async: 117 | dependency: transitive 118 | description: 119 | name: fake_async 120 | sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" 121 | url: "https://pub.dev" 122 | source: hosted 123 | version: "1.3.1" 124 | ffi: 125 | dependency: transitive 126 | description: 127 | name: ffi 128 | sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6" 129 | url: "https://pub.dev" 130 | source: hosted 131 | version: "2.1.3" 132 | file_selector: 133 | dependency: "direct main" 134 | description: 135 | name: file_selector 136 | sha256: "5019692b593455127794d5718304ff1ae15447dea286cdda9f0db2a796a1b828" 137 | url: "https://pub.dev" 138 | source: hosted 139 | version: "1.0.3" 140 | file_selector_android: 141 | dependency: transitive 142 | description: 143 | name: file_selector_android 144 | sha256: "00aafa9ae05a8663d0b4f17abd2a02316911ca0f46f9b9dacb9578b324d99590" 145 | url: "https://pub.dev" 146 | source: hosted 147 | version: "0.5.1+9" 148 | file_selector_ios: 149 | dependency: transitive 150 | description: 151 | name: file_selector_ios 152 | sha256: "94b98ad950b8d40d96fee8fa88640c2e4bd8afcdd4817993bd04e20310f45420" 153 | url: "https://pub.dev" 154 | source: hosted 155 | version: "0.5.3+1" 156 | file_selector_linux: 157 | dependency: transitive 158 | description: 159 | name: file_selector_linux 160 | sha256: "712ce7fab537ba532c8febdb1a8f167b32441e74acd68c3ccb2e36dcb52c4ab2" 161 | url: "https://pub.dev" 162 | source: hosted 163 | version: "0.9.3" 164 | file_selector_macos: 165 | dependency: transitive 166 | description: 167 | name: file_selector_macos 168 | sha256: "271ab9986df0c135d45c3cdb6bd0faa5db6f4976d3e4b437cf7d0f258d941bfc" 169 | url: "https://pub.dev" 170 | source: hosted 171 | version: "0.9.4+2" 172 | file_selector_platform_interface: 173 | dependency: transitive 174 | description: 175 | name: file_selector_platform_interface 176 | sha256: a3994c26f10378a039faa11de174d7b78eb8f79e4dd0af2a451410c1a5c3f66b 177 | url: "https://pub.dev" 178 | source: hosted 179 | version: "2.6.2" 180 | file_selector_web: 181 | dependency: transitive 182 | description: 183 | name: file_selector_web 184 | sha256: c4c0ea4224d97a60a7067eca0c8fd419e708ff830e0c83b11a48faf566cec3e7 185 | url: "https://pub.dev" 186 | source: hosted 187 | version: "0.9.4+2" 188 | file_selector_windows: 189 | dependency: transitive 190 | description: 191 | name: file_selector_windows 192 | sha256: "8f5d2f6590d51ecd9179ba39c64f722edc15226cc93dcc8698466ad36a4a85a4" 193 | url: "https://pub.dev" 194 | source: hosted 195 | version: "0.9.3+3" 196 | fixnum: 197 | dependency: transitive 198 | description: 199 | name: fixnum 200 | sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" 201 | url: "https://pub.dev" 202 | source: hosted 203 | version: "1.1.0" 204 | flutter: 205 | dependency: "direct main" 206 | description: flutter 207 | source: sdk 208 | version: "0.0.0" 209 | flutter_animate: 210 | dependency: transitive 211 | description: 212 | name: flutter_animate 213 | sha256: "7c8a6594a9252dad30cc2ef16e33270b6248c4dedc3b3d06c86c4f3f4dc05ae5" 214 | url: "https://pub.dev" 215 | source: hosted 216 | version: "4.5.0" 217 | flutter_lints: 218 | dependency: "direct dev" 219 | description: 220 | name: flutter_lints 221 | sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 222 | url: "https://pub.dev" 223 | source: hosted 224 | version: "2.0.3" 225 | flutter_localizations: 226 | dependency: transitive 227 | description: flutter 228 | source: sdk 229 | version: "0.0.0" 230 | flutter_shaders: 231 | dependency: transitive 232 | description: 233 | name: flutter_shaders 234 | sha256: "34794acadd8275d971e02df03afee3dee0f98dbfb8c4837082ad0034f612a3e2" 235 | url: "https://pub.dev" 236 | source: hosted 237 | version: "0.1.3" 238 | flutter_spinbox: 239 | dependency: "direct main" 240 | description: 241 | name: flutter_spinbox 242 | sha256: "38d8c1a3a39f0fa72823d4470785f5e165f2deb53531ca7803b54ba45e4dbd46" 243 | url: "https://pub.dev" 244 | source: hosted 245 | version: "0.13.1" 246 | flutter_svg: 247 | dependency: transitive 248 | description: 249 | name: flutter_svg 250 | sha256: "7b4ca6cf3304575fe9c8ec64813c8d02ee41d2afe60bcfe0678bcb5375d596a2" 251 | url: "https://pub.dev" 252 | source: hosted 253 | version: "2.0.10+1" 254 | flutter_test: 255 | dependency: "direct dev" 256 | description: flutter 257 | source: sdk 258 | version: "0.0.0" 259 | flutter_web_plugins: 260 | dependency: transitive 261 | description: flutter 262 | source: sdk 263 | version: "0.0.0" 264 | gsettings: 265 | dependency: transitive 266 | description: 267 | name: gsettings 268 | sha256: "1b0ce661f5436d2db1e51f3c4295a49849f03d304003a7ba177d01e3a858249c" 269 | url: "https://pub.dev" 270 | source: hosted 271 | version: "0.2.8" 272 | gtk: 273 | dependency: transitive 274 | description: 275 | name: gtk 276 | sha256: e8ce9ca4b1df106e4d72dad201d345ea1a036cc12c360f1a7d5a758f78ffa42c 277 | url: "https://pub.dev" 278 | source: hosted 279 | version: "2.1.0" 280 | handy_window: 281 | dependency: "direct main" 282 | description: 283 | name: handy_window 284 | sha256: "56b813e58a68b0ee2ab22051400b8b1f1b5cfe88b8cd32288623defb3926245a" 285 | url: "https://pub.dev" 286 | source: hosted 287 | version: "0.4.0" 288 | http: 289 | dependency: transitive 290 | description: 291 | name: http 292 | sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010 293 | url: "https://pub.dev" 294 | source: hosted 295 | version: "1.2.2" 296 | http_parser: 297 | dependency: transitive 298 | description: 299 | name: http_parser 300 | sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" 301 | url: "https://pub.dev" 302 | source: hosted 303 | version: "4.0.2" 304 | image: 305 | dependency: "direct main" 306 | description: 307 | name: image 308 | sha256: "2237616a36c0d69aef7549ab439b833fb7f9fb9fc861af2cc9ac3eedddd69ca8" 309 | url: "https://pub.dev" 310 | source: hosted 311 | version: "4.2.0" 312 | intl: 313 | dependency: transitive 314 | description: 315 | name: intl 316 | sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf 317 | url: "https://pub.dev" 318 | source: hosted 319 | version: "0.19.0" 320 | leak_tracker: 321 | dependency: transitive 322 | description: 323 | name: leak_tracker 324 | sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" 325 | url: "https://pub.dev" 326 | source: hosted 327 | version: "10.0.5" 328 | leak_tracker_flutter_testing: 329 | dependency: transitive 330 | description: 331 | name: leak_tracker_flutter_testing 332 | sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" 333 | url: "https://pub.dev" 334 | source: hosted 335 | version: "3.0.5" 336 | leak_tracker_testing: 337 | dependency: transitive 338 | description: 339 | name: leak_tracker_testing 340 | sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" 341 | url: "https://pub.dev" 342 | source: hosted 343 | version: "3.0.1" 344 | lints: 345 | dependency: transitive 346 | description: 347 | name: lints 348 | sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" 349 | url: "https://pub.dev" 350 | source: hosted 351 | version: "2.1.1" 352 | lucide_icons_flutter: 353 | dependency: transitive 354 | description: 355 | name: lucide_icons_flutter 356 | sha256: "6c612d8593d6dbab2b12f27e765a139863c89bfb23938790224f6294de8b5c0d" 357 | url: "https://pub.dev" 358 | source: hosted 359 | version: "1.2.0" 360 | matcher: 361 | dependency: transitive 362 | description: 363 | name: matcher 364 | sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb 365 | url: "https://pub.dev" 366 | source: hosted 367 | version: "0.12.16+1" 368 | material_color_utilities: 369 | dependency: transitive 370 | description: 371 | name: material_color_utilities 372 | sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec 373 | url: "https://pub.dev" 374 | source: hosted 375 | version: "0.11.1" 376 | meta: 377 | dependency: transitive 378 | description: 379 | name: meta 380 | sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 381 | url: "https://pub.dev" 382 | source: hosted 383 | version: "1.15.0" 384 | package_info_plus: 385 | dependency: "direct main" 386 | description: 387 | name: package_info_plus 388 | sha256: a75164ade98cb7d24cfd0a13c6408927c6b217fa60dee5a7ff5c116a58f28918 389 | url: "https://pub.dev" 390 | source: hosted 391 | version: "8.0.2" 392 | package_info_plus_platform_interface: 393 | dependency: transitive 394 | description: 395 | name: package_info_plus_platform_interface 396 | sha256: ac1f4a4847f1ade8e6a87d1f39f5d7c67490738642e2542f559ec38c37489a66 397 | url: "https://pub.dev" 398 | source: hosted 399 | version: "3.0.1" 400 | path: 401 | dependency: "direct main" 402 | description: 403 | name: path 404 | sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" 405 | url: "https://pub.dev" 406 | source: hosted 407 | version: "1.9.0" 408 | path_parsing: 409 | dependency: transitive 410 | description: 411 | name: path_parsing 412 | sha256: e3e67b1629e6f7e8100b367d3db6ba6af4b1f0bb80f64db18ef1fbabd2fa9ccf 413 | url: "https://pub.dev" 414 | source: hosted 415 | version: "1.0.1" 416 | path_provider: 417 | dependency: "direct main" 418 | description: 419 | name: path_provider 420 | sha256: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378 421 | url: "https://pub.dev" 422 | source: hosted 423 | version: "2.1.4" 424 | path_provider_android: 425 | dependency: transitive 426 | description: 427 | name: path_provider_android 428 | sha256: f7544c346a0742aee1450f9e5c0f5269d7c602b9c95fdbcd9fb8f5b1df13b1cc 429 | url: "https://pub.dev" 430 | source: hosted 431 | version: "2.2.11" 432 | path_provider_foundation: 433 | dependency: transitive 434 | description: 435 | name: path_provider_foundation 436 | sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16 437 | url: "https://pub.dev" 438 | source: hosted 439 | version: "2.4.0" 440 | path_provider_linux: 441 | dependency: transitive 442 | description: 443 | name: path_provider_linux 444 | sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 445 | url: "https://pub.dev" 446 | source: hosted 447 | version: "2.2.1" 448 | path_provider_platform_interface: 449 | dependency: transitive 450 | description: 451 | name: path_provider_platform_interface 452 | sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" 453 | url: "https://pub.dev" 454 | source: hosted 455 | version: "2.1.2" 456 | path_provider_windows: 457 | dependency: transitive 458 | description: 459 | name: path_provider_windows 460 | sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 461 | url: "https://pub.dev" 462 | source: hosted 463 | version: "2.3.0" 464 | petitparser: 465 | dependency: transitive 466 | description: 467 | name: petitparser 468 | sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 469 | url: "https://pub.dev" 470 | source: hosted 471 | version: "6.0.2" 472 | platform: 473 | dependency: transitive 474 | description: 475 | name: platform 476 | sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65" 477 | url: "https://pub.dev" 478 | source: hosted 479 | version: "3.1.5" 480 | platform_linux: 481 | dependency: transitive 482 | description: 483 | name: platform_linux 484 | sha256: "856cfc9871e3ff3df6926991729d24bba9b70d0229ae377fa08b562344baaaa8" 485 | url: "https://pub.dev" 486 | source: hosted 487 | version: "0.1.2" 488 | plugin_platform_interface: 489 | dependency: transitive 490 | description: 491 | name: plugin_platform_interface 492 | sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" 493 | url: "https://pub.dev" 494 | source: hosted 495 | version: "2.1.8" 496 | pretty_qr_code: 497 | dependency: "direct main" 498 | description: 499 | name: pretty_qr_code 500 | sha256: cbdb4af29da1c1fa21dd76f809646c591320ab9e435d3b0eab867492d43607d5 501 | url: "https://pub.dev" 502 | source: hosted 503 | version: "3.3.0" 504 | qr: 505 | dependency: transitive 506 | description: 507 | name: qr 508 | sha256: "5a1d2586170e172b8a8c8470bbbffd5eb0cd38a66c0d77155ea138d3af3a4445" 509 | url: "https://pub.dev" 510 | source: hosted 511 | version: "3.0.2" 512 | screen_retriever: 513 | dependency: transitive 514 | description: 515 | name: screen_retriever 516 | sha256: "6ee02c8a1158e6dae7ca430da79436e3b1c9563c8cf02f524af997c201ac2b90" 517 | url: "https://pub.dev" 518 | source: hosted 519 | version: "0.1.9" 520 | screenshot: 521 | dependency: "direct main" 522 | description: 523 | name: screenshot 524 | sha256: "63817697a7835e6ce82add4228e15d233b74d42975c143ad8cfe07009fab866b" 525 | url: "https://pub.dev" 526 | source: hosted 527 | version: "3.0.0" 528 | screenshotx: 529 | dependency: "direct main" 530 | description: 531 | name: screenshotx 532 | sha256: da7c0d7c1fc511caf2048167dd36d6ab6dc7f36be9a39789cabe7c9a93549096 533 | url: "https://pub.dev" 534 | source: hosted 535 | version: "1.0.0" 536 | screenshotx_linux: 537 | dependency: transitive 538 | description: 539 | name: screenshotx_linux 540 | sha256: "7cc0369c763275d28d95e16f6134d6e2a9fceb1f78e29b7e10c8765faeeea4f5" 541 | url: "https://pub.dev" 542 | source: hosted 543 | version: "1.0.0" 544 | screenshotx_platform_interface: 545 | dependency: transitive 546 | description: 547 | name: screenshotx_platform_interface 548 | sha256: "5b2291bca014729c0108f60e0c2f2a220abbd81452df09394a71bab0f610acbb" 549 | url: "https://pub.dev" 550 | source: hosted 551 | version: "1.0.0" 552 | shadcn_ui: 553 | dependency: "direct main" 554 | description: 555 | name: shadcn_ui 556 | sha256: "9bf69945311043704b05ce353fb929b2e40970b5798ba7c28f40311c9c8371c5" 557 | url: "https://pub.dev" 558 | source: hosted 559 | version: "0.11.0" 560 | sky_engine: 561 | dependency: transitive 562 | description: flutter 563 | source: sdk 564 | version: "0.0.99" 565 | source_span: 566 | dependency: transitive 567 | description: 568 | name: source_span 569 | sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" 570 | url: "https://pub.dev" 571 | source: hosted 572 | version: "1.10.0" 573 | stack_trace: 574 | dependency: transitive 575 | description: 576 | name: stack_trace 577 | sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" 578 | url: "https://pub.dev" 579 | source: hosted 580 | version: "1.11.1" 581 | stream_channel: 582 | dependency: transitive 583 | description: 584 | name: stream_channel 585 | sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 586 | url: "https://pub.dev" 587 | source: hosted 588 | version: "2.1.2" 589 | string_scanner: 590 | dependency: transitive 591 | description: 592 | name: string_scanner 593 | sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" 594 | url: "https://pub.dev" 595 | source: hosted 596 | version: "1.2.0" 597 | term_glyph: 598 | dependency: transitive 599 | description: 600 | name: term_glyph 601 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 602 | url: "https://pub.dev" 603 | source: hosted 604 | version: "1.2.1" 605 | test_api: 606 | dependency: transitive 607 | description: 608 | name: test_api 609 | sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" 610 | url: "https://pub.dev" 611 | source: hosted 612 | version: "0.7.2" 613 | two_dimensional_scrollables: 614 | dependency: transitive 615 | description: 616 | name: two_dimensional_scrollables 617 | sha256: "74ce1f35a8c74370b322049c9d00bf098938661e9f67054eae0f618e6dc0cb62" 618 | url: "https://pub.dev" 619 | source: hosted 620 | version: "0.3.2" 621 | typed_data: 622 | dependency: transitive 623 | description: 624 | name: typed_data 625 | sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c 626 | url: "https://pub.dev" 627 | source: hosted 628 | version: "1.3.2" 629 | url_launcher: 630 | dependency: "direct main" 631 | description: 632 | name: url_launcher 633 | sha256: "21b704ce5fa560ea9f3b525b43601c678728ba46725bab9b01187b4831377ed3" 634 | url: "https://pub.dev" 635 | source: hosted 636 | version: "6.3.0" 637 | url_launcher_android: 638 | dependency: transitive 639 | description: 640 | name: url_launcher_android 641 | sha256: "8fc3bae0b68c02c47c5c86fa8bfa74471d42687b0eded01b78de87872db745e2" 642 | url: "https://pub.dev" 643 | source: hosted 644 | version: "6.3.12" 645 | url_launcher_ios: 646 | dependency: transitive 647 | description: 648 | name: url_launcher_ios 649 | sha256: e43b677296fadce447e987a2f519dcf5f6d1e527dc35d01ffab4fff5b8a7063e 650 | url: "https://pub.dev" 651 | source: hosted 652 | version: "6.3.1" 653 | url_launcher_linux: 654 | dependency: transitive 655 | description: 656 | name: url_launcher_linux 657 | sha256: e2b9622b4007f97f504cd64c0128309dfb978ae66adbe944125ed9e1750f06af 658 | url: "https://pub.dev" 659 | source: hosted 660 | version: "3.2.0" 661 | url_launcher_macos: 662 | dependency: transitive 663 | description: 664 | name: url_launcher_macos 665 | sha256: "769549c999acdb42b8bcfa7c43d72bf79a382ca7441ab18a808e101149daf672" 666 | url: "https://pub.dev" 667 | source: hosted 668 | version: "3.2.1" 669 | url_launcher_platform_interface: 670 | dependency: transitive 671 | description: 672 | name: url_launcher_platform_interface 673 | sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" 674 | url: "https://pub.dev" 675 | source: hosted 676 | version: "2.3.2" 677 | url_launcher_web: 678 | dependency: transitive 679 | description: 680 | name: url_launcher_web 681 | sha256: "772638d3b34c779ede05ba3d38af34657a05ac55b06279ea6edd409e323dca8e" 682 | url: "https://pub.dev" 683 | source: hosted 684 | version: "2.3.3" 685 | url_launcher_windows: 686 | dependency: transitive 687 | description: 688 | name: url_launcher_windows 689 | sha256: "49c10f879746271804767cb45551ec5592cdab00ee105c06dddde1a98f73b185" 690 | url: "https://pub.dev" 691 | source: hosted 692 | version: "3.1.2" 693 | uuid: 694 | dependency: transitive 695 | description: 696 | name: uuid 697 | sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313" 698 | url: "https://pub.dev" 699 | source: hosted 700 | version: "3.0.7" 701 | vector_graphics: 702 | dependency: transitive 703 | description: 704 | name: vector_graphics 705 | sha256: "32c3c684e02f9bc0afb0ae0aa653337a2fe022e8ab064bcd7ffda27a74e288e3" 706 | url: "https://pub.dev" 707 | source: hosted 708 | version: "1.1.11+1" 709 | vector_graphics_codec: 710 | dependency: transitive 711 | description: 712 | name: vector_graphics_codec 713 | sha256: c86987475f162fadff579e7320c7ddda04cd2fdeffbe1129227a85d9ac9e03da 714 | url: "https://pub.dev" 715 | source: hosted 716 | version: "1.1.11+1" 717 | vector_graphics_compiler: 718 | dependency: transitive 719 | description: 720 | name: vector_graphics_compiler 721 | sha256: "12faff3f73b1741a36ca7e31b292ddeb629af819ca9efe9953b70bd63fc8cd81" 722 | url: "https://pub.dev" 723 | source: hosted 724 | version: "1.1.11+1" 725 | vector_math: 726 | dependency: transitive 727 | description: 728 | name: vector_math 729 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" 730 | url: "https://pub.dev" 731 | source: hosted 732 | version: "2.1.4" 733 | vm_service: 734 | dependency: transitive 735 | description: 736 | name: vm_service 737 | sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" 738 | url: "https://pub.dev" 739 | source: hosted 740 | version: "14.2.5" 741 | web: 742 | dependency: transitive 743 | description: 744 | name: web 745 | sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb 746 | url: "https://pub.dev" 747 | source: hosted 748 | version: "1.1.0" 749 | win32: 750 | dependency: transitive 751 | description: 752 | name: win32 753 | sha256: "4d45dc9069dba4619dc0ebd93c7cec5e66d8482cb625a370ac806dcc8165f2ec" 754 | url: "https://pub.dev" 755 | source: hosted 756 | version: "5.5.5" 757 | window_manager: 758 | dependency: transitive 759 | description: 760 | name: window_manager 761 | sha256: ab8b2a7f97543d3db2b506c9d875e637149d48ee0c6a5cb5f5fd6e0dac463792 762 | url: "https://pub.dev" 763 | source: hosted 764 | version: "0.4.2" 765 | xdg_directories: 766 | dependency: transitive 767 | description: 768 | name: xdg_directories 769 | sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" 770 | url: "https://pub.dev" 771 | source: hosted 772 | version: "1.1.0" 773 | xml: 774 | dependency: transitive 775 | description: 776 | name: xml 777 | sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 778 | url: "https://pub.dev" 779 | source: hosted 780 | version: "6.5.0" 781 | yaru: 782 | dependency: "direct main" 783 | description: 784 | name: yaru 785 | sha256: "9e07131b9c3b9997d7784c3cb6ad24a218f8e0507d82f8fb07b7e160e111236d" 786 | url: "https://pub.dev" 787 | source: hosted 788 | version: "5.2.1" 789 | yaru_window: 790 | dependency: transitive 791 | description: 792 | name: yaru_window 793 | sha256: bc2a1df3c6f33477b47f84bf0a9325df411dbb7bd483ac88e5bc1c019d2f2560 794 | url: "https://pub.dev" 795 | source: hosted 796 | version: "0.2.1+1" 797 | yaru_window_linux: 798 | dependency: transitive 799 | description: 800 | name: yaru_window_linux 801 | sha256: "46a1a0743dfd45794cdaf8c5b3a48771ab73632b50a693f59c83b07988e96689" 802 | url: "https://pub.dev" 803 | source: hosted 804 | version: "0.2.1" 805 | yaru_window_manager: 806 | dependency: transitive 807 | description: 808 | name: yaru_window_manager 809 | sha256: b36c909fa082a7cb6e2f259d4357e16f08d3d8ab086685b81d1916e457100d1e 810 | url: "https://pub.dev" 811 | source: hosted 812 | version: "0.1.2+1" 813 | yaru_window_platform_interface: 814 | dependency: transitive 815 | description: 816 | name: yaru_window_platform_interface 817 | sha256: "93493d7e17a9e887ffa94c518bc5a4b3eb5425c009446e3294c689cb1a87b7e1" 818 | url: "https://pub.dev" 819 | source: hosted 820 | version: "0.1.2+1" 821 | yaru_window_web: 822 | dependency: transitive 823 | description: 824 | name: yaru_window_web 825 | sha256: "31468aeb515f72d5eeddcd62773094a4f48fee96f7f0494f8ce53ad3b38054f1" 826 | url: "https://pub.dev" 827 | source: hosted 828 | version: "0.0.3+1" 829 | zxing2: 830 | dependency: "direct main" 831 | description: 832 | name: zxing2 833 | sha256: "6cf995abd3c86f01ba882968dedffa7bc130185e382f2300239d2e857fc7912c" 834 | url: "https://pub.dev" 835 | source: hosted 836 | version: "0.2.3" 837 | sdks: 838 | dart: ">=3.5.3 <4.0.0" 839 | flutter: ">=3.24.3" 840 | --------------------------------------------------------------------------------