├── .gitignore
├── .metadata
├── .vscode
└── settings.json
├── LICENSE.md
├── README.md
├── analysis_options.yaml
├── android
├── .gitignore
├── app
│ ├── build.gradle
│ ├── google-services.json
│ └── src
│ │ ├── debug
│ │ └── AndroidManifest.xml
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── kotlin
│ │ │ └── com
│ │ │ │ └── gskinner
│ │ │ │ ├── flutter_folio
│ │ │ │ └── MainActivity.kt
│ │ │ │ └── flutterfolio
│ │ │ │ └── MainActivity.kt
│ │ └── res
│ │ │ ├── drawable-v21
│ │ │ └── launch_background.xml
│ │ │ ├── drawable
│ │ │ └── launch_background.xml
│ │ │ ├── mipmap-hdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-mdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xhdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── values-night
│ │ │ └── styles.xml
│ │ │ ├── values
│ │ │ └── styles.xml
│ │ │ └── xml
│ │ │ └── network_security_config.xml
│ │ └── profile
│ │ └── AndroidManifest.xml
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ └── gradle-wrapper.properties
├── settings.gradle
└── settings_aar.gradle
├── assets
├── fonts
│ ├── EmojiOneColor.otf
│ ├── Fraunces_72pt-SemiBold.ttf
│ ├── Raleway-Bold.ttf
│ ├── Raleway-BoldItalic.ttf
│ ├── Raleway-ExtraBold.ttf
│ ├── Raleway-ExtraBoldItalic.ttf
│ ├── Raleway-Italic.ttf
│ ├── Raleway-Medium.ttf
│ ├── Raleway-MediumItalic.ttf
│ ├── Raleway-Regular.ttf
│ └── scraps
│ │ ├── AlfaSlabOne-Regular.ttf
│ │ ├── Amiri-Regular.ttf
│ │ ├── Caveat-Medium.ttf
│ │ ├── Lato-Regular.ttf
│ │ ├── Mali-Medium.ttf
│ │ └── PathwayGothicOne-Regular.ttf
└── images
│ ├── emoji
│ ├── beers.svg
│ ├── checkmark.svg
│ ├── confetti.svg
│ ├── cool.svg
│ ├── crying-face.svg
│ ├── dizzy-face.svg
│ ├── exclamation-question.svg
│ ├── fire.svg
│ ├── folded-hands.svg
│ ├── heart-eyes.svg
│ ├── hundred-points.svg
│ ├── kissing-face.svg
│ ├── location-pin.svg
│ ├── musical-notes.svg
│ ├── palms-up.svg
│ ├── pile-of-poo.svg
│ ├── red-heart.svg
│ ├── shooting-star.svg
│ ├── smiling-eyes.svg
│ ├── sparkles.svg
│ ├── squinting-face.svg
│ ├── sunglasses-face.svg
│ ├── tears-of-joy-face.svg
│ └── warning-sign.svg
│ ├── empty-background.png
│ ├── empty_home_bg.png
│ ├── icons
│ ├── 2.0x
│ │ ├── add-page.png
│ │ ├── add.png
│ │ ├── camera.png
│ │ ├── emoji.png
│ │ ├── github.png
│ │ ├── image.png
│ │ ├── link-out.png
│ │ ├── microsite.png
│ │ ├── move-forward.png
│ │ ├── scraps.png
│ │ ├── send-backward.png
│ │ ├── share.png
│ │ ├── star.png
│ │ ├── text.png
│ │ ├── toggle-carousel.png
│ │ ├── toggle-list.png
│ │ ├── trashcan.png
│ │ └── view.png
│ ├── 3.0x
│ │ ├── add-page.png
│ │ ├── add.png
│ │ ├── camera.png
│ │ ├── emoji.png
│ │ ├── github.png
│ │ ├── image.png
│ │ ├── link-out.png
│ │ ├── microsite.png
│ │ ├── move-forward.png
│ │ ├── scraps.png
│ │ ├── send-backward.png
│ │ ├── share.png
│ │ ├── star.png
│ │ ├── text.png
│ │ ├── toggle-carousel.png
│ │ ├── toggle-list.png
│ │ ├── trashcan.png
│ │ └── view.png
│ ├── add-page.png
│ ├── add.png
│ ├── camera.png
│ ├── emoji.png
│ ├── github.png
│ ├── image.png
│ ├── link-out.png
│ ├── move-forward.png
│ ├── scraps.png
│ ├── send-backward.png
│ ├── share.png
│ ├── star.png
│ ├── text.png
│ ├── toggle-carousel.png
│ ├── toggle-list.png
│ ├── trashcan.png
│ ├── view.png
│ └── website.png
│ ├── landing_page
│ ├── dashedLine-desktop.png
│ ├── dashedLine-mobile.png
│ ├── laptop.png
│ ├── phone.png
│ ├── tablet.png
│ └── web.png
│ ├── logos
│ ├── 2.0x
│ │ └── flutterfolio-logo.png
│ └── flutterfolio-logo.png
│ ├── orange.jpg
│ └── profile.png
├── benchmark
├── .gitignore
├── README.md
├── app.dart
└── app_benchmark.dart
├── ios
├── .gitignore
├── Flutter
│ ├── AppFrameworkInfo.plist
│ ├── Debug.xcconfig
│ └── Release.xcconfig
├── GoogleService-Info.plist
├── Podfile
├── Podfile.lock
├── Runner.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ └── WorkspaceSettings.xcsettings
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── Runner.xcscheme
├── Runner.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ ├── IDEWorkspaceChecks.plist
│ │ └── WorkspaceSettings.xcsettings
├── Runner
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ ├── Contents.json
│ │ │ ├── Icon-App-1024x1024@1x.png
│ │ │ ├── Icon-App-20x20@1x.png
│ │ │ ├── Icon-App-20x20@2x.png
│ │ │ ├── Icon-App-20x20@3x.png
│ │ │ ├── Icon-App-29x29@1x.png
│ │ │ ├── Icon-App-29x29@2x.png
│ │ │ ├── Icon-App-29x29@3x.png
│ │ │ ├── Icon-App-40x40@1x.png
│ │ │ ├── Icon-App-40x40@2x.png
│ │ │ ├── Icon-App-40x40@3x.png
│ │ │ ├── Icon-App-60x60@2x.png
│ │ │ ├── Icon-App-60x60@3x.png
│ │ │ ├── Icon-App-76x76@1x.png
│ │ │ ├── Icon-App-76x76@2x.png
│ │ │ └── Icon-App-83.5x83.5@2x.png
│ │ ├── Contents.json
│ │ └── LaunchImage.imageset
│ │ │ ├── Contents.json
│ │ │ ├── LaunchImage.png
│ │ │ ├── LaunchImage@2x.png
│ │ │ ├── LaunchImage@3x.png
│ │ │ └── README.md
│ ├── Base.lproj
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ ├── Info.plist
│ └── Runner-Bridging-Header.h
└── build
│ ├── Runner.build
│ └── Release-iphoneos
│ │ └── Runner.build
│ │ └── dgph
│ └── XCBuildData
│ ├── 0aad0ddea01bc8265bfcfad241336ce9-desc.xcbuild
│ ├── 0aad0ddea01bc8265bfcfad241336ce9-manifest.xcbuild
│ ├── 27425e4797678a5d73e74b82b71b1b00-desc.xcbuild
│ ├── 27425e4797678a5d73e74b82b71b1b00-manifest.xcbuild
│ ├── 3acdea3adc952cfbd8fb351a0572bba6-desc.xcbuild
│ ├── 3acdea3adc952cfbd8fb351a0572bba6-manifest.xcbuild
│ ├── 5966525ad93b4840a9524e3d2dc8d5be-desc.xcbuild
│ ├── 5966525ad93b4840a9524e3d2dc8d5be-manifest.xcbuild
│ ├── BuildDescriptionCacheIndex-622a40702ce4e765a0d75779c6de3e7a
│ └── build.db
├── lib
├── _spikes
│ ├── button_sheet.dart
│ ├── firebase_realtime_db.dart
│ ├── firedart_stream_test.dart
│ ├── optimized_drag_stack
│ │ ├── my_movable_box.dart
│ │ └── optimized_drag_stack.dart
│ ├── popup_menu_spike
│ │ └── popup_panel_spike.dart
│ └── tab_bug_repro.dart
├── _utils
│ ├── build_utils.dart
│ ├── color_utils.dart
│ ├── context_utils.dart
│ ├── data_utils.dart
│ ├── device_info.dart
│ ├── easy_notifier.dart
│ ├── file_utils.dart
│ ├── input_utils.dart
│ ├── keyboard_utils.dart
│ ├── list_utils.dart
│ ├── logger.dart
│ ├── native_window_utils
│ │ ├── macos_window_utils.dart
│ │ ├── titlebar_wrappers
│ │ │ ├── linux_title_bar.dart
│ │ │ ├── linux_title_bar_buttons.dart
│ │ │ ├── linux_title_bar_icons.dart
│ │ │ ├── macos_title_bar.dart
│ │ │ └── windows_title_bar.dart
│ │ ├── window_utils.dart
│ │ ├── window_utils_native.dart
│ │ └── window_utils_no_op.dart
│ ├── notifications
│ │ └── close_notification.dart
│ ├── path_utils.dart
│ ├── string_utils.dart
│ ├── time_utils.dart
│ ├── timed
│ │ ├── cooldown.dart
│ │ ├── debouncer.dart
│ │ └── throttler.dart
│ └── universal_file
│ │ ├── io_file.dart
│ │ ├── universal_file.dart
│ │ ├── universal_file_locator.dart
│ │ └── web_file.dart
├── _widgets
│ ├── alignments.dart
│ ├── animate_do_extensions.dart
│ ├── animated
│ │ ├── animated_fractional_offset.dart
│ │ ├── animated_index_stack.dart
│ │ ├── animated_offset.dart
│ │ ├── animated_rotation.dart
│ │ ├── animated_scale.dart
│ │ ├── animated_shadow.dart
│ │ ├── animated_size.dart
│ │ ├── auto_fade.dart
│ │ └── opening_card.dart
│ ├── app_image.dart
│ ├── clickable_extensions.dart
│ ├── context_menu_overlay.dart
│ ├── decorated_container.dart
│ ├── flexibles
│ │ ├── padded_flexibles.dart
│ │ └── seperated_flexibles.dart
│ ├── gradient_container.dart
│ ├── listenable_builder.dart
│ ├── measure_size.dart
│ ├── mixins
│ │ ├── animated_state_mixins.dart
│ │ ├── focus_node_mixin.dart
│ │ ├── loading_state_mixin.dart
│ │ └── raw_keyboard_listener_mixin.dart
│ ├── no_animation_page.dart
│ ├── no_glow_scroll_behavior.dart
│ ├── positioned_all.dart
│ ├── rando_colored_box.dart
│ ├── rotation_3d.dart.dart
│ ├── rounded_card.dart
│ └── sized_and_translated.dart
├── app_keys.dart
├── commands
│ ├── app
│ │ ├── authenticate_user_command.dart
│ │ ├── bootstrap_command.dart
│ │ ├── copy_share_link_command.dart
│ │ ├── refresh_menubar_command.dart
│ │ ├── save_image_to_disk_command.dart
│ │ ├── save_window_size_command.dart
│ │ ├── set_current_user_command.dart
│ │ └── update_user_command.dart
│ ├── books
│ │ ├── create_folio_command.dart
│ │ ├── create_page_command.dart
│ │ ├── create_placed_scraps_command.dart
│ │ ├── delete_book_command.dart
│ │ ├── delete_page_command.dart
│ │ ├── delete_page_scrap_command.dart
│ │ ├── delete_scraps_command.dart
│ │ ├── refresh_all_books_command.dart
│ │ ├── refresh_current_book_command.dart
│ │ ├── refresh_current_page_command.dart
│ │ ├── set_current_book_command.dart
│ │ ├── set_current_page_command.dart
│ │ ├── shift_placed_scraps_sort_order_command.dart
│ │ ├── update_book_command.dart
│ │ ├── update_book_modified_command.dart
│ │ ├── update_current_book_cover_photo_command.dart
│ │ ├── update_page_command.dart
│ │ ├── update_page_count_command.dart
│ │ ├── update_placed_scrap_command.dart
│ │ └── upload_image_scraps_command.dart
│ ├── commands.dart
│ └── pick_images_command.dart
├── core_packages.dart
├── data
│ ├── app_user.dart
│ ├── app_user.freezed.dart
│ ├── app_user.g.dart
│ ├── book_data.dart
│ ├── book_data.freezed.dart
│ └── book_data.g.dart
├── main.dart
├── main_app_scaffold.dart
├── models
│ ├── app_model.dart
│ └── books_model.dart
├── routing
│ ├── app_link.dart
│ ├── app_route_parser.dart
│ └── app_router.dart
├── services
│ ├── cloudinary
│ │ └── cloud_storage_service.dart
│ └── firebase
│ │ ├── firebase_service.dart
│ │ ├── firebase_service_firedart.dart
│ │ └── firebase_service_native.dart
├── styled_widgets
│ ├── app_icons.dart
│ ├── app_logo_text.dart
│ ├── book_cover_image.dart
│ ├── buttons
│ │ ├── raw_styled_btn.dart
│ │ ├── styled_buttons.dart
│ │ └── styled_share_btn.dart
│ ├── circle_avatar.dart
│ ├── context_menus
│ │ ├── app_context_menu.dart
│ │ ├── book_context_menu.dart
│ │ ├── context_menu_widgets.dart
│ │ ├── scrap_context_menu.dart
│ │ └── styled_context_menu_overlay.dart
│ ├── dialogs
│ │ ├── base_dialog.dart
│ │ ├── delete_dialog.dart
│ │ └── edit_text_dialog.dart
│ ├── emoji.dart
│ ├── glass_cards.dart
│ ├── inline_text_editor.dart
│ ├── labeled_text_input.dart
│ ├── material_icon.dart
│ ├── shadowed_box.dart
│ ├── styled_bottom_sheet.dart
│ ├── styled_load_spinner.dart
│ ├── styled_page_scaffold.dart
│ ├── styled_scrollbar.dart
│ ├── styled_spacers.dart
│ ├── styled_spinner.dart
│ ├── styled_toggle_switch.dart
│ ├── styled_tooltip.dart
│ ├── styled_widgets.dart
│ ├── toaster.dart
│ └── ui_text.dart
├── styles.dart
├── themes.dart
└── views
│ ├── app_title_bar
│ ├── app_title_bar.dart
│ ├── rounded_profile_button.dart
│ └── touch_mode_toggle_btn.dart
│ ├── auth_page
│ ├── auth_form.dart
│ ├── auth_page.dart
│ └── device_screens.dart
│ ├── editor_page
│ ├── draggable_page_menu
│ │ ├── draggable_page_menu.dart
│ │ └── draggable_page_title_btn.dart
│ ├── editor_page.dart
│ ├── empty_editor_view.dart
│ ├── networked_scrapboard.dart
│ ├── panels
│ │ ├── collapsible_info_panel.dart
│ │ ├── collapsible_pages_panel.dart
│ │ ├── collapsible_panels.dart
│ │ ├── content_picker_emoji_panel.dart
│ │ ├── content_picker_scraps_panel.dart
│ │ ├── content_picker_tab_menu.dart
│ │ └── simple_pages_menu.dart
│ ├── placed_scrap_keyboard_listener.dart
│ ├── placed_scrap_renderer.dart
│ ├── scrap_popup_editor
│ │ ├── animated_menu_panel.dart
│ │ ├── scrap_popup_editor.dart
│ │ ├── scrap_popup_panel_alignment.dart
│ │ ├── scrap_popup_panel_button_strip.dart
│ │ ├── scrap_popup_panel_color.dart
│ │ ├── scrap_popup_panel_fonts.dart
│ │ └── scrap_popup_panel_rotation.dart
│ └── scrapboard
│ │ ├── movable_scrap.dart
│ │ ├── movable_scrap_selection_box.dart
│ │ ├── scrap_data.dart
│ │ └── scrapboard.dart
│ ├── home_page
│ ├── book_cover
│ │ ├── book_cover.dart
│ │ ├── book_cover_large.dart
│ │ └── book_cover_small.dart
│ ├── covers_flow_list.dart
│ ├── covers_flow_list_mobile.dart
│ ├── covers_sortable_list.dart
│ ├── home_nav_bar.dart
│ ├── home_nav_bar_mobile.dart
│ └── home_page.dart
│ ├── scrap_pile_picker
│ ├── scrap_pile_picker.dart
│ ├── scrap_pile_picker_title_bar.dart
│ ├── scrap_pile_picker_view.dart
│ └── selectable_scrap_btn.dart
│ ├── splash_page.dart
│ └── user_profile_card
│ ├── user_profile_card.dart
│ └── user_profile_form.dart
├── linux
├── .gitignore
├── CMakeLists.txt
├── flutter
│ ├── CMakeLists.txt
│ ├── generated_plugin_registrant.cc
│ ├── generated_plugin_registrant.h
│ └── generated_plugins.cmake
├── main.cc
├── my_application.cc
└── my_application.h
├── macos
├── .gitignore
├── Flutter
│ ├── Flutter-Debug.xcconfig
│ ├── Flutter-Release.xcconfig
│ └── GeneratedPluginRegistrant.swift
├── Podfile
├── Podfile.lock
├── Runner.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ └── WorkspaceSettings.xcsettings
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── Runner.xcscheme
├── Runner.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
└── Runner
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ ├── Contents.json
│ │ ├── app_icon_1024.png
│ │ ├── app_icon_128.png
│ │ ├── app_icon_16.png
│ │ ├── app_icon_256.png
│ │ ├── app_icon_32.png
│ │ ├── app_icon_512.png
│ │ ├── app_icon_64.png
│ │ ├── folio-icon-1024.png
│ │ ├── folio-icon-128.png
│ │ ├── folio-icon-16.png
│ │ ├── folio-icon-256.png
│ │ ├── folio-icon-257.png
│ │ ├── folio-icon-32.png
│ │ ├── folio-icon-33.png
│ │ ├── folio-icon-512.png
│ │ ├── folio-icon-513.png
│ │ └── folio-icon-64.png
│ └── Contents.json
│ ├── Base.lproj
│ └── MainMenu.xib
│ ├── Configs
│ ├── AppInfo.xcconfig
│ ├── Debug.xcconfig
│ ├── Release.xcconfig
│ └── Warnings.xcconfig
│ ├── DebugProfile.entitlements
│ ├── GoogleService-Info.plist
│ ├── Info.plist
│ ├── MainFlutterWindow.swift
│ └── Release.entitlements
├── pubspec.lock
├── pubspec.yaml
├── snap
├── gui
│ ├── flutter-folio.desktop
│ └── flutter-folio.png
└── snapcraft.yaml
├── source-assets
├── app-flutter-folio-no-alpha.png
├── app-flutter-folio.png
└── flutterfolio.dmgCanvas
│ ├── Disk Image
│ └── QuickLook
│ └── Preview.jpg
├── web
├── favicon.ico
├── favicon.png
├── icons
│ ├── Icon-192.png
│ ├── Icon-512.png
│ └── icon-1024.png
├── index.html
└── manifest.json
└── windows
├── .gitignore
├── CMakeLists.txt
├── flutter
├── CMakeLists.txt
├── generated_plugin_registrant.cc
├── generated_plugin_registrant.h
└── generated_plugins.cmake
└── runner
├── CMakeLists.txt
├── Runner.rc
├── flutter_window.cpp
├── flutter_window.h
├── main.cpp
├── resource.h
├── resources
└── app_icon.ico
├── run_loop.cpp
├── run_loop.h
├── runner.exe.manifest
├── utils.cpp
├── utils.h
├── win32_window.cpp
└── win32_window.h
/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 |
12 | # IntelliJ related
13 | *.iml
14 | *.ipr
15 | *.iws
16 | .idea/
17 |
18 | # The .vscode folder contains launch configuration and tasks you configure in
19 | # VS Code which you may wish to be included in version control, so this line
20 | # is commented out by default.
21 | #.vscode/
22 |
23 | # Flutter/Dart/Pub related
24 | **/doc/api/
25 | **/ios/Flutter/.last_build_id
26 | .dart_tool/
27 | .flutter-plugins
28 | .flutter-plugins-dependencies
29 | .packages
30 | .pub-cache/
31 | .pub/
32 | /build/
33 |
34 | # Web related
35 | lib/generated_plugin_registrant.dart
36 |
37 | # Symbolication related
38 | app.*.symbols
39 |
40 | # Obfuscation related
41 | app.*.map.json
42 |
--------------------------------------------------------------------------------
/.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: 198df796aa80073ef22bdf249e614e2ff33c6895
8 | channel: beta
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "dart.lineLength": 120
3 | }
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 gskinner.com
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10 |
--------------------------------------------------------------------------------
/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | include: package:flutter_lints/flutter.yaml
2 |
3 | analyzer:
4 | exclude:
5 | - "**/*.g.dart"
6 | - "**/*.freezed.dart"
7 | language:
8 | strict-inference: true
9 | strong-mode:
10 | implicit-casts: false
11 | errors:
12 | missing_required_param: error
13 | linter:
14 | rules:
15 | avoid_print: false
16 | constant_identifier_names: false
17 |
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | GeneratedPluginRegistrant.java
8 |
9 | # Remember to never publicly share your keystore.
10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
11 | key.properties
12 |
--------------------------------------------------------------------------------
/android/app/google-services.json:
--------------------------------------------------------------------------------
1 | {
2 | "project_info": {
3 | "project_number": "150221469863",
4 | "project_id": "flutter-folio-demo",
5 | "storage_bucket": "flutter-folio-demo.appspot.com"
6 | },
7 | "client": [
8 | {
9 | "client_info": {
10 | "mobilesdk_app_id": "1:150221469863:android:9144b399e40b6759d6c382",
11 | "android_client_info": {
12 | "package_name": "com.gskinner.flutterfolio"
13 | }
14 | },
15 | "oauth_client": [
16 | {
17 | "client_id": "150221469863-0altlsfpu1cn9icosantcrefs27b6n6k.apps.googleusercontent.com",
18 | "client_type": 3
19 | }
20 | ],
21 | "api_key": [
22 | {
23 | "current_key": "AIzaSyAYThw0BP9fTuvbAtmV2DMYeggQLeI7v7Q"
24 | }
25 | ],
26 | "services": {
27 | "appinvite_service": {
28 | "other_platform_oauth_client": [
29 | {
30 | "client_id": "150221469863-0altlsfpu1cn9icosantcrefs27b6n6k.apps.googleusercontent.com",
31 | "client_type": 3
32 | },
33 | {
34 | "client_id": "150221469863-fricoi1555bg294u9j0i61tqfvpoo3th.apps.googleusercontent.com",
35 | "client_type": 2,
36 | "ios_info": {
37 | "bundle_id": "com.gskinner.flutterfolio"
38 | }
39 | }
40 | ]
41 | }
42 | }
43 | }
44 | ],
45 | "configuration_version": "1"
46 | }
--------------------------------------------------------------------------------
/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/com/gskinner/flutter_folio/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.gskinner.flutter_folio
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity: FlutterActivity() {
6 | }
7 |
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/com/gskinner/flutterfolio/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.gskinner.flutterfolio
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity: FlutterActivity() {
6 | }
7 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-v21/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/values-night/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/android/app/src/main/res/xml/network_security_config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.3.50'
3 | repositories {
4 | google()
5 | jcenter()
6 | }
7 |
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:3.5.0'
10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
11 | classpath 'com.google.gms:google-services:4.3.4' // Google Services plugin
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | google()
18 | jcenter()
19 | }
20 | }
21 |
22 | rootProject.buildDir = '../build'
23 | subprojects {
24 | project.buildDir = "${rootProject.buildDir}/${project.name}"
25 | }
26 | subprojects {
27 | project.evaluationDependsOn(':app')
28 | }
29 |
30 | task clean(type: Delete) {
31 | delete rootProject.buildDir
32 | }
33 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 | android.enableR8=true
5 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 23 08:50:38 CEST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip
7 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
4 | def properties = new Properties()
5 |
6 | assert localPropertiesFile.exists()
7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
8 |
9 | def flutterSdkPath = properties.getProperty("flutter.sdk")
10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
12 |
--------------------------------------------------------------------------------
/android/settings_aar.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/assets/fonts/EmojiOneColor.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/fonts/EmojiOneColor.otf
--------------------------------------------------------------------------------
/assets/fonts/Fraunces_72pt-SemiBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/fonts/Fraunces_72pt-SemiBold.ttf
--------------------------------------------------------------------------------
/assets/fonts/Raleway-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/fonts/Raleway-Bold.ttf
--------------------------------------------------------------------------------
/assets/fonts/Raleway-BoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/fonts/Raleway-BoldItalic.ttf
--------------------------------------------------------------------------------
/assets/fonts/Raleway-ExtraBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/fonts/Raleway-ExtraBold.ttf
--------------------------------------------------------------------------------
/assets/fonts/Raleway-ExtraBoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/fonts/Raleway-ExtraBoldItalic.ttf
--------------------------------------------------------------------------------
/assets/fonts/Raleway-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/fonts/Raleway-Italic.ttf
--------------------------------------------------------------------------------
/assets/fonts/Raleway-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/fonts/Raleway-Medium.ttf
--------------------------------------------------------------------------------
/assets/fonts/Raleway-MediumItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/fonts/Raleway-MediumItalic.ttf
--------------------------------------------------------------------------------
/assets/fonts/Raleway-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/fonts/Raleway-Regular.ttf
--------------------------------------------------------------------------------
/assets/fonts/scraps/AlfaSlabOne-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/fonts/scraps/AlfaSlabOne-Regular.ttf
--------------------------------------------------------------------------------
/assets/fonts/scraps/Amiri-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/fonts/scraps/Amiri-Regular.ttf
--------------------------------------------------------------------------------
/assets/fonts/scraps/Caveat-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/fonts/scraps/Caveat-Medium.ttf
--------------------------------------------------------------------------------
/assets/fonts/scraps/Lato-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/fonts/scraps/Lato-Regular.ttf
--------------------------------------------------------------------------------
/assets/fonts/scraps/Mali-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/fonts/scraps/Mali-Medium.ttf
--------------------------------------------------------------------------------
/assets/fonts/scraps/PathwayGothicOne-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/fonts/scraps/PathwayGothicOne-Regular.ttf
--------------------------------------------------------------------------------
/assets/images/emoji/checkmark.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/images/emoji/cool.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/images/emoji/crying-face.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/images/emoji/dizzy-face.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/images/emoji/exclamation-question.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/images/emoji/fire.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/images/emoji/heart-eyes.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/images/emoji/hundred-points.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/images/emoji/kissing-face.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/images/emoji/location-pin.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/images/emoji/musical-notes.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/images/emoji/pile-of-poo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/images/emoji/red-heart.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/images/emoji/shooting-star.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/images/emoji/smiling-eyes.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/images/emoji/sparkles.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/images/emoji/squinting-face.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/images/emoji/sunglasses-face.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/images/emoji/tears-of-joy-face.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/images/emoji/warning-sign.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/images/empty-background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/empty-background.png
--------------------------------------------------------------------------------
/assets/images/empty_home_bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/empty_home_bg.png
--------------------------------------------------------------------------------
/assets/images/icons/2.0x/add-page.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/2.0x/add-page.png
--------------------------------------------------------------------------------
/assets/images/icons/2.0x/add.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/2.0x/add.png
--------------------------------------------------------------------------------
/assets/images/icons/2.0x/camera.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/2.0x/camera.png
--------------------------------------------------------------------------------
/assets/images/icons/2.0x/emoji.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/2.0x/emoji.png
--------------------------------------------------------------------------------
/assets/images/icons/2.0x/github.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/2.0x/github.png
--------------------------------------------------------------------------------
/assets/images/icons/2.0x/image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/2.0x/image.png
--------------------------------------------------------------------------------
/assets/images/icons/2.0x/link-out.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/2.0x/link-out.png
--------------------------------------------------------------------------------
/assets/images/icons/2.0x/microsite.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/2.0x/microsite.png
--------------------------------------------------------------------------------
/assets/images/icons/2.0x/move-forward.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/2.0x/move-forward.png
--------------------------------------------------------------------------------
/assets/images/icons/2.0x/scraps.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/2.0x/scraps.png
--------------------------------------------------------------------------------
/assets/images/icons/2.0x/send-backward.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/2.0x/send-backward.png
--------------------------------------------------------------------------------
/assets/images/icons/2.0x/share.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/2.0x/share.png
--------------------------------------------------------------------------------
/assets/images/icons/2.0x/star.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/2.0x/star.png
--------------------------------------------------------------------------------
/assets/images/icons/2.0x/text.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/2.0x/text.png
--------------------------------------------------------------------------------
/assets/images/icons/2.0x/toggle-carousel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/2.0x/toggle-carousel.png
--------------------------------------------------------------------------------
/assets/images/icons/2.0x/toggle-list.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/2.0x/toggle-list.png
--------------------------------------------------------------------------------
/assets/images/icons/2.0x/trashcan.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/2.0x/trashcan.png
--------------------------------------------------------------------------------
/assets/images/icons/2.0x/view.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/2.0x/view.png
--------------------------------------------------------------------------------
/assets/images/icons/3.0x/add-page.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/3.0x/add-page.png
--------------------------------------------------------------------------------
/assets/images/icons/3.0x/add.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/3.0x/add.png
--------------------------------------------------------------------------------
/assets/images/icons/3.0x/camera.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/3.0x/camera.png
--------------------------------------------------------------------------------
/assets/images/icons/3.0x/emoji.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/3.0x/emoji.png
--------------------------------------------------------------------------------
/assets/images/icons/3.0x/github.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/3.0x/github.png
--------------------------------------------------------------------------------
/assets/images/icons/3.0x/image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/3.0x/image.png
--------------------------------------------------------------------------------
/assets/images/icons/3.0x/link-out.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/3.0x/link-out.png
--------------------------------------------------------------------------------
/assets/images/icons/3.0x/microsite.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/3.0x/microsite.png
--------------------------------------------------------------------------------
/assets/images/icons/3.0x/move-forward.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/3.0x/move-forward.png
--------------------------------------------------------------------------------
/assets/images/icons/3.0x/scraps.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/3.0x/scraps.png
--------------------------------------------------------------------------------
/assets/images/icons/3.0x/send-backward.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/3.0x/send-backward.png
--------------------------------------------------------------------------------
/assets/images/icons/3.0x/share.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/3.0x/share.png
--------------------------------------------------------------------------------
/assets/images/icons/3.0x/star.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/3.0x/star.png
--------------------------------------------------------------------------------
/assets/images/icons/3.0x/text.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/3.0x/text.png
--------------------------------------------------------------------------------
/assets/images/icons/3.0x/toggle-carousel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/3.0x/toggle-carousel.png
--------------------------------------------------------------------------------
/assets/images/icons/3.0x/toggle-list.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/3.0x/toggle-list.png
--------------------------------------------------------------------------------
/assets/images/icons/3.0x/trashcan.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/3.0x/trashcan.png
--------------------------------------------------------------------------------
/assets/images/icons/3.0x/view.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/3.0x/view.png
--------------------------------------------------------------------------------
/assets/images/icons/add-page.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/add-page.png
--------------------------------------------------------------------------------
/assets/images/icons/add.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/add.png
--------------------------------------------------------------------------------
/assets/images/icons/camera.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/camera.png
--------------------------------------------------------------------------------
/assets/images/icons/emoji.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/emoji.png
--------------------------------------------------------------------------------
/assets/images/icons/github.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/github.png
--------------------------------------------------------------------------------
/assets/images/icons/image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/image.png
--------------------------------------------------------------------------------
/assets/images/icons/link-out.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/link-out.png
--------------------------------------------------------------------------------
/assets/images/icons/move-forward.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/move-forward.png
--------------------------------------------------------------------------------
/assets/images/icons/scraps.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/scraps.png
--------------------------------------------------------------------------------
/assets/images/icons/send-backward.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/send-backward.png
--------------------------------------------------------------------------------
/assets/images/icons/share.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/share.png
--------------------------------------------------------------------------------
/assets/images/icons/star.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/star.png
--------------------------------------------------------------------------------
/assets/images/icons/text.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/text.png
--------------------------------------------------------------------------------
/assets/images/icons/toggle-carousel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/toggle-carousel.png
--------------------------------------------------------------------------------
/assets/images/icons/toggle-list.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/toggle-list.png
--------------------------------------------------------------------------------
/assets/images/icons/trashcan.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/trashcan.png
--------------------------------------------------------------------------------
/assets/images/icons/view.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/view.png
--------------------------------------------------------------------------------
/assets/images/icons/website.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/icons/website.png
--------------------------------------------------------------------------------
/assets/images/landing_page/dashedLine-desktop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/landing_page/dashedLine-desktop.png
--------------------------------------------------------------------------------
/assets/images/landing_page/dashedLine-mobile.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/landing_page/dashedLine-mobile.png
--------------------------------------------------------------------------------
/assets/images/landing_page/laptop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/landing_page/laptop.png
--------------------------------------------------------------------------------
/assets/images/landing_page/phone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/landing_page/phone.png
--------------------------------------------------------------------------------
/assets/images/landing_page/tablet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/landing_page/tablet.png
--------------------------------------------------------------------------------
/assets/images/landing_page/web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/landing_page/web.png
--------------------------------------------------------------------------------
/assets/images/logos/2.0x/flutterfolio-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/logos/2.0x/flutterfolio-logo.png
--------------------------------------------------------------------------------
/assets/images/logos/flutterfolio-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/logos/flutterfolio-logo.png
--------------------------------------------------------------------------------
/assets/images/orange.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/orange.jpg
--------------------------------------------------------------------------------
/assets/images/profile.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/assets/images/profile.png
--------------------------------------------------------------------------------
/benchmark/.gitignore:
--------------------------------------------------------------------------------
1 | *.benchmark
2 | *.json
3 |
--------------------------------------------------------------------------------
/benchmark/README.md:
--------------------------------------------------------------------------------
1 | # Performance profiling
2 |
3 | Run the following to gather new benchmark data:
4 |
5 | ```text
6 | $ flutter drive --target=benchmark/app.dart --driver=benchmark/app_benchmark.dart --profile --endless-trace-buffer --purge-persistent-cache
7 | ```
8 |
9 | You can do many runs one after another to minimize the effect of random noise
10 | in measurements.
11 |
12 | ```text
13 | for n in {1..10}; do echo "=== Run number ${n}/10 ==="; \
14 | flutter drive \
15 | --target=benchmark/app.dart \
16 | --driver=benchmark/app_benchmark.dart \
17 | --profile \
18 | --endless-trace-buffer \
19 | --purge-persistent-cache; \
20 | done
21 | ```
22 |
23 | (You can then combine the separate runs into one file using `benchmerge`.)
24 |
25 | Install Benchmarkhor (`pub global activate benchmarkhor`), then run:
26 |
27 | ```text
28 | $ benchextract benchmark/*.json
29 | ```
30 |
31 | Finally, compare different benchmark runs with
32 |
33 | ```text
34 | $ benchcompare benchmark/some-baseline.benchmark benchmark/new.benchmark
35 | ```
36 |
--------------------------------------------------------------------------------
/ios/.gitignore:
--------------------------------------------------------------------------------
1 | *.mode1v3
2 | *.mode2v3
3 | *.moved-aside
4 | *.pbxuser
5 | *.perspectivev3
6 | **/*sync/
7 | .sconsign.dblite
8 | .tags*
9 | **/.vagrant/
10 | **/DerivedData/
11 | Icon?
12 | **/Pods/
13 | **/.symlinks/
14 | profile
15 | xcuserdata
16 | **/.generated/
17 | Flutter/App.framework
18 | Flutter/Flutter.framework
19 | Flutter/Flutter.podspec
20 | Flutter/Generated.xcconfig
21 | Flutter/app.flx
22 | Flutter/app.zip
23 | Flutter/flutter_assets/
24 | Flutter/flutter_export_environment.sh
25 | ServiceDefinitions.json
26 | Runner/GeneratedPluginRegistrant.*
27 |
28 | # Exceptions to above rules.
29 | !default.mode1v3
30 | !default.mode2v3
31 | !default.pbxuser
32 | !default.perspectivev3
33 |
--------------------------------------------------------------------------------
/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | App
9 | CFBundleIdentifier
10 | io.flutter.flutter.app
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | App
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | MinimumOSVersion
24 | 9.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
2 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
3 | #include "Generated.xcconfig"
4 |
--------------------------------------------------------------------------------
/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
2 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
3 | #include "Generated.xcconfig"
4 |
--------------------------------------------------------------------------------
/ios/GoogleService-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CLIENT_ID
6 | 150221469863-fricoi1555bg294u9j0i61tqfvpoo3th.apps.googleusercontent.com
7 | REVERSED_CLIENT_ID
8 | com.googleusercontent.apps.150221469863-fricoi1555bg294u9j0i61tqfvpoo3th
9 | API_KEY
10 | AIzaSyDV0SE4WzZX--KRsdVZeS2pk8_81VrAJMk
11 | GCM_SENDER_ID
12 | 150221469863
13 | PLIST_VERSION
14 | 1
15 | BUNDLE_ID
16 | com.gskinner.flutterfolio
17 | PROJECT_ID
18 | flutter-folio-demo
19 | STORAGE_BUCKET
20 | flutter-folio-demo.appspot.com
21 | IS_ADS_ENABLED
22 |
23 | IS_ANALYTICS_ENABLED
24 |
25 | IS_APPINVITE_ENABLED
26 |
27 | IS_GCM_ENABLED
28 |
29 | IS_SIGNIN_ENABLED
30 |
31 | GOOGLE_APP_ID
32 | 1:150221469863:ios:67573e189598eff0d6c382
33 | DATABASE_URL
34 | https://flutter-folio-demo-default-rtdb.firebaseio.com
35 |
36 |
--------------------------------------------------------------------------------
/ios/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment this line to define a global platform for your project
2 | platform :ios, '10.3'
3 |
4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true'
6 |
7 | project 'Runner', {
8 | 'Debug' => :debug,
9 | 'Profile' => :release,
10 | 'Release' => :release,
11 | }
12 |
13 | def flutter_root
14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
15 | unless File.exist?(generated_xcode_build_settings_path)
16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
17 | end
18 |
19 | File.foreach(generated_xcode_build_settings_path) do |line|
20 | matches = line.match(/FLUTTER_ROOT\=(.*)/)
21 | return matches[1].strip if matches
22 | end
23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
24 | end
25 |
26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
27 |
28 | flutter_ios_podfile_setup
29 |
30 | target 'Runner' do
31 | use_frameworks!
32 | use_modular_headers!
33 |
34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
35 | end
36 |
37 | post_install do |installer|
38 | installer.pods_project.targets.each do |target|
39 | flutter_additional_ios_build_settings(target)
40 | target.build_configurations.each do |config|
41 | config.build_settings.delete 'IPHONEOS_DEPLOYMENT_TARGET'
42 | end
43 | end
44 | end
45 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Flutter
3 |
4 | @UIApplicationMain
5 | @objc class AppDelegate: FlutterAppDelegate {
6 | override func application(
7 | _ application: UIApplication,
8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
9 | ) -> Bool {
10 | GeneratedPluginRegistrant.register(with: self)
11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "LaunchImage.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "LaunchImage@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "LaunchImage@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md:
--------------------------------------------------------------------------------
1 | # Launch Screen Assets
2 |
3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory.
4 |
5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
--------------------------------------------------------------------------------
/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
2 |
--------------------------------------------------------------------------------
/ios/build/Runner.build/Release-iphoneos/Runner.build/dgph:
--------------------------------------------------------------------------------
1 | DGPH1.04Dec 21 202021:57:39 / Users shawn Dev gskinner
flutter-folio ios
--------------------------------------------------------------------------------
/ios/build/XCBuildData/0aad0ddea01bc8265bfcfad241336ce9-desc.xcbuild:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/ios/build/XCBuildData/0aad0ddea01bc8265bfcfad241336ce9-desc.xcbuild
--------------------------------------------------------------------------------
/ios/build/XCBuildData/27425e4797678a5d73e74b82b71b1b00-desc.xcbuild:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/ios/build/XCBuildData/27425e4797678a5d73e74b82b71b1b00-desc.xcbuild
--------------------------------------------------------------------------------
/ios/build/XCBuildData/3acdea3adc952cfbd8fb351a0572bba6-desc.xcbuild:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/ios/build/XCBuildData/3acdea3adc952cfbd8fb351a0572bba6-desc.xcbuild
--------------------------------------------------------------------------------
/ios/build/XCBuildData/5966525ad93b4840a9524e3d2dc8d5be-desc.xcbuild:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/ios/build/XCBuildData/5966525ad93b4840a9524e3d2dc8d5be-desc.xcbuild
--------------------------------------------------------------------------------
/ios/build/XCBuildData/BuildDescriptionCacheIndex-622a40702ce4e765a0d75779c6de3e7a:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/ios/build/XCBuildData/BuildDescriptionCacheIndex-622a40702ce4e765a0d75779c6de3e7a
--------------------------------------------------------------------------------
/ios/build/XCBuildData/build.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/ios/build/XCBuildData/build.db
--------------------------------------------------------------------------------
/lib/_spikes/tab_bug_repro.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class TabBugRepro extends StatelessWidget {
4 | const TabBugRepro({Key? key}) : super(key: key);
5 |
6 | @override
7 | Widget build(BuildContext context) {
8 | return MaterialApp(
9 | home: Scaffold(
10 | body: Column(
11 | children: [
12 | TextButton(onPressed: () {}, child: const Text("btn")),
13 | Flexible(
14 | child: IndexedStack(
15 | index: 0,
16 | children: [
17 | const SomeView(),
18 | ExcludeFocus(
19 | excluding: true,
20 | child: FocusTraversalGroup(child: const SomeView()),
21 | )
22 | ],
23 | )),
24 | ],
25 | ),
26 | ),
27 | );
28 | }
29 | }
30 |
31 | class SomeView extends StatelessWidget {
32 | const SomeView({Key? key}) : super(key: key);
33 |
34 | @override
35 | Widget build(BuildContext context) {
36 | return Row(children: [
37 | TextButton(onPressed: () {}, child: const Text("FOO")),
38 | TextButton(onPressed: () {}, child: const Text("BAR")),
39 | ]);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/lib/_utils/build_utils.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class BuildUtils {
4 | static void getFutureSizeFromGlobalKey(GlobalKey key, void Function(Size size) callback) {
5 | Future.microtask(() {
6 | Size? size = getSizeFromContext(key.currentContext);
7 | if (size != null) {
8 | callback(size);
9 | }
10 | });
11 | }
12 |
13 | static Size? getSizeFromContext(BuildContext? context) {
14 | if (context == null) return null;
15 | RenderBox? rb = context.findRenderObject() as RenderBox?;
16 | return rb?.size;
17 | }
18 |
19 | static Offset? getOffsetFromContext(BuildContext? context, [Offset offset = Offset.zero]) {
20 | if (context == null) return null;
21 | RenderBox? rb = context.findRenderObject() as RenderBox?;
22 | return rb?.localToGlobal(offset);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/lib/_utils/color_utils.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 |
3 | class ColorUtils {
4 | static Color shiftHsl(Color c, [double amt = 0]) {
5 | HSLColor hslColor = HSLColor.fromColor(c);
6 | return hslColor.withLightness((hslColor.lightness + amt).clamp(0.0, 1.0).toDouble()).toColor();
7 | }
8 |
9 | static Color parseHex(String value) => Color(int.parse(value.substring(1, 7), radix: 16) + 0xFF000000);
10 |
11 | static Color blend(Color dst, Color src, double opacity) {
12 | return Color.fromARGB(
13 | 255,
14 | (dst.red.toDouble() * (1.0 - opacity) + src.red.toDouble() * opacity).toInt(),
15 | (dst.green.toDouble() * (1.0 - opacity) + src.green.toDouble() * opacity).toInt(),
16 | (dst.blue.toDouble() * (1.0 - opacity) + src.blue.toDouble() * opacity).toInt(),
17 | );
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/lib/_utils/context_utils.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class ContextUtils {
4 | // Utility methods to get the size/pos of our render boxes in global & local space
5 | static Size getSize(BuildContext c) {
6 | try {
7 | RenderBox? rb = c.findRenderObject() as RenderBox?;
8 | return rb?.size ?? Size.zero;
9 | } catch (e) {
10 | print(e);
11 | }
12 | return const Size(1, 1);
13 | }
14 |
15 | static Offset localToGlobal(BuildContext c, {Offset local = Offset.zero}) {
16 | try {
17 | return (c.findRenderObject() as RenderBox?)?.localToGlobal(local) ?? Offset.zero;
18 | } catch (e) {
19 | //print(e);
20 | }
21 | return Offset.zero;
22 | }
23 |
24 | static Offset globalToLocal(BuildContext c, Offset global) {
25 | try {
26 | return (c.findRenderObject() as RenderBox?)?.globalToLocal(global) ?? Offset.zero;
27 | } catch (e) {
28 | //print(e);
29 | }
30 | return Offset.zero;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/lib/_utils/data_utils.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_folio/data/book_data.dart';
2 |
3 | class _IndexPair {
4 | int index1, index2;
5 | _IndexPair(this.index1, this.index2);
6 | }
7 |
8 | class DataUtils {
9 | static List sortListById(List pages, List? pageIds) {
10 | if (pageIds?.isEmpty ?? true) return pages;
11 | List<_IndexPair> listIndices = [];
12 | List sortedList = [];
13 | for (int i = 0; i < pages.length; ++i) {
14 | int elementOrder = pageIds!.indexWhere((pageId) => pageId == pages[i].documentId);
15 | listIndices.add(_IndexPair(elementOrder != -1 ? elementOrder : 1000000000, i));
16 | }
17 | listIndices.sort((a, b) => a.index1.compareTo(b.index1));
18 | for (int i = 0; i < listIndices.length; ++i) {
19 | sortedList.add(pages[listIndices[i].index2]);
20 | }
21 | return sortedList;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/lib/_utils/easy_notifier.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | // Has a notify() method that reduces boilerplate in ChangeNotifiers, similar to setState((){}) in a StatefulWidget;
4 | // Also allows external .notify() calls, being un-opinionated about whether this is called externally.
5 | class EasyNotifier extends ChangeNotifier {
6 | void notify([VoidCallback? action]) {
7 | action?.call();
8 | notifyListeners();
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/lib/_utils/file_utils.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | class FileUtils {
4 | Future readAsString(String path) async {
5 | try {
6 | return await File(path).readAsString();
7 | } catch (e) {
8 | print("$e");
9 | }
10 | return null;
11 | }
12 |
13 | Future writeAsString(String path, String contents) async {
14 | try {
15 | await File(path).writeAsString(contents, flush: true);
16 | } catch (e) {
17 | print("$e");
18 | }
19 | return;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/lib/_utils/input_utils.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter/rendering.dart';
3 | import 'package:flutter/services.dart';
4 |
5 | class InputUtils {
6 | static void hideKeyboard() {
7 | SystemChannels.textInput.invokeMethod('TextInput.hide');
8 | }
9 |
10 | static bool get isMouseConnected => RendererBinding.instance?.mouseTracker.mouseIsConnected ?? false;
11 |
12 | static void unFocus() {
13 | primaryFocus?.unfocus();
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/lib/_utils/list_utils.dart:
--------------------------------------------------------------------------------
1 | class ListUtils {
2 | static List interleave(List listA, List listB) {
3 | List result = [];
4 | for (var i = 0; i < listA.length; i++) {
5 | result..add(listA[i])..add(listB[i]);
6 | }
7 | return result;
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/lib/_utils/logger.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:flutter/foundation.dart';
4 | import 'package:flutter/material.dart';
5 |
6 | _Dispatcher logHistory = _Dispatcher("");
7 |
8 | void log(String? value) {
9 | String v = value ?? "";
10 | logHistory.value = v + "\n" + logHistory.value;
11 | if (kReleaseMode == false) {
12 | print(v);
13 | }
14 | }
15 |
16 | void logError(String? value) => log("[ERROR] " + (value ?? ""));
17 |
18 | // Take from: https://flutter.dev/docs/testing/errors
19 | void initLogger(VoidCallback runApp) {
20 | runZonedGuarded(() async {
21 | WidgetsFlutterBinding.ensureInitialized();
22 | FlutterError.onError = (FlutterErrorDetails details) {
23 | FlutterError.dumpErrorToConsole(details);
24 | logError(details.stack.toString());
25 | };
26 | runApp.call();
27 | }, (Object error, StackTrace stack) {
28 | logError(stack.toString());
29 | });
30 | }
31 |
32 | class _Dispatcher extends ValueNotifier {
33 | _Dispatcher(String value) : super(value);
34 | }
35 |
--------------------------------------------------------------------------------
/lib/_utils/native_window_utils/macos_window_utils.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 | import 'dart:math';
3 |
4 | import 'package:flutter/services.dart';
5 |
6 | class MacosWindowUtils {
7 | static const methodChannel = MethodChannel("flutterfolio.com/io");
8 | static const kMinTitlebarHeight = 24.0;
9 | static const kDefaultTitlebarHeight = 24.0;
10 | static double _calculatedTitlebarHeight = 0.0;
11 |
12 | static Future requestTitlebarHeight() async {
13 | if (Platform.isMacOS == false) return kDefaultTitlebarHeight;
14 | if (_calculatedTitlebarHeight > 0.0) {
15 | return _calculatedTitlebarHeight;
16 | }
17 | try {
18 | final double h = await methodChannel.invokeMethod("getTitlebarHeight") as double;
19 | _calculatedTitlebarHeight = max(h, kMinTitlebarHeight);
20 | } catch (e) {
21 | print("MethodChannel error: $e");
22 | _calculatedTitlebarHeight = kDefaultTitlebarHeight;
23 | }
24 | return _calculatedTitlebarHeight;
25 | }
26 |
27 | static void zoom() async {
28 | if (Platform.isMacOS == false) return;
29 | try {
30 | await methodChannel.invokeMethod("zoom");
31 | } catch (e) {
32 | print("MethodChannel error: $e");
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/lib/_utils/native_window_utils/titlebar_wrappers/macos_title_bar.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_folio/_utils/native_window_utils/macos_window_utils.dart';
3 |
4 | class MacosTitleBar extends StatefulWidget {
5 | const MacosTitleBar(this.child, {Key? key}) : super(key: key);
6 | final Widget child;
7 |
8 | @override
9 | _MacosTitleBarState createState() => _MacosTitleBarState();
10 | }
11 |
12 | class _MacosTitleBarState extends State {
13 | late Future _futureHeight;
14 |
15 | @override
16 | void initState() {
17 | _futureHeight = MacosWindowUtils.requestTitlebarHeight();
18 | super.initState();
19 | }
20 |
21 | @override
22 | Widget build(BuildContext context) {
23 | double defaultHeight = MacosWindowUtils.kDefaultTitlebarHeight;
24 | return FutureBuilder(
25 | // Request the transparent titlebar height from the OS (async)
26 | future: _futureHeight,
27 | builder: (BuildContext context, AsyncSnapshot snapshot) {
28 | bool isFutureDone = snapshot.connectionState == ConnectionState.done;
29 | double height = isFutureDone ? snapshot.data ?? defaultHeight : defaultHeight;
30 | return GestureDetector(
31 | onDoubleTap: () => MacosWindowUtils.zoom(),
32 | child: Container(color: Colors.transparent, height: height, child: widget.child),
33 | );
34 | });
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/lib/_utils/native_window_utils/titlebar_wrappers/windows_title_bar.dart:
--------------------------------------------------------------------------------
1 | import 'package:bitsdojo_window/bitsdojo_window.dart';
2 | import 'package:flutter/material.dart';
3 |
4 | /// Native TitleBar for Windows, uses BitDojo platform
5 | class WindowsTitleBar extends StatelessWidget {
6 | const WindowsTitleBar(this.child, {Key? key}) : super(key: key);
7 | final Widget child;
8 |
9 | @override
10 | Widget build(BuildContext context) {
11 | final WindowButtonColors _btnColors = WindowButtonColors(
12 | iconNormal: Colors.black,
13 | mouseOver: Colors.black.withOpacity(.2),
14 | mouseDown: Colors.black.withOpacity(.3),
15 | normal: Colors.black.withOpacity(0),
16 | );
17 | return SizedBox(
18 | height: 40,
19 | child: Stack(
20 | children: [
21 | MoveWindow(),
22 | child,
23 | Align(
24 | alignment: Alignment.topRight,
25 | child: Row(
26 | mainAxisAlignment: MainAxisAlignment.end,
27 | children: [
28 | MinimizeWindowButton(colors: _btnColors),
29 | MaximizeWindowButton(colors: _btnColors),
30 | CloseWindowButton(),
31 | ],
32 | ),
33 | ),
34 | ],
35 | ),
36 | );
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/lib/_utils/native_window_utils/window_utils.dart:
--------------------------------------------------------------------------------
1 | library native_utils;
2 |
3 | import 'package:flutter/material.dart';
4 |
5 | import 'window_utils_no_op.dart' if (dart.library.io) 'window_utils_native.dart' as pkg;
6 |
7 | abstract class IoUtils {
8 | static IoUtils get instance => pkg.getInstance();
9 |
10 | void setTitle(String title);
11 | void showWindowWhenReady();
12 | Widget wrapNativeTitleBarIfRequired(Widget child);
13 | }
14 |
--------------------------------------------------------------------------------
/lib/_utils/native_window_utils/window_utils_native.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | import 'package:bitsdojo_window/bitsdojo_window.dart';
4 | import 'package:flutter/foundation.dart';
5 | import 'package:flutter/material.dart';
6 | import 'package:flutter_folio/_utils/device_info.dart';
7 | import 'package:flutter_folio/_utils/native_window_utils/titlebar_wrappers/linux_title_bar.dart';
8 | import 'package:flutter_folio/_utils/native_window_utils/titlebar_wrappers/macos_title_bar.dart';
9 | import 'package:flutter_folio/_utils/native_window_utils/titlebar_wrappers/windows_title_bar.dart';
10 | import 'package:flutter_folio/_utils/native_window_utils/window_utils.dart';
11 |
12 | IoUtils _instance = IoUtilsNative();
13 | IoUtils getInstance() => _instance;
14 |
15 | class IoUtilsNative implements IoUtils {
16 | IoUtilsNative();
17 |
18 | @override
19 | void showWindowWhenReady() {
20 | if (Platform.isWindows == false) return;
21 | doWhenWindowReady(() {
22 | // Apply min-window size, allow a smaller size when developing for easier responsive testing.
23 | appWindow.minSize = kReleaseMode ? const Size(800, 600) : const Size(300, 400);
24 | appWindow.show();
25 | });
26 | }
27 |
28 | @override
29 | Widget wrapNativeTitleBarIfRequired(Widget child) {
30 | if (DeviceOS.isWindows) {
31 | return WindowsTitleBar(child);
32 | } else if (DeviceOS.isLinux) {
33 | return LinuxTitleBar(child);
34 | } else if (DeviceOS.isMacOS) {
35 | return MacosTitleBar(child);
36 | }
37 | return child;
38 | }
39 |
40 | @override
41 | void setTitle(String title) {
42 | appWindow.title = title;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/lib/_utils/native_window_utils/window_utils_no_op.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import 'window_utils.dart';
4 |
5 | IoUtils _instance = IoUtilsNoOp();
6 | IoUtils getInstance() => _instance;
7 |
8 | class IoUtilsNoOp implements IoUtils {
9 | @override
10 | void showWindowWhenReady() {}
11 | @override
12 | Widget wrapNativeTitleBarIfRequired(Widget child) => child;
13 | void setMinSize(Size size) {}
14 |
15 | @override
16 | void setTitle(String title) {}
17 | }
18 |
--------------------------------------------------------------------------------
/lib/_utils/notifications/close_notification.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class CloseNotification extends Notification {}
4 |
--------------------------------------------------------------------------------
/lib/_utils/path_utils.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | import 'package:path_provider/path_provider.dart';
4 | import 'package:xdg_directories/xdg_directories.dart' as xdg_directories;
5 |
6 | class PathUtil {
7 | static Future get dataPath async {
8 | String? result;
9 | if (Platform.isLinux) {
10 | result = "${xdg_directories.dataHome.path}/flutterfolio";
11 | } else {
12 | try {
13 | return (await getApplicationSupportDirectory()).path;
14 | } catch (e) {
15 | print("$e");
16 | }
17 | }
18 | return result;
19 | }
20 |
21 | static Future get homePath async {
22 | return "~/";
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/lib/_utils/time_utils.dart:
--------------------------------------------------------------------------------
1 | class TimeUtils {
2 | static int get nowMillis => DateTime.now().millisecondsSinceEpoch;
3 | static int get nowSeconds => (nowMillis * .001).round();
4 | }
5 |
--------------------------------------------------------------------------------
/lib/_utils/timed/cooldown.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class CoolDown {
4 | int _lastCallMs = 0;
5 |
6 | CoolDown(this.duration);
7 | final Duration duration;
8 |
9 | void run(VoidCallback action) {
10 | if (DateTime.now().millisecondsSinceEpoch - _lastCallMs > duration.inMilliseconds) {
11 | action.call();
12 | _lastCallMs = DateTime.now().millisecondsSinceEpoch;
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/lib/_utils/timed/debouncer.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:flutter/material.dart';
4 |
5 | class Debouncer {
6 | Debouncer(this.duration);
7 | Duration duration;
8 | Timer? _timer;
9 |
10 | void run(VoidCallback action) {
11 | _timer?.cancel();
12 | _timer = Timer(duration, action);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/lib/_utils/timed/throttler.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:flutter/material.dart';
4 |
5 | class Throttler {
6 | Throttler(this.interval);
7 | final Duration interval;
8 |
9 | VoidCallback? _action;
10 | Timer? _timer;
11 |
12 | void call(VoidCallback action, {bool immediateCall = false}) {
13 | _action = action;
14 | if (_timer == null) {
15 | // If no timer is running, and immediateCall is true, just handle the action now
16 | if (immediateCall) {
17 | _action?.call();
18 | _action = null; // Set it to null since we've already handled it
19 | }
20 | _timer = Timer(interval, () {
21 | _action?.call();
22 | _timer = null;
23 | });
24 | }
25 | }
26 |
27 | void cancelPending() => _action = null;
28 | }
29 |
--------------------------------------------------------------------------------
/lib/_utils/universal_file/universal_file.dart:
--------------------------------------------------------------------------------
1 | //If in web, this class will write a string to the prefs file, using filename as key
2 | //If on desktop or mobile, write to the appData folder
3 |
4 | import 'universal_file_locator.dart' if (dart.library.html) 'web_file.dart' if (dart.library.io) 'io_file.dart';
5 |
6 | abstract class UniversalFile {
7 | late final String fileName;
8 |
9 | Future write(String value, [bool append = false]);
10 |
11 | Future read();
12 |
13 | factory UniversalFile(String fileName) => getPlatformFileWriter(fileName);
14 | }
15 |
--------------------------------------------------------------------------------
/lib/_utils/universal_file/universal_file_locator.dart:
--------------------------------------------------------------------------------
1 | import 'universal_file.dart';
2 |
3 | UniversalFile getPlatformFileWriter(String fileName) =>
4 | throw UnsupportedError('Cannot create a fileWriter for "$fileName" without the packages dart:html or dart:io');
5 |
--------------------------------------------------------------------------------
/lib/_utils/universal_file/web_file.dart:
--------------------------------------------------------------------------------
1 | import 'package:shared_preferences/shared_preferences.dart';
2 |
3 | import 'universal_file.dart';
4 |
5 | class WebFileWriter implements UniversalFile {
6 | SharedPreferences? prefs;
7 |
8 | @override
9 | String fileName;
10 |
11 | String _lastWrite = "";
12 |
13 | WebFileWriter(this.fileName);
14 |
15 | Future initPrefs() async {
16 | prefs ??= await SharedPreferences.getInstance();
17 | }
18 |
19 | @override
20 | Future read() async {
21 | await initPrefs();
22 | String? value = prefs?.getString(fileName);
23 | //print("Reading pref: $fileName = $value");
24 | return value;
25 | }
26 |
27 | @override
28 | Future write(String value, [bool append = false]) async {
29 | await initPrefs();
30 | if (append) {
31 | _lastWrite = await read() ?? "";
32 | value = _lastWrite + value;
33 | }
34 | //print("Write: $fileName = $value");
35 | _lastWrite = value;
36 | await prefs?.setString(fileName, value);
37 | }
38 | }
39 |
40 | UniversalFile getPlatformFileWriter(String fileName) => WebFileWriter(fileName);
41 |
--------------------------------------------------------------------------------
/lib/_widgets/animate_do_extensions.dart:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/lib/_widgets/animated/animated_fractional_offset.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class AnimatedFractionalOffset extends StatelessWidget {
4 | const AnimatedFractionalOffset({
5 | required this.child,
6 | required this.duration,
7 | this.begin,
8 | required this.end,
9 | this.curve = Curves.easeOut,
10 | Key? key,
11 | }) : super(key: key);
12 | final Widget child;
13 | final Duration duration;
14 | final Offset? begin;
15 | final Offset end;
16 | final Curve curve;
17 |
18 | @override
19 | Widget build(BuildContext context) {
20 | return TweenAnimationBuilder(
21 | duration: duration,
22 | curve: curve,
23 | tween: Tween(begin: begin ?? end, end: end),
24 | builder: (context, offset, _) => FractionalTranslation(
25 | translation: offset,
26 | child: child,
27 | ),
28 | );
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/lib/_widgets/animated/animated_index_stack.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class AnimatedIndexStack extends StatefulWidget {
4 | final int index;
5 | final List children;
6 | final Duration duration;
7 |
8 | const AnimatedIndexStack({
9 | Key? key,
10 | required this.index,
11 | required this.children,
12 | this.duration = const Duration(milliseconds: 250),
13 | }) : super(key: key);
14 |
15 | @override
16 | _AnimatedIndexStackState createState() => _AnimatedIndexStackState();
17 | }
18 |
19 | class _AnimatedIndexStackState extends State with SingleTickerProviderStateMixin {
20 | late AnimationController _controller;
21 |
22 | @override
23 | void initState() {
24 | _controller = AnimationController(vsync: this, duration: widget.duration);
25 | _controller.forward();
26 | super.initState();
27 | }
28 |
29 | @override
30 | void dispose() {
31 | _controller.dispose();
32 | super.dispose();
33 | }
34 |
35 | @override
36 | void didUpdateWidget(AnimatedIndexStack oldWidget) {
37 | if (widget.index != oldWidget.index) {
38 | _controller.forward(from: 0.0);
39 | }
40 | super.didUpdateWidget(oldWidget);
41 | }
42 |
43 | @override
44 | Widget build(BuildContext context) {
45 | return FadeTransition(
46 | opacity: _controller,
47 | child: IndexedStack(index: widget.index, children: widget.children),
48 | );
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/lib/_widgets/animated/animated_offset.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_folio/_widgets/alignments.dart';
3 |
4 | class AnimatedOffset extends StatelessWidget {
5 | const AnimatedOffset({
6 | required this.child,
7 | required this.duration,
8 | this.begin,
9 | required this.end,
10 | this.curve = Curves.easeOut,
11 | Key? key,
12 | }) : super(key: key);
13 | final Widget child;
14 | final Duration duration;
15 | final Offset? begin;
16 | final Offset end;
17 | final Curve curve;
18 |
19 | @override
20 | Widget build(BuildContext context) {
21 | return TweenAnimationBuilder(
22 | duration: duration,
23 | curve: curve,
24 | tween: Tween(begin: begin ?? end, end: end),
25 | builder: (context, offset, _) => Transform.translate(
26 | offset: offset,
27 | child: TopLeft(child: child),
28 | ),
29 | );
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/lib/_widgets/animated/animated_rotation.dart:
--------------------------------------------------------------------------------
1 | import 'dart:math';
2 |
3 | import 'package:flutter/material.dart';
4 |
5 | class AnimatedRotation extends StatelessWidget {
6 | const AnimatedRotation({
7 | this.begin,
8 | required this.end,
9 | required this.duration,
10 | required this.child,
11 | this.curve = Curves.linear,
12 | Key? key,
13 | }) : super(key: key);
14 | final Widget child;
15 | final Duration duration;
16 | final double? begin;
17 | final double end;
18 | final Curve curve;
19 |
20 | @override
21 | Widget build(BuildContext context) {
22 | return TweenAnimationBuilder(
23 | duration: duration,
24 | curve: curve,
25 | tween: Tween(begin: begin ?? end, end: end),
26 | builder: (context, tweenValue, _) {
27 | // Convert degrees to rads
28 | return Transform.rotate(angle: (tweenValue * pi) / 180, child: child);
29 | },
30 | );
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/lib/_widgets/animated/animated_scale.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class AnimatedScale extends StatelessWidget {
4 | const AnimatedScale(
5 | {Key? key, required this.child, required this.end, required this.duration, this.begin, this.curve})
6 | : super(key: key);
7 | final Widget child;
8 | final Duration duration;
9 | final double? begin;
10 | final double end;
11 | final Curve? curve;
12 |
13 | @override
14 | Widget build(BuildContext context) => TweenAnimationBuilder(
15 | tween: Tween(begin: begin ?? .2, end: end),
16 | curve: curve ?? Curves.easeOut,
17 | duration: duration,
18 | child: child,
19 | builder: (_, value, cachedChild) {
20 | return Transform.scale(scale: value, child: cachedChild);
21 | },
22 | );
23 | }
24 |
--------------------------------------------------------------------------------
/lib/_widgets/animated/animated_shadow.dart:
--------------------------------------------------------------------------------
1 | import 'dart:math';
2 |
3 | import 'package:flutter/material.dart';
4 |
5 | class AnimatedShadow extends StatelessWidget {
6 | AnimatedShadow({
7 | Key? key,
8 | required this.child,
9 | required this.duration,
10 | required this.blurs,
11 | required this.colors,
12 | this.begin,
13 | required this.end,
14 | this.curve,
15 | }) : super(key: key) {
16 | assert(blurs.length == colors.length, "blurs.length and colors.length must match");
17 | }
18 |
19 | final Widget child;
20 | final Duration duration;
21 | final double? begin;
22 | final double end;
23 | final List blurs;
24 | final List colors;
25 | final Curve? curve;
26 |
27 | @override
28 | Widget build(BuildContext context) {
29 | return TweenAnimationBuilder(
30 | tween: Tween(begin: begin ?? end, end: end),
31 | curve: curve ?? Curves.easeOut,
32 | builder: (_, double value, _child) {
33 | return Container(
34 | decoration: BoxDecoration(boxShadow: [
35 | ...blurs.map((b) {
36 | Color c = colors[blurs.indexOf(b)];
37 | // Like a real shadow, it blurs more when raised, but the strength actually goes down
38 | return BoxShadow(
39 | blurRadius: max(0, b * value),
40 | color: c.withOpacity(value < .1 ? 0 : max(0, 1 - (value * (1 - c.opacity)))),
41 | );
42 | })
43 | ]),
44 | child: _child,
45 | );
46 | },
47 | child: child,
48 | duration: duration,
49 | );
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/lib/_widgets/app_image.dart:
--------------------------------------------------------------------------------
1 | import 'package:cached_network_image/cached_network_image.dart';
2 | import 'package:flutter/material.dart';
3 |
4 | // Simple wrapper around CachedNetworkImage that provides any boilerplate we need for images in the app
5 | class HostedImage extends StatelessWidget {
6 | const HostedImage(this.url, {Key? key, this.fit = BoxFit.cover}) : super(key: key);
7 | final String url;
8 | final BoxFit fit;
9 |
10 | @override
11 | Widget build(BuildContext context) {
12 | String secureUrl = url;
13 | if (url.contains("http://")) {
14 | secureUrl = secureUrl.replaceAll("http://", "https://");
15 | }
16 | return CachedNetworkImage(imageUrl: secureUrl, fit: fit);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/lib/_widgets/clickable_extensions.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | extension ClickableExtensions on Widget {
4 | Widget clickable(void Function() action, {bool opaque = true}) {
5 | return GestureDetector(
6 | behavior: opaque ? HitTestBehavior.opaque : HitTestBehavior.deferToChild,
7 | onTap: action,
8 | child: MouseRegion(
9 | cursor: SystemMouseCursors.click,
10 | opaque: opaque,
11 | child: this,
12 | ),
13 | );
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/lib/_widgets/listenable_builder.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | // Alternative to [AnimatedBuilder], same functionality but it reads better and follows the other builders (ValueListenableBuilder).
4 | class ListenableBuilder extends AnimatedWidget {
5 | const ListenableBuilder({Key? key, this.child, required Listenable listenable, required this.builder})
6 | : super(key: key, listenable: listenable);
7 |
8 | final Widget Function(BuildContext context, Widget? child) builder;
9 | final Widget? child;
10 |
11 | @override
12 | Widget build(BuildContext context) {
13 | return builder(context, child);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/lib/_widgets/measure_size.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter/rendering.dart';
3 |
4 | class MeasureSizeRenderObject extends RenderProxyBox {
5 | MeasureSizeRenderObject(this.onChange);
6 | void Function(Size size) onChange;
7 |
8 | Size? _prevSize;
9 | @override
10 | void performLayout() {
11 | super.performLayout();
12 | Size newSize = child?.size ?? Size.zero;
13 | if (_prevSize == newSize) return;
14 | _prevSize = newSize;
15 | WidgetsBinding.instance?.addPostFrameCallback((_) => onChange(newSize));
16 | }
17 | }
18 |
19 | class MeasureSize extends SingleChildRenderObjectWidget {
20 | const MeasureSize({Key? key, required this.onChange, required Widget child}) : super(key: key, child: child);
21 | final void Function(Size size) onChange;
22 | @override
23 | RenderObject createRenderObject(BuildContext context) => MeasureSizeRenderObject(onChange);
24 | }
25 |
--------------------------------------------------------------------------------
/lib/_widgets/mixins/focus_node_mixin.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | mixin SingleFocusNodeMixin on State {
4 | late FocusNode focusNode;
5 |
6 | @override
7 | void initState() {
8 | super.initState();
9 | focusNode = FocusNode();
10 | focusNode.addListener(() => setState(() {}));
11 | }
12 |
13 | @override
14 | void dispose() {
15 | focusNode.dispose();
16 | super.dispose();
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/lib/_widgets/mixins/loading_state_mixin.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | // Provides the common functionalist of having a isLoading toggle for a view, that is turned on while loading some content.
4 | mixin LoadingStateMixin on State {
5 | bool _isLoading = false;
6 |
7 | bool get isLoading => _isLoading;
8 |
9 | set isLoading(bool isLoading) {
10 | if (!mounted) return;
11 | setState(() => _isLoading = isLoading);
12 | }
13 |
14 | Future load(Future Function() action) async {
15 | isLoading = true;
16 | R result = await action();
17 | isLoading = false;
18 | return result;
19 | }
20 | }
21 | /*
22 | class LoadingStateMixinExample extends StatefulWidget {
23 | @override
24 | _LoadingStateMixinExampleState createState() => _LoadingStateMixinExampleState();
25 | }
26 |
27 | class _LoadingStateMixinExampleState extends State with LoadingStateMixin {
28 |
29 | void onPress() async {
30 | bool result = await load(doStuffAndReturnBool);
31 | await load(() => doStuffAndTakeBool(false));
32 | }
33 |
34 | Future doStuffAndReturnBool() async => true;
35 | Future doStuffAndTakeBool(bool value) async => value;
36 |
37 | @override
38 | Widget build(BuildContext context) {
39 | return Scaffold(
40 | body: isLoading
41 | // Loading State
42 | ? Text("Loading...")
43 | // Main Tree
44 | : Column(
45 | children: [],
46 | ),
47 | );
48 | }
49 | }
50 | */
51 |
--------------------------------------------------------------------------------
/lib/_widgets/mixins/raw_keyboard_listener_mixin.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter/services.dart';
3 |
4 | mixin RawKeyboardListenerMixin on State {
5 | // Must be provided by implementing class
6 | bool get enableKeyListener;
7 |
8 | @override
9 | void initState() {
10 | super.initState();
11 | RawKeyboard.instance.addListener(_handleKey);
12 | }
13 |
14 | @override
15 | void dispose() {
16 | RawKeyboard.instance.removeListener(_handleKey);
17 | super.dispose();
18 | }
19 |
20 | void handleKeyUp(RawKeyUpEvent value) {}
21 |
22 | void handleKeyDown(RawKeyDownEvent value) {}
23 |
24 | void _handleKey(RawKeyEvent value) {
25 | if (enableKeyListener == false) return;
26 | if (value is RawKeyDownEvent) handleKeyDown(value);
27 | if (value is RawKeyUpEvent) handleKeyUp(value);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/lib/_widgets/no_animation_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class NoAnimationPage extends Page {
4 | final Widget child;
5 | const NoAnimationPage({required this.child, LocalKey? key}) : super(key: key);
6 |
7 | @override
8 | Route createRoute(BuildContext context) {
9 | return PageRouteBuilder(
10 | maintainState: true,
11 | transitionDuration: Duration.zero,
12 | reverseTransitionDuration: Duration.zero,
13 | settings: this,
14 | pageBuilder: (context, animation, animation2) => child);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/lib/_widgets/no_glow_scroll_behavior.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class NoGlowScrollBehavior extends ScrollBehavior {
4 | @override
5 | Widget buildViewportChrome(BuildContext context, Widget child, AxisDirection axisDirection) {
6 | return child;
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/lib/_widgets/positioned_all.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class PositionedAll extends StatelessWidget {
4 | const PositionedAll({Key? key, this.all = 0, required this.child}) : super(key: key);
5 | final Widget child;
6 | final double all;
7 |
8 | @override
9 | Widget build(BuildContext context) => Positioned(left: all, top: all, right: all, bottom: all, child: child);
10 | }
11 |
--------------------------------------------------------------------------------
/lib/_widgets/rando_colored_box.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:random_color/random_color.dart';
3 |
4 | class RandoColoredBox extends StatelessWidget {
5 | const RandoColoredBox({required this.child, Key? key}) : super(key: key);
6 | final Widget child;
7 | @override
8 | Widget build(BuildContext context) {
9 | return Container(
10 | color: RandomColor().randomColor(colorBrightness: ColorBrightness.light),
11 | child: child,
12 | );
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/lib/_widgets/rotation_3d.dart.dart:
--------------------------------------------------------------------------------
1 | //Takes a x,y or z rotation, in degrees, and rotates. Good for spins & 3d flip effects
2 | import 'dart:math';
3 |
4 | import 'package:flutter/material.dart';
5 |
6 | class Rotation3d extends StatelessWidget {
7 | //Degrees to rads constant
8 | static const double degrees2Radians = pi / 180;
9 |
10 | final Widget child;
11 | final double rotationX;
12 | final double rotationY;
13 | final double rotationZ;
14 |
15 | const Rotation3d({Key? key, required this.child, this.rotationX = 0, this.rotationY = 0, this.rotationZ = 0})
16 | : super(key: key);
17 |
18 | @override
19 | Widget build(BuildContext context) {
20 | return Transform(
21 | alignment: FractionalOffset.center,
22 | transform: Matrix4.identity()
23 | ..setEntry(3, 2, 0.001)
24 | ..rotateX(rotationX * degrees2Radians)
25 | ..rotateY(rotationY * degrees2Radians)
26 | ..rotateZ(rotationZ * degrees2Radians),
27 | child: child);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/lib/_widgets/rounded_card.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_folio/_widgets/decorated_container.dart';
3 | import 'package:flutter_folio/styles.dart';
4 |
5 | class RoundedCard extends StatelessWidget {
6 | const RoundedCard({Key? key, required this.child, this.radius}) : super(key: key);
7 | final Widget child;
8 | final double? radius;
9 |
10 | @override
11 | Widget build(BuildContext context) => ClipRRect(
12 | borderRadius: BorderRadius.all(Radius.circular(radius ?? 24)),
13 | child: child,
14 | );
15 | }
16 |
17 | class RoundedBorder extends StatelessWidget {
18 | const RoundedBorder({Key? key, this.color, this.width, this.radius, this.ignorePointer = true, this.child})
19 | : super(key: key);
20 | final Color? color;
21 | final double? width;
22 | final double? radius;
23 | final Widget? child;
24 | final bool ignorePointer;
25 |
26 | @override
27 | Widget build(BuildContext context) {
28 | return IgnorePointer(
29 | ignoring: ignorePointer,
30 | child: DecoratedContainer(
31 | borderRadius: radius ?? Corners.med,
32 | borderColor: color ?? Colors.white,
33 | borderWidth: width ?? Strokes.thin,
34 | child: child ?? Container(),
35 | ),
36 | );
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/lib/_widgets/sized_and_translated.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class SizedAndTranslated extends StatelessWidget {
4 | const SizedAndTranslated({
5 | Key? key,
6 | required this.size,
7 | required this.offset,
8 | required this.child,
9 | this.pivotPoint,
10 | }) : super(key: key);
11 | final Size size;
12 | final Offset offset;
13 | final Widget child;
14 | final Alignment? pivotPoint;
15 |
16 | @override
17 | Widget build(BuildContext context) {
18 | Alignment pivot = pivotPoint ?? const Alignment(.5, .5);
19 | return Transform.translate(
20 | offset: offset,
21 | child: Transform.translate(
22 | offset: Offset(-size.width * pivot.x, -size.height * pivot.y),
23 | child: Align(
24 | alignment: Alignment.topLeft,
25 | child: SizedBox(width: size.width, height: size.height, child: child),
26 | ),
27 | ),
28 | );
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/lib/app_keys.dart:
--------------------------------------------------------------------------------
1 | class AppKeys {
2 | static String cloudinaryCloud = "flutterfoliodemo";
3 | static String cloudinaryPreset = "ujszrrfn";
4 | static String firebaseApiKey = "AIzaSyDIMnzUz9TshIyRSSl7iUpp5QxPhAcL1ZQ";
5 | static String firestoreProjectId = "flutter-folio-demo";
6 | }
7 |
--------------------------------------------------------------------------------
/lib/commands/app/authenticate_user_command.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_folio/_utils/logger.dart';
2 | import 'package:flutter_folio/commands/app/set_current_user_command.dart';
3 | import 'package:flutter_folio/commands/commands.dart';
4 | import 'package:flutter_folio/data/app_user.dart';
5 |
6 | class AuthenticateUserCommand extends BaseAppCommand {
7 | Future run({required String email, required String pass, required bool createNew}) async {
8 | AppUser? user;
9 | try {
10 | // Authenticate user
11 | user = await firebase.signIn(email: email, password: pass, createAccount: createNew);
12 | // If they are new, create a database record to hold their content
13 | if (user != null && createNew) {
14 | user = user.copyWith(documentId: email, firstName: "", lastName: "");
15 | // Set the userId here, so firebase can update this new users data, this is also set in [SetCurrentUserCommand]
16 | firebase.userId = email;
17 | await firebase.addUser(user);
18 | }
19 | log("Authentication complete, user=$user");
20 | // Login??
21 | if (user != null) {
22 | SetCurrentUserCommand().run(user);
23 | return true;
24 | }
25 | } on Exception catch (e) {
26 | log(e.toString());
27 | }
28 | return false;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/lib/commands/app/copy_share_link_command.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/services.dart';
2 | import 'package:flutter_folio/_utils/device_info.dart';
3 | import 'package:flutter_folio/commands/commands.dart';
4 | import 'package:flutter_folio/routing/app_link.dart';
5 | import 'package:flutter_folio/styled_widgets/toaster.dart';
6 | import 'package:share/share.dart';
7 |
8 | import '../../_utils/timed/cooldown.dart';
9 |
10 | class CopyShareLinkCommand extends BaseAppCommand {
11 | String get baseUrl => "https://flutterfolio.com/#";
12 | static CoolDown mobileShareCooldown = CoolDown(const Duration(seconds: 1));
13 |
14 | Future run(String bookId, {String? pageId}) async {
15 | // Form a url using an AppLink
16 | String url = baseUrl +
17 | AppLink(
18 | user: appModel.currentUserEmail,
19 | bookId: bookId,
20 | pageId: pageId,
21 | ).toLocation();
22 |
23 | // Device clipboard
24 | if (DeviceOS.isDesktopOrWeb) {
25 | Toaster.showToast(mainContext, "Share link copied!");
26 | Clipboard.setData(ClipboardData(text: url));
27 | }
28 | // Mobile share sheet
29 | else {
30 | // Put this on a cool-down, prevents a bug in the share api where it can open multiple sheets.
31 | mobileShareCooldown.run(() => Share.share(url));
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/lib/commands/app/save_image_to_disk_command.dart:
--------------------------------------------------------------------------------
1 | import 'package:file_selector/file_selector.dart' as file_selector;
2 | import 'package:file_selector/file_selector.dart';
3 | import 'package:flutter_folio/_utils/device_info.dart';
4 | import 'package:flutter_folio/commands/commands.dart';
5 |
6 | class SaveImageToDiskCommand extends BaseAppCommand {
7 | //TODO: Add support for web https://github.com/flutter/flutter/issues/78142
8 | static bool get canUse => DeviceOS.isDesktop;
9 |
10 | Future run(String url) async {
11 | if (canUse == false) return;
12 | String fileName = url.split("/").last;
13 | final path = await file_selector.getSavePath(acceptedTypeGroups: [
14 | XTypeGroup(label: 'images', extensions: ['jpg', 'jpeg', 'png'])
15 | ], suggestedName: fileName, confirmButtonText: "Save");
16 | print(path);
17 | // if (path != null) {
18 | // final ByteData imageData = await NetworkAssetBundle(Uri.parse(url)).load("");
19 | // final Uint8List bytes = imageData.buffer.asUint8List();
20 | // final file = XFile.fromData(bytes);
21 | // file.saveTo(path);
22 | //}
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/lib/commands/app/save_window_size_command.dart:
--------------------------------------------------------------------------------
1 | import 'package:desktop_window/desktop_window.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter_folio/_utils/device_info.dart';
4 | import 'package:flutter_folio/commands/commands.dart';
5 |
6 | class SaveWindowSizeCommand extends BaseAppCommand {
7 | Future run() async {
8 | // Only save window size to disk on desktop platforms.
9 | if (DeviceOS.isDesktop) {
10 | Size size = await DesktopWindow.getWindowSize();
11 | if (size != appModel.windowSize) {
12 | appModel.windowSize = size;
13 | appModel.scheduleSave();
14 | }
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/lib/commands/app/set_current_user_command.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_folio/_utils/string_utils.dart';
2 | import 'package:flutter_folio/commands/commands.dart';
3 | import 'package:flutter_folio/core_packages.dart';
4 | import 'package:flutter_folio/data/app_user.dart';
5 |
6 | import 'refresh_menubar_command.dart';
7 |
8 | class SetCurrentUserCommand extends BaseAppCommand {
9 | Future run(AppUser? user) async {
10 | log("SetCurrentUserCommand: $user");
11 | // Update appController with new user. If user is null, this acts as a logout command.
12 | firebase.userId = user?.email;
13 | appModel.currentUser = user;
14 | if (StringUtils.isNotEmpty(firebase.userId)) {
15 | AppUser? user = await firebase.getUser();
16 | if (user != null) {
17 | appModel.currentUser = user;
18 | log("User loaded from firebase: ${user.toJson()}");
19 | }
20 | }
21 | // If currentUser is null here, then we've either logged out, or auth failed.
22 | if (appModel.currentUser == null) {
23 | appModel.reset();
24 | booksModel.reset();
25 | }
26 | RefreshMenuBarCommand().run();
27 | appModel.save();
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/lib/commands/app/update_user_command.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_folio/commands/commands.dart';
2 | import 'package:flutter_folio/data/app_user.dart';
3 |
4 | class UpdateUserCommand extends BaseAppCommand {
5 | Future run(AppUser user) async {
6 | if (appModel.currentUser == null) return;
7 | appModel.currentUser = user;
8 | await firebase.setUserData(user);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/lib/commands/books/create_folio_command.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_folio/_utils/string_utils.dart';
2 | import 'package:flutter_folio/_utils/time_utils.dart';
3 | import 'package:flutter_folio/commands/books/refresh_all_books_command.dart';
4 | import 'package:flutter_folio/commands/books/set_current_book_command.dart';
5 | import 'package:flutter_folio/commands/commands.dart';
6 | import 'package:flutter_folio/data/book_data.dart';
7 | import 'package:flutter_folio/styles.dart';
8 | import 'package:uuid/uuid.dart';
9 |
10 | class CreateFolioCommand extends BaseAppCommand {
11 | Future run({String? title, String? desc}) async {
12 | // Create an empty book
13 | ScrapBookData book = ScrapBookData(
14 | documentId: const Uuid().v1(),
15 | title: title ?? "",
16 | desc: desc ?? "",
17 | creationTime: TimeUtils.nowMillis,
18 | lastModifiedTime: TimeUtils.nowMillis,
19 | );
20 | // Send to server
21 | String documentId = await firebase.addBook(book);
22 | if (StringUtils.isNotEmpty(documentId)) {
23 | // Set as selected book once we get the id back. This will change views
24 | SetCurrentBookCommand().run(book.copyWith(documentId: documentId));
25 | // Refresh the book list after a small delay. This allows any transitions to happen before the books lists are updated
26 | Future.delayed(Times.medium, () => RefreshAllBooks().run());
27 | }
28 | return documentId;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/lib/commands/books/create_page_command.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_folio/commands/books/create_placed_scraps_command.dart';
3 | import 'package:flutter_folio/commands/books/update_page_count_command.dart';
4 | import 'package:flutter_folio/commands/commands.dart';
5 | import 'package:flutter_folio/data/book_data.dart';
6 | import 'package:uuid/uuid.dart';
7 |
8 | class CreatePageCommand extends BaseAppCommand {
9 | Future run() async {
10 | ScrapBookData? currentBook = booksModel.currentBook;
11 | List? currentPages = booksModel.currentBookPages;
12 | if (currentBook == null || currentPages == null) return;
13 | // Increment pageCount
14 | int count = await UpdatePageCountCommand().run(currentPages.length + 1);
15 | // Create new page
16 | ScrapPageData newPage = ScrapPageData(
17 | documentId: const Uuid().v1(),
18 | bookId: currentBook.documentId,
19 | title: "Page $count",
20 | desc: "Add a description...",
21 | boxOrder: [],
22 | );
23 |
24 | /// Add page locally
25 | booksModel.currentBookPages = List.from(currentPages)..add(newPage);
26 | booksModel.currentPage ??= newPage;
27 |
28 | /// Add to database
29 | String pageId = await firebase.addPage(newPage);
30 |
31 | // Add a hidden scrap, this sidesteps a bug in firedart regarding empty collections.
32 | ScrapItem emptyScrap = ScrapItem(bookId: newPage.bookId, contentType: ContentType.Hidden, data: "");
33 | await CreatePlacedScrapCommand().run(pageId: pageId, size: Size.zero, scraps: [emptyScrap]);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/lib/commands/books/delete_book_command.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_folio/commands/commands.dart';
3 | import 'package:flutter_folio/data/book_data.dart';
4 | import 'package:flutter_folio/styled_widgets/dialogs/delete_dialog.dart';
5 |
6 | class DeleteBookCommand extends BaseAppCommand {
7 | Future run(ScrapBookData book) async {
8 | // Show dialog
9 | bool doDelete = await showDialog(
10 | context: mainContext,
11 | builder: (_) => DeleteDialog(
12 | title: "Delete Folio",
13 | desc1: "Are you sure you want to permanently\ndelete the selected folio?",
14 | desc2: "\"${book.title}\"",
15 | )) ??
16 | false;
17 | //Delete
18 | if (doDelete) {
19 | // Delete locally right away
20 | booksModel.removeBookById(book.documentId);
21 | // Sent to database
22 | firebase.deleteBook(book);
23 |
24 | while ((booksModel.books?.length ?? 0) > 30) {
25 | final book = booksModel.books!.last;
26 | booksModel.removeBookById(book.documentId);
27 | firebase.deleteBook(book);
28 | }
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/lib/commands/books/delete_page_scrap_command.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_folio/commands/commands.dart';
2 | import 'package:flutter_folio/data/book_data.dart';
3 |
4 | import 'update_book_modified_command.dart';
5 |
6 | class DeletePageScrapCommand extends BaseAppCommand {
7 | Future run(PlacedScrapItem scrap) async {
8 | booksModel.removePageScrapById(scrap.documentId);
9 | UpdateBookModifiedCommand().run(bookId: scrap.bookId);
10 | await firebase.deletePlacedScrap(scrap);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/lib/commands/books/delete_scraps_command.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_folio/_utils/string_utils.dart';
3 | import 'package:flutter_folio/commands/commands.dart';
4 | import 'package:flutter_folio/styled_widgets/dialogs/delete_dialog.dart';
5 |
6 | import 'update_book_modified_command.dart';
7 |
8 | class DeleteScrapsCommand extends BaseAppCommand {
9 | Future run({required String bookId, required List scrapIds}) async {
10 | // Guard against empty ids
11 | if ((scrapIds.isEmpty)) return false;
12 |
13 | // Show dialog
14 | String pluralScraps = StringUtils.pluralize("scrap", scrapIds.length);
15 | bool doDelete = await showDialog(
16 | context: mainContext,
17 | builder: (_) {
18 | return DeleteDialog(
19 | title: "Delete ${scrapIds.length} $pluralScraps?",
20 | desc1: "Are you sure you want to permanently\ndelete the selected $pluralScraps?",
21 | );
22 | }) ??
23 | false;
24 | //Delete
25 | if (doDelete) {
26 | for (final id in scrapIds) {
27 | // Clear local data
28 | booksModel.removeBookScrapById(id);
29 | // Delete from db
30 | firebase.deleteBookScrap(bookId: bookId, scrapId: id);
31 | }
32 | // Mark book as changed
33 | UpdateBookModifiedCommand().run(bookId: bookId);
34 | return true;
35 | }
36 | return false;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/lib/commands/books/refresh_all_books_command.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_folio/commands/commands.dart';
2 | import 'package:flutter_folio/data/book_data.dart';
3 | import 'package:flutter_folio/services/cloudinary/cloud_storage_service.dart';
4 |
5 | class RefreshAllBooks extends BaseAppCommand {
6 | Future?> run() async {
7 | List? allBooks = await firebase.getAllBooks();
8 | if (allBooks != null) {
9 | CloudStorageService.addMaxSizeToUrlList(
10 | allBooks, (s) => s.imageUrl, (s, url) => s.copyWith(imageUrl: url));
11 | booksModel.books = allBooks;
12 | }
13 | return booksModel.books;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/lib/commands/books/refresh_current_book_command.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_folio/commands/commands.dart';
2 | import 'package:flutter_folio/data/book_data.dart';
3 | import 'package:flutter_folio/services/cloudinary/cloud_storage_service.dart';
4 |
5 | class RefreshCurrentBookCommand extends BaseAppCommand {
6 | Future run({bool book = true, bool pages = true, bool scraps = true}) async {
7 | String? bookId = booksModel.currentBookId;
8 | if (bookId == null) return;
9 | List futures = [
10 | if (book)
11 | firebase.getBook(bookId: bookId).then((value) {
12 | if (value == null) return;
13 | booksModel.currentBook = value;
14 | }),
15 | if (pages)
16 | firebase.getAllPages(bookId: bookId).then((value) {
17 | if (value == null) return;
18 | booksModel.currentBookPages = value..removeWhere((p) => p.documentId == "");
19 | }),
20 | if (scraps)
21 | firebase.getAllBookScraps(bookId: bookId).then((value) {
22 | if (value == null) return;
23 | CloudStorageService.addMaxSizeToUrlList(
24 | value,
25 | (s) => s.data,
26 | (s, url) => s.copyWith(data: url),
27 | );
28 | booksModel.currentBookScraps = value;
29 | }),
30 | ];
31 | await Future.wait(futures);
32 | }
33 |
34 | void onlyScraps() => run(book: false, pages: false);
35 | void onlyPages() => run(book: false);
36 | }
37 |
--------------------------------------------------------------------------------
/lib/commands/books/refresh_current_page_command.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_folio/commands/commands.dart';
2 | import 'package:flutter_folio/data/book_data.dart';
3 | import 'package:flutter_folio/services/cloudinary/cloud_storage_service.dart';
4 |
5 | class RefreshCurrentPageCommand extends BaseAppCommand {
6 | Future run() async {
7 | String? bookId = booksModel.currentPage?.bookId;
8 | String? pageId = booksModel.currentPage?.documentId;
9 | if (bookId == null || pageId == null) return;
10 | List futures = [
11 | firebase.getPage(bookId: bookId, pageId: pageId).then((value) {
12 | if (value == null) return;
13 | booksModel.currentPage = value;
14 | }),
15 | firebase.getAllPlacedScraps(bookId: bookId, pageId: pageId).then((value) {
16 | if (value == null) return;
17 | CloudStorageService.addMaxSizeToUrlList(
18 | value, (s) => s.data, (s, url) => s.copyWith(data: url));
19 | booksModel.currentPageScraps = value
20 | ..removeWhere((element) {
21 | return element.documentId == "";
22 | });
23 | }),
24 | ];
25 | await Future.wait(futures);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/lib/commands/books/set_current_book_command.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_folio/_utils/input_utils.dart';
2 | import 'package:flutter_folio/commands/books/refresh_current_book_command.dart';
3 | import 'package:flutter_folio/commands/commands.dart';
4 | import 'package:flutter_folio/data/book_data.dart';
5 |
6 | import 'set_current_page_command.dart';
7 |
8 | class SetCurrentBookCommand extends BaseAppCommand {
9 | Future run(ScrapBookData? book, {bool setInitialPage = true}) async {
10 | booksModel.currentBook = book;
11 | if (book != null) {
12 | // Because TextEditing relies on FocusOut for saving, we have to make sure we focus out before changing books
13 | InputUtils.unFocus();
14 | // Load new book contents
15 | await RefreshCurrentBookCommand().run();
16 | // If we have any pages, set the first one as the current page
17 | bool hasPages = booksModel.currentBookPages?.isNotEmpty ?? false;
18 | if (hasPages) {
19 | ScrapPageData? firstPage = booksModel.currentBookPages?.first;
20 | if (setInitialPage && firstPage != null) {
21 | await SetCurrentPageCommand().run(firstPage);
22 | }
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/lib/commands/books/set_current_page_command.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_folio/_utils/input_utils.dart';
2 | import 'package:flutter_folio/commands/books/refresh_current_page_command.dart';
3 | import 'package:flutter_folio/commands/commands.dart';
4 | import 'package:flutter_folio/data/book_data.dart';
5 |
6 | class SetCurrentPageCommand extends BaseAppCommand {
7 | Future run(ScrapPageData? page) async {
8 | // Because TextEditing relies on FocusOut for saving, we have to make sure we focus out before changing pages
9 | InputUtils.unFocus();
10 | booksModel.currentPage = page;
11 | if (page != null) {
12 | RefreshCurrentPageCommand().run();
13 | }
14 | return booksModel.currentPage;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/lib/commands/books/shift_placed_scraps_sort_order_command.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_folio/commands/commands.dart';
2 | import 'package:flutter_folio/data/book_data.dart';
3 | import 'package:flutter_folio/styled_widgets/toaster.dart';
4 |
5 | class ShiftPlacedScrapsSortOrderCommand extends BaseAppCommand {
6 | Future run(int indexesToShift, PlacedScrapItem scrapItem) async {
7 | // Fetch the book and try to get the current sortIndex of this page
8 | ScrapPageData? page = booksModel.currentPage;
9 | if (page == null) return;
10 |
11 | page = page.copyWith(
12 | boxOrder: _move(page.boxOrder, scrapItem.documentId, indexesToShift),
13 | );
14 | Toaster.showToast(mainContext, indexesToShift < 0 ? "Sent back" : "Moved forward");
15 | booksModel.replacePage(page);
16 | booksModel.currentPageScraps = List.from(booksModel.currentPageScraps ?? []);
17 | // Update firebase
18 | firebase.setPage(page);
19 | }
20 |
21 | List _move(List existing, String value, int indexesToShift) {
22 | int i = existing.indexOf(value);
23 | // If it doesn't exist in the list, then use the last item or -1, and move from there.
24 | if (i == -1) i = existing.length - 1;
25 | int newIndex = (i + indexesToShift).clamp(0, existing.length - 1).toInt();
26 | // Remove the item if it existed in the list
27 | if (i != -1) {
28 | existing.removeAt(i);
29 | }
30 | // Insert the new item back in at the correct index, or 0 if it didn't previous exist
31 | return existing..insert(newIndex, value);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/lib/commands/books/update_book_command.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_folio/_utils/time_utils.dart';
2 | import 'package:flutter_folio/commands/commands.dart';
3 | import 'package:flutter_folio/data/book_data.dart';
4 |
5 | class UpdateBookCommand extends BaseAppCommand {
6 | Future run(ScrapBookData book) async {
7 | booksModel.replaceBook(book);
8 | await firebase.setBook(book.copyWith(
9 | lastModifiedTime: TimeUtils.nowMillis,
10 | ));
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/lib/commands/books/update_book_modified_command.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_folio/_utils/string_utils.dart';
2 | import 'package:flutter_folio/_utils/time_utils.dart';
3 | import 'package:flutter_folio/commands/commands.dart';
4 | import 'package:flutter_folio/data/book_data.dart';
5 |
6 | class UpdateBookModifiedCommand extends BaseAppCommand {
7 | Future run({String? bookId, ScrapBookData? book}) async {
8 | assert(
9 | StringUtils.isNotEmpty(bookId) || book != null, "You must pass either an id or an instance to this Command.");
10 | // fetch a book, or use the one passed in
11 | if (bookId != null) {
12 | book ??= await firebase.getBook(bookId: bookId);
13 | }
14 | if (book != null) {
15 | book = book.copyWith(lastModifiedTime: TimeUtils.nowMillis);
16 | booksModel.replaceBook(book);
17 | firebase.setBook(book);
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/lib/commands/books/update_current_book_cover_photo_command.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_folio/_utils/time_utils.dart';
2 | import 'package:flutter_folio/commands/commands.dart';
3 | import 'package:flutter_folio/data/book_data.dart';
4 | import 'package:flutter_folio/styled_widgets/toaster.dart';
5 |
6 | class UpdateCurrentBookCoverPhotoCommand extends BaseAppCommand {
7 | Future run(PlacedScrapItem item) async {
8 | // Guard against non-photo content types
9 | if (item.contentType != ContentType.Photo) return;
10 | // Protect against non-changes so the views don't need to check
11 | if (item.data == booksModel.currentBook?.imageUrl) return;
12 | ScrapBookData? book = booksModel.currentBook;
13 | if (book != null) {
14 | book = book.copyWith(
15 | imageUrl: item.data,
16 | lastModifiedTime: TimeUtils.nowMillis,
17 | );
18 | // Update local
19 | booksModel.replaceBook(book);
20 | // Update db
21 | firebase.setBook(book);
22 | Toaster.showToast(mainContext, "Cover photo changed!");
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/lib/commands/books/update_page_command.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_folio/commands/books/update_book_modified_command.dart';
2 | import 'package:flutter_folio/commands/commands.dart';
3 | import 'package:flutter_folio/data/book_data.dart';
4 |
5 | class UpdatePageCommand extends BaseAppCommand {
6 | Future run(ScrapPageData page) async {
7 | booksModel.replacePage(page);
8 | await firebase.setPage(page);
9 | UpdateBookModifiedCommand().run(bookId: page.bookId);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/lib/commands/books/update_page_count_command.dart:
--------------------------------------------------------------------------------
1 | import 'dart:math';
2 |
3 | import 'package:flutter_folio/_utils/time_utils.dart';
4 | import 'package:flutter_folio/commands/books/update_book_modified_command.dart';
5 | import 'package:flutter_folio/commands/commands.dart';
6 | import 'package:flutter_folio/data/book_data.dart';
7 |
8 | class UpdatePageCountCommand extends BaseAppCommand {
9 | Future run(int value, {ScrapBookData? book}) async {
10 | book ??= booksModel.currentBook;
11 | assert(book != null, "UpdatePageCountCommand was called but there is no current book.");
12 | if (book != null) {
13 | value = max(0, value);
14 | ScrapBookData newBook = book.copyWith(
15 | lastModifiedTime: TimeUtils.nowMillis,
16 | pageCount: value,
17 | );
18 | booksModel.replaceBook(newBook);
19 | await firebase.setBook(newBook);
20 | UpdateBookModifiedCommand().run(book: newBook);
21 | }
22 | return value;
23 | }
24 |
25 | Future incrementCurrent() async {
26 | ScrapBookData? book = booksModel.currentBook;
27 | if (book == null) return 0;
28 | return await run(book.pageCount + 1, book: book);
29 | }
30 |
31 | Future decrementCurrent() async {
32 | ScrapBookData? book = booksModel.currentBook;
33 | if (book == null) return 0;
34 | return await run(book.pageCount - 1, book: book);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/lib/commands/books/update_placed_scrap_command.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_folio/_utils/time_utils.dart';
2 | import 'package:flutter_folio/commands/books/update_book_modified_command.dart';
3 | import 'package:flutter_folio/commands/commands.dart';
4 | import 'package:flutter_folio/data/book_data.dart';
5 |
6 | class UpdatePageScrapCommand extends BaseAppCommand {
7 | Future run(PlacedScrapItem scrapItem, {bool localOnly = false}) async {
8 | PlacedScrapItem newScrap = scrapItem.copyWith(lastModifiedTime: TimeUtils.nowMillis);
9 | booksModel.replaceCurrentPageScrap(newScrap);
10 | if (localOnly == false) {
11 | firebase.setPlacedScrap(newScrap);
12 | UpdateBookModifiedCommand().run(bookId: scrapItem.bookId);
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/lib/commands/commands.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_folio/models/app_model.dart';
3 | import 'package:flutter_folio/models/books_model.dart';
4 | import 'package:flutter_folio/services/cloudinary/cloud_storage_service.dart';
5 | import 'package:flutter_folio/services/firebase/firebase_service.dart';
6 | import 'package:flutter_folio/themes.dart';
7 | import 'package:provider/provider.dart';
8 |
9 | BuildContext? _mainContext;
10 | BuildContext get mainContext => _mainContext!;
11 | bool get hasContext => _mainContext != null;
12 |
13 | /// Someone needs to call this so our Commands can access models and services.
14 | void setContext(BuildContext c) {
15 | _mainContext = c;
16 | }
17 |
18 | class BaseAppCommand {
19 | /// Provide quick lookups for the main Models and Services in the App.
20 | T getProvided() {
21 | assert(_mainContext != null, "You must call `setContext(BuildContext)` method before calling Commands.");
22 | return _mainContext!.read();
23 | }
24 |
25 | AppTheme get appTheme => getProvided();
26 |
27 | FirebaseService get firebase => getProvided();
28 | CloudStorageService get cloudStorage => getProvided();
29 | AppModel get appModel => getProvided();
30 | BooksModel get booksModel => getProvided();
31 | }
32 |
--------------------------------------------------------------------------------
/lib/core_packages.dart:
--------------------------------------------------------------------------------
1 | export 'package:darq/darq.dart'; // Extra list sorting methods sortByDescending, etc
2 | export 'package:flutter_folio/_utils/logger.dart';
3 | export 'package:flutter_folio/_utils/logger.dart';
4 | export 'package:flutter_folio/styled_widgets/styled_widgets.dart';
5 | export 'package:flutter_folio/styles.dart';
6 | export 'package:flutter_folio/themes.dart';
7 | export 'package:provider/provider.dart'; // Context extensions for Provider, .watch() etc
8 | export 'package:sized_context/sized_context.dart'; // Shortcut for MediaQuery info, context.sizePx.with, etc
9 |
--------------------------------------------------------------------------------
/lib/data/app_user.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_folio/_utils/string_utils.dart';
2 | import 'package:freezed_annotation/freezed_annotation.dart';
3 |
4 | part 'app_user.freezed.dart';
5 | part 'app_user.g.dart';
6 |
7 | @freezed
8 | class AppUser with _$AppUser {
9 | static String kDefaultImageUrl =
10 | "https://images.unsplash.com/photo-1481627834876-b7833e8f5570?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=50&q=80";
11 | const AppUser._();
12 | factory AppUser({
13 | @Default("") String documentId,
14 | required String email,
15 | required String fireId,
16 | String? firstName,
17 | String? lastName,
18 | String? imageUrl,
19 | }) = _AppUser;
20 |
21 | factory AppUser.fromJson(Map json) => _$AppUserFromJson(json);
22 |
23 | String? getDisplayName() {
24 | String? result = firstName;
25 | if (StringUtils.isNotEmpty(lastName)) result = (result ?? "") + " $lastName";
26 | return result;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/lib/data/app_user.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'app_user.dart';
4 |
5 | // **************************************************************************
6 | // JsonSerializableGenerator
7 | // **************************************************************************
8 |
9 | _$_AppUser _$_$_AppUserFromJson(Map json) {
10 | return _$_AppUser(
11 | documentId: json['documentId'] as String? ?? '',
12 | email: json['email'] as String,
13 | fireId: json['fireId'] as String,
14 | firstName: json['firstName'] as String?,
15 | lastName: json['lastName'] as String?,
16 | imageUrl: json['imageUrl'] as String?,
17 | );
18 | }
19 |
20 | Map _$_$_AppUserToJson(_$_AppUser instance) =>
21 | {
22 | 'documentId': instance.documentId,
23 | 'email': instance.email,
24 | 'fireId': instance.fireId,
25 | 'firstName': instance.firstName,
26 | 'lastName': instance.lastName,
27 | 'imageUrl': instance.imageUrl,
28 | };
29 |
--------------------------------------------------------------------------------
/lib/routing/app_route_parser.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_folio/routing/app_link.dart';
3 |
4 | /// Converts browser location strings to [AppLink], and vice-versa.
5 | /// This leans on [AppLink] to the actual parsing, so this is largely boilerplate.
6 | class AppRouteParser extends RouteInformationParser {
7 | @override
8 | // Take a url bar location, and create an AppLink from it
9 | Future parseRouteInformation(RouteInformation routeInformation) async {
10 | AppLink link = AppLink.fromLocation(routeInformation.location);
11 | //safePrint("parseRouteInfo: ${routeInformation.location} == ${link.toLocation()}");
12 | //safePrint("link.user=${link.user},link.pageId=${link.pageId},link.bookId=${link.bookId},");
13 | return link;
14 | }
15 |
16 | @override
17 | // Convert an applink into a string used for the browser location
18 | RouteInformation restoreRouteInformation(AppLink configuration) {
19 | // Ask the applink to give us a string
20 | String location = configuration.toLocation();
21 | //safePrint("restoreRouteInfo: $location");
22 | // Pass that string back to the OS so it can update the url bar
23 | return RouteInformation(location: location);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/lib/styled_widgets/app_icons.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/foundation.dart';
2 | import 'package:flutter/material.dart';
3 |
4 | enum AppIcons {
5 | add,
6 | camera,
7 | emoji,
8 | image,
9 | link_out,
10 | move_forward,
11 | scraps,
12 | send_backward,
13 | share,
14 | star,
15 | text,
16 | toggle_carousel,
17 | toggle_list,
18 | trashcan,
19 | view,
20 | github,
21 | website
22 | }
23 |
24 | class AppIcon extends StatelessWidget {
25 | final AppIcons icon;
26 | final double size;
27 | final Color color;
28 |
29 | const AppIcon(this.icon, {Key? key, required this.size, required this.color}) : super(key: key);
30 | @override
31 | Widget build(BuildContext context) {
32 | String i = describeEnum(icon).toLowerCase().replaceAll("_", "-");
33 | String path = 'assets/images/icons/' + i + '.png';
34 | //print(path);
35 | return SizedBox(
36 | width: size,
37 | height: size,
38 | child: Center(
39 | child: Image.asset(path, width: size, height: size, color: color, filterQuality: FilterQuality.high),
40 | ),
41 | );
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/lib/styled_widgets/app_logo_text.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_folio/core_packages.dart';
3 |
4 | class AppLogoText extends StatelessWidget {
5 | const AppLogoText({Key? key, this.constraints, this.color}) : super(key: key);
6 | final BoxConstraints? constraints;
7 | final Color? color;
8 |
9 | @override
10 | Widget build(BuildContext context) {
11 | AppTheme theme = context.watch();
12 | Widget img = Image.asset(
13 | "assets/images/logos/flutterfolio-logo.png",
14 | fit: BoxFit.contain,
15 | color: color ?? theme.accent1,
16 | //TODO: Log bug for blurry image filtering on windows
17 | filterQuality: FilterQuality.high,
18 | );
19 | return (constraints == null) ? img : ConstrainedBox(constraints: constraints!, child: img);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/lib/styled_widgets/book_cover_image.dart:
--------------------------------------------------------------------------------
1 | // Wraps a CachedNetworkImage + a fallback placeholder if no image is set.
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter_folio/_utils/string_utils.dart';
4 | import 'package:flutter_folio/_widgets/app_image.dart';
5 | import 'package:flutter_folio/data/book_data.dart';
6 |
7 | /// An image that falls back to a placeholder img
8 | class BookCoverImage extends StatefulWidget {
9 | const BookCoverImage(this.data, {Key? key}) : super(key: key);
10 | final ScrapBookData data;
11 |
12 | @override
13 | _BookCoverImageState createState() => _BookCoverImageState();
14 | }
15 |
16 | class _BookCoverImageState extends State {
17 | @override
18 | Widget build(BuildContext context) {
19 | bool usePlaceholder = StringUtils.isEmpty(widget.data.imageUrl);
20 | if (!usePlaceholder) {
21 | //print(widget.data.imageUrl);
22 | return HostedImage(widget.data.imageUrl, fit: BoxFit.cover);
23 | } else {
24 | return Image.asset("assets/images/empty-background.png", fit: BoxFit.cover);
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/lib/styled_widgets/buttons/styled_share_btn.dart:
--------------------------------------------------------------------------------
1 | import 'package:anchored_popups/anchored_popup_region.dart';
2 | import 'package:anchored_popups/anchored_popups.dart';
3 | import 'package:flutter/material.dart';
4 | import 'package:flutter_folio/commands/app/copy_share_link_command.dart';
5 | import 'package:flutter_folio/core_packages.dart';
6 | import 'package:flutter_folio/data/book_data.dart';
7 |
8 | class StyledSharedBtn extends StatelessWidget {
9 | const StyledSharedBtn({
10 | Key? key,
11 | required this.book,
12 | this.iconColor,
13 | }) : super(key: key);
14 | final Color? iconColor;
15 | final ScrapBookData book;
16 |
17 | @override
18 | Widget build(BuildContext context) {
19 | void _handleSharePressed() {
20 | // Close popup when pressed
21 | AnchoredPopups.of(context)?.hide();
22 | CopyShareLinkCommand().run(book.documentId);
23 | }
24 |
25 | AppTheme theme = context.watch();
26 | return AnchoredPopUpRegion.hover(
27 | //TODO: anchors should be configurable here?
28 | anchor: Alignment.centerRight,
29 | popAnchor: Alignment.centerLeft,
30 | popChild: const StyledTooltip("Copy Share Link", arrowAlignment: Alignment.centerLeft),
31 | child: SimpleBtn(
32 | child: Padding(
33 | padding: EdgeInsets.all(Insets.sm),
34 | child: Icon(Icons.share, color: iconColor ?? theme.surface1),
35 | ),
36 | onPressed: _handleSharePressed));
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/lib/styled_widgets/circle_avatar.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_folio/_widgets/app_image.dart';
3 | import 'package:flutter_folio/_widgets/decorated_container.dart';
4 | import 'package:flutter_folio/core_packages.dart';
5 |
6 | class StyledCircleImage extends StatelessWidget {
7 | const StyledCircleImage({Key? key, required this.url, this.padding}) : super(key: key);
8 |
9 | final EdgeInsets? padding;
10 | final String url;
11 |
12 | @override
13 | Widget build(BuildContext context) {
14 | AppTheme theme = Provider.of(context);
15 | return Padding(
16 | padding: padding ?? EdgeInsets.zero,
17 | child: AspectRatio(
18 | aspectRatio: 1,
19 | child: DecoratedContainer(
20 | clipChild: true,
21 | borderColor: theme.greyWeak,
22 | borderWidth: 2,
23 | borderRadius: 99,
24 | child: HostedImage(url, fit: BoxFit.cover),
25 | ),
26 | ),
27 | );
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/lib/styled_widgets/context_menus/app_context_menu.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | import 'package:context_menus/context_menus.dart';
4 | import 'package:flutter/foundation.dart';
5 | import 'package:flutter/material.dart';
6 | import 'package:flutter_folio/commands/app/set_current_user_command.dart';
7 | import 'package:flutter_folio/core_packages.dart';
8 | import 'package:flutter_folio/models/app_model.dart';
9 |
10 | class AppContextMenu extends StatefulWidget {
11 | const AppContextMenu({Key? key}) : super(key: key);
12 |
13 | @override
14 | _AppContextMenuState createState() => _AppContextMenuState();
15 | }
16 |
17 | class _AppContextMenuState extends State with ContextMenuStateMixin {
18 | void _handleSignoutPressed() => SetCurrentUserCommand().run(null);
19 |
20 | @override
21 | Widget build(BuildContext context) {
22 | bool isLoggedIn = context.select((AppModel am) => am.isAuthenticated);
23 | return cardBuilder(
24 | context,
25 | [
26 | if (isLoggedIn) ...[
27 | buttonBuilder(context,
28 | ContextMenuButtonConfig("Logout", onPressed: () => handlePressed(context, _handleSignoutPressed))),
29 | ],
30 | if (kIsWeb == false) ...[
31 | buttonBuilder(
32 | context, ContextMenuButtonConfig("Exit Application", onPressed: () => handlePressed(context, exit(0)))),
33 | ],
34 | ],
35 | );
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/lib/styled_widgets/context_menus/context_menu_widgets.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_folio/core_packages.dart';
3 |
4 | class ContextMenuIcon extends StatelessWidget {
5 | final AppIcons icon;
6 | final Color? color;
7 |
8 | const ContextMenuIcon({Key? key, required this.icon, this.color}) : super(key: key);
9 | @override
10 | Widget build(BuildContext context) {
11 | AppTheme theme = context.watch();
12 | return AppIcon(icon, size: 14, color: color ?? theme.greyStrong);
13 | }
14 | }
15 |
16 | class ContextMenuIconHovered extends StatelessWidget {
17 | const ContextMenuIconHovered({Key? key, required this.icon}) : super(key: key);
18 | final AppIcons icon;
19 | @override
20 | Widget build(BuildContext context) {
21 | AppTheme theme = context.watch();
22 | return ContextMenuIcon(icon: icon, color: theme.surface1);
23 | }
24 | }
25 |
26 | class ContextDivider extends StatelessWidget {
27 | const ContextDivider({Key? key}) : super(key: key);
28 |
29 | @override
30 | Widget build(BuildContext context) {
31 | AppTheme theme = context.watch();
32 | return Padding(
33 | padding: const EdgeInsets.symmetric(vertical: 2),
34 | child: Divider(color: theme.greyWeak, height: .5),
35 | );
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/lib/styled_widgets/context_menus/styled_context_menu_overlay.dart:
--------------------------------------------------------------------------------
1 | import 'package:context_menus/context_menus.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter_folio/core_packages.dart';
4 | import 'package:flutter_folio/styled_widgets/context_menus/context_menu_widgets.dart';
5 |
6 | class StyledContextMenuOverlay extends StatelessWidget {
7 | const StyledContextMenuOverlay({Key? key, required this.child}) : super(key: key);
8 | final Widget child;
9 |
10 | @override
11 | Widget build(BuildContext context) {
12 | AppTheme theme = context.watch();
13 | return ContextMenuOverlay(
14 | dividerBuilder: (_) => const ContextDivider(),
15 | cardBuilder: (_, children) => ContextMenuCard(children: children, padding: EdgeInsets.zero),
16 | buttonStyle: ContextMenuButtonStyle(
17 | textStyle: TextStyles.body2,
18 | shortcutTextStyle: TextStyles.body2.copyWith(color: theme.grey),
19 | hoverFgColor: theme.surface1,
20 | hoverBgColor: theme.accent1,
21 | ),
22 | child: child,
23 | );
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/lib/styled_widgets/dialogs/base_dialog.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_folio/core_packages.dart';
3 |
4 | class BaseStyledDialog extends StatelessWidget {
5 | final Color? bgColor;
6 | final EdgeInsets? padding;
7 | final Widget child;
8 |
9 | const BaseStyledDialog({Key? key, required this.child, this.bgColor, this.padding}) : super(key: key);
10 | @override
11 | Widget build(BuildContext context) {
12 | var theme = context.watch();
13 | return Dialog(
14 | backgroundColor: bgColor ?? theme.bg1,
15 | elevation: 0,
16 | child: ConstrainedBox(
17 | constraints: const BoxConstraints(minWidth: 280),
18 | child: Padding(
19 | padding: padding ?? EdgeInsets.symmetric(vertical: Insets.lg),
20 | child: IntrinsicWidth(child: child),
21 | ),
22 | ),
23 | );
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/lib/styled_widgets/dialogs/edit_text_dialog.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_folio/commands/books/update_placed_scrap_command.dart';
3 | import 'package:flutter_folio/data/book_data.dart';
4 | import 'package:flutter_folio/styled_widgets/dialogs/base_dialog.dart';
5 | import 'package:flutter_folio/styled_widgets/labeled_text_input.dart';
6 | import 'package:flutter_folio/styles.dart';
7 |
8 | class ScrapTextEditorDialog extends StatelessWidget {
9 | const ScrapTextEditorDialog(this.item, {Key? key}) : super(key: key);
10 | final PlacedScrapItem item;
11 |
12 | @override
13 | Widget build(BuildContext context) {
14 | void _handleSubmit(String value) => Navigator.pop(context);
15 |
16 | void _handleTextChanged(String value) {
17 | UpdatePageScrapCommand().run(item.copyWith(data: value));
18 | }
19 |
20 | return BaseStyledDialog(
21 | child: Column(
22 | mainAxisSize: MainAxisSize.min,
23 | children: [
24 | Padding(
25 | padding: EdgeInsets.symmetric(horizontal: Insets.lg),
26 | child: LabeledTextInput(
27 | label: "Edit Text",
28 | text: item.data,
29 | onChanged: _handleTextChanged,
30 | onSubmit: _handleSubmit,
31 | ),
32 | ),
33 | ],
34 | ),
35 | );
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/lib/styled_widgets/emoji.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/foundation.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter_svg/flutter_svg.dart';
4 |
5 | enum Emojis {
6 | beers,
7 | checkmark,
8 | confetti,
9 | cool,
10 | crying_face,
11 | dizzy_face,
12 | exclamation_question,
13 | fire,
14 | folded_hands,
15 | heart_eyes,
16 | hundred_points,
17 | kissing_face,
18 | location_pin,
19 | musical_notes,
20 | palms_up,
21 | pile_of_poo,
22 | red_heart,
23 | shooting_star,
24 | smiling_eyes,
25 | sparkles,
26 | squinting_face,
27 | sunglasses_face,
28 | tears_of_joy_face,
29 | warning_sign,
30 | }
31 |
32 | class Emoji extends StatelessWidget {
33 | final Emojis? emoji;
34 | final double? size;
35 |
36 | const Emoji(this.emoji, {Key? key, this.size}) : super(key: key);
37 | @override
38 | Widget build(BuildContext context) {
39 | if (emoji == null) return Container();
40 | String fileName = describeEnum(emoji!).toLowerCase().replaceAll("_", "-");
41 | String path = 'assets/images/emoji/' + fileName + '.svg';
42 | return SvgPicture.asset(path, width: size, height: size);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/lib/styled_widgets/material_icon.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_folio/core_packages.dart';
3 |
4 | class MaterialIcon extends StatelessWidget {
5 | final IconData icon;
6 | final double size;
7 | final Color? color;
8 |
9 | const MaterialIcon(this.icon, {Key? key, this.size = 20, this.color}) : super(key: key);
10 | @override
11 | Widget build(BuildContext context) {
12 | AppTheme theme = context.watch();
13 | return Icon(icon, size: size, color: color ?? theme.greyStrong);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/lib/styled_widgets/shadowed_box.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_folio/_widgets/decorated_container.dart';
3 | import 'package:flutter_folio/core_packages.dart';
4 |
5 | class ShadowedBg extends StatelessWidget {
6 | const ShadowedBg(this.color, {Key? key, this.ignorePointer = true}) : super(key: key);
7 | final Color color;
8 | final bool ignorePointer;
9 |
10 | @override
11 | Widget build(BuildContext context) {
12 | return IgnorePointer(
13 | ignoring: ignorePointer,
14 | child: DecoratedContainer(color: color, shadows: Shadows.universal),
15 | );
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/lib/styled_widgets/styled_bottom_sheet.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_folio/_widgets/decorated_container.dart';
3 | import 'package:flutter_folio/core_packages.dart';
4 |
5 | class StyledBottomSheet extends StatelessWidget {
6 | const StyledBottomSheet({required this.child, Key? key}) : super(key: key);
7 |
8 | final Widget child;
9 |
10 | @override
11 | Widget build(BuildContext context) {
12 | AppTheme theme = context.watch();
13 | return Container(
14 | decoration: BoxDecoration(
15 | borderRadius: const BorderRadius.vertical(top: Corners.medRadius, bottom: Radius.zero),
16 | color: theme.surface1,
17 | ),
18 | child: Column(children: [
19 | VSpace.sm,
20 |
21 | /// Drag Handle
22 | DecoratedContainer(
23 | width: 96,
24 | height: 4,
25 | borderRadius: Corners.med,
26 | color: theme.greyWeak,
27 | ),
28 |
29 | /// Content
30 | child
31 | ]),
32 | );
33 | }
34 | }
35 |
36 | Future showStyledBottomSheet(BuildContext context, {required Widget child}) async {
37 | return showModalBottomSheet(
38 | isScrollControlled: true,
39 | context: context,
40 | shape: const RoundedRectangleBorder(
41 | borderRadius: BorderRadius.vertical(top: Corners.medRadius, bottom: Radius.zero),
42 | ),
43 | builder: (BuildContext context) {
44 | return Wrap(
45 | children: [
46 | StyledBottomSheet(child: child),
47 | ],
48 | );
49 | });
50 | }
51 |
--------------------------------------------------------------------------------
/lib/styled_widgets/styled_load_spinner.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_folio/core_packages.dart';
3 |
4 | class StyledLoadSpinner extends StatelessWidget {
5 | const StyledLoadSpinner({Key? key}) : super(key: key);
6 |
7 | @override
8 | Widget build(BuildContext context) {
9 | AppTheme theme = context.watch();
10 | return SizedBox(
11 | width: 24,
12 | height: 24,
13 | child: CircularProgressIndicator(
14 | backgroundColor: theme.greyWeak,
15 | valueColor: AlwaysStoppedAnimation(theme.greyStrong),
16 | ));
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/lib/styled_widgets/styled_page_scaffold.dart:
--------------------------------------------------------------------------------
1 | import 'package:context_menus/context_menus.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter_folio/_utils/input_utils.dart';
4 | import 'package:flutter_folio/core_packages.dart';
5 |
6 | class StyledPageScaffold extends StatelessWidget {
7 | const StyledPageScaffold({Key? key, required this.body}) : super(key: key);
8 | final Widget body;
9 |
10 | @override
11 | Widget build(BuildContext context) {
12 | AppTheme theme = context.watch();
13 | //TODO: Add a FocusTraversalGroup() when this bug is addressed:https://github.com/flutter/flutter/issues/74656
14 | return GestureDetector(
15 | onTap: InputUtils.unFocus,
16 | child: Scaffold(
17 | backgroundColor: theme.bg1,
18 | body: Stack(
19 | children: [
20 | ContextMenuRegion(child: Container(), contextMenu: const AppContextMenu()),
21 | body,
22 | ],
23 | ),
24 | ),
25 | );
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/lib/styled_widgets/styled_scrollbar.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_folio/core_packages.dart';
3 | import 'package:flutter_folio/models/app_model.dart';
4 |
5 | class StyledScrollbar extends StatelessWidget {
6 | const StyledScrollbar({
7 | Key? key,
8 | required this.child,
9 | required this.controller,
10 | this.padding,
11 | this.enabled = true,
12 | }) : super(key: key);
13 | final bool enabled;
14 | final Widget child;
15 | final ScrollController controller;
16 | final EdgeInsets? padding;
17 |
18 | @override
19 | Widget build(BuildContext context) {
20 | bool touchMode = context.select((AppModel m) => m.enableTouchMode);
21 | Widget paddedChild = Padding(padding: padding ?? EdgeInsets.only(right: Insets.lg), child: child);
22 | return enabled
23 | ? Scrollbar(
24 | controller: controller,
25 | radius: Corners.smRadius,
26 | thickness: touchMode ? 6 : 10,
27 | showTrackOnHover: false,
28 | isAlwaysShown: touchMode == false,
29 | child: paddedChild,
30 | )
31 | : paddedChild;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/lib/styled_widgets/styled_spacers.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:flutter_folio/core_packages.dart';
3 |
4 | class VSpace extends StatelessWidget {
5 | final double size;
6 |
7 | const VSpace(this.size, {Key? key}) : super(key: key);
8 |
9 | @override
10 | Widget build(BuildContext context) => SizedBox(height: size);
11 |
12 | static VSpace get xs => VSpace(Insets.xs);
13 | static VSpace get sm => VSpace(Insets.sm);
14 | static VSpace get med => VSpace(Insets.med);
15 | static VSpace get lg => VSpace(Insets.lg);
16 | static VSpace get xl => VSpace(Insets.xl);
17 | }
18 |
19 | class HSpace extends StatelessWidget {
20 | final double size;
21 |
22 | const HSpace(this.size, {Key? key}) : super(key: key);
23 |
24 | @override
25 | Widget build(BuildContext context) => SizedBox(width: size);
26 |
27 | static HSpace get xs => HSpace(Insets.xs);
28 | static HSpace get sm => HSpace(Insets.sm);
29 | static HSpace get med => HSpace(Insets.med);
30 | static HSpace get lg => HSpace(Insets.lg);
31 | static HSpace get xl => HSpace(Insets.xl);
32 | }
33 |
--------------------------------------------------------------------------------
/lib/styled_widgets/styled_spinner.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_folio/core_packages.dart';
3 |
4 | class LoadingIndicator extends StatelessWidget {
5 | const LoadingIndicator({Key? key, this.size = 30}) : super(key: key);
6 | final double size;
7 |
8 | @override
9 | Widget build(BuildContext context) {
10 | return Container(
11 | alignment: Alignment.center,
12 | child: Text(
13 | "Fetching data, please wait...",
14 | style: TextStyles.caption,
15 | )
16 | //child: SizedBox(width: size, height: size, child: CircularProgressIndicator()),
17 | );
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/lib/styled_widgets/styled_widgets.dart:
--------------------------------------------------------------------------------
1 | export 'app_icons.dart';
2 | export 'app_logo_text.dart';
3 | export 'book_cover_image.dart';
4 | export 'buttons/raw_styled_btn.dart';
5 | export 'buttons/styled_buttons.dart';
6 | export 'buttons/styled_share_btn.dart';
7 | export 'circle_avatar.dart';
8 | export 'context_menus/app_context_menu.dart';
9 | export 'context_menus/book_context_menu.dart';
10 | export 'context_menus/scrap_context_menu.dart';
11 | export 'context_menus/styled_context_menu_overlay.dart';
12 | export 'dialogs/base_dialog.dart';
13 | export 'dialogs/delete_dialog.dart';
14 | export 'emoji.dart';
15 | export 'glass_cards.dart';
16 | export 'inline_text_editor.dart';
17 | export 'labeled_text_input.dart';
18 | export 'material_icon.dart';
19 | export 'shadowed_box.dart';
20 | export 'styled_bottom_sheet.dart';
21 | export 'styled_load_spinner.dart';
22 | export 'styled_page_scaffold.dart';
23 | export 'styled_scrollbar.dart';
24 | export 'styled_spacers.dart';
25 | export 'styled_spinner.dart';
26 | export 'styled_spinner.dart';
27 | export 'styled_toggle_switch.dart';
28 | export 'styled_tooltip.dart';
29 | export 'toaster.dart';
30 | export 'ui_text.dart';
31 |
--------------------------------------------------------------------------------
/lib/styled_widgets/toaster.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_folio/core_packages.dart';
3 |
4 | class Toaster {
5 | static void showToast(BuildContext context, String content) {
6 | AppTheme theme = context.read();
7 | TextStyle textStyle = TextStyles.body2.copyWith(color: theme.inverseTextColor);
8 | ScaffoldMessenger.of(context).clearSnackBars();
9 | ScaffoldMessenger.of(context).showSnackBar(
10 | SnackBar(
11 | behavior: SnackBarBehavior.floating,
12 | duration: const Duration(milliseconds: 1700),
13 | content: Container(
14 | padding: EdgeInsets.all(Insets.sm),
15 | child: Text(content, style: textStyle),
16 | ),
17 | ),
18 | );
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/lib/styled_widgets/ui_text.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | // Extends SelectableText, providing default web-like behavior of a non-focuseable but selectable text region
4 | class UiText extends StatefulWidget {
5 | const UiText({Key? key, this.style, this.text, this.span}) : super(key: key);
6 | final String? text;
7 | final TextSpan? span;
8 | final TextStyle? style;
9 |
10 | @override
11 | _UiTextState createState() => _UiTextState();
12 | }
13 |
14 | class _UiTextState extends State {
15 | final FocusNode _focusNode = FocusNode(skipTraversal: true);
16 | @override
17 | Widget build(BuildContext context) {
18 | if (widget.span != null) {
19 | return SelectableText.rich(widget.span!, style: widget.style, focusNode: _focusNode);
20 | } else {
21 | return SelectableText(widget.text ?? "", style: widget.style, focusNode: _focusNode);
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/lib/views/editor_page/scrap_popup_editor/scrap_popup_panel_rotation.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:flutter_folio/core_packages.dart';
3 |
4 | import 'scrap_popup_editor.dart';
5 |
6 | class ScrapPopupPanelRotation extends StatelessWidget {
7 | final bool isOpen;
8 | final double degrees;
9 | final void Function(double value) onDegreesChanged;
10 | const ScrapPopupPanelRotation({Key? key, required this.isOpen, required this.degrees, required this.onDegreesChanged})
11 | : super(key: key);
12 | @override
13 | Widget build(BuildContext context) {
14 | return Stack(
15 | fit: StackFit.expand,
16 | children: [
17 | /// Closed State
18 | if (!isOpen) ...[
19 | const PopPanelIconBtn(),
20 | ] else ...[
21 | SingleChildScrollView(
22 | scrollDirection: Axis.horizontal,
23 | child: SingleChildScrollView(
24 | child: SizedBox(
25 | width: 300 - Insets.sm * 2,
26 | child: Column(
27 | children: [
28 | const PanelHeader(label: "Rotation", showBackArrow: false),
29 | SizedBox(
30 | width: 280,
31 | child: CupertinoSlider(min: -180, max: 180, value: degrees, onChanged: onDegreesChanged)),
32 | VSpace(Insets.sm),
33 | Text("${degrees.round()}", style: TextStyles.body3),
34 | ],
35 | ),
36 | ),
37 | ),
38 | ),
39 | ]
40 | ],
41 | );
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/lib/views/editor_page/scrapboard/scrap_data.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_folio/_utils/easy_notifier.dart';
3 |
4 | class ScrapData extends EasyNotifier {
5 | ScrapData(this.data, {this.aspect = 1});
6 |
7 | Offset _offset = Offset.zero;
8 | Offset get offset => _offset;
9 | set offset(Offset value) => notify(() => _offset = value);
10 |
11 | Size _size = const Size(100, 100);
12 | Size get size => _size;
13 | set size(Size value) => notify(() => _size = value);
14 |
15 | double _rot = 1;
16 | double get rot => _rot;
17 | set rot(double value) => notify(() => _rot = value);
18 |
19 | double aspect;
20 | T data;
21 | }
22 |
--------------------------------------------------------------------------------
/lib/views/home_page/book_cover/book_cover_small.dart:
--------------------------------------------------------------------------------
1 | import 'package:animate_do/animate_do.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter_folio/core_packages.dart';
4 | import 'package:flutter_folio/data/book_data.dart';
5 |
6 | class SmallBookCover extends StatelessWidget {
7 | const SmallBookCover(this.book, {Key? key, this.topTitle = false}) : super(key: key);
8 | final ScrapBookData book;
9 | final bool topTitle;
10 |
11 | @override
12 | Widget build(BuildContext context) {
13 | AppTheme theme = context.watch();
14 | return FadeInUp(
15 | delay: Times.medium,
16 | child: Text(
17 | book.title,
18 | style: TextStyles.h3.copyWith(color: theme.surface1),
19 | ),
20 | );
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/lib/views/scrap_pile_picker/selectable_scrap_btn.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_folio/_utils/string_utils.dart';
3 | import 'package:flutter_folio/_widgets/app_image.dart';
4 | import 'package:flutter_folio/_widgets/decorated_container.dart';
5 | import 'package:flutter_folio/core_packages.dart';
6 | import 'scrap_pile_picker.dart';
7 |
8 | class SelectableScrapBtn extends StatelessWidget {
9 | const SelectableScrapBtn({Key? key, required this.img, required this.onPressed, this.isSelected = false})
10 | : super(key: key);
11 |
12 | final VoidCallback onPressed;
13 | final String img;
14 | final bool isSelected;
15 |
16 | @override
17 | Widget build(BuildContext context) {
18 | AppTheme theme = context.watch();
19 | if (StringUtils.isEmpty(img)) {
20 | return const Center(child: StyledLoadSpinner());
21 | }
22 | return Stack(
23 | children: [
24 | Padding(
25 | padding: EdgeInsets.all(Insets.xs),
26 | child: GridBtn(
27 | onPressed: onPressed,
28 | bgColor: theme.greyStrong,
29 | child: HostedImage(
30 | img,
31 | fit: BoxFit.contain,
32 | )),
33 | ),
34 | if (isSelected) ...[
35 | DecoratedContainer(borderWidth: 2, borderColor: theme.focus, borderRadius: Corners.lg, ignorePointer: true),
36 | ],
37 | ],
38 | );
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/linux/.gitignore:
--------------------------------------------------------------------------------
1 | flutter/ephemeral
2 |
--------------------------------------------------------------------------------
/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 |
14 | void fl_register_plugins(FlPluginRegistry* registry) {
15 | g_autoptr(FlPluginRegistrar) bitsdojo_window_linux_registrar =
16 | fl_plugin_registry_get_registrar_for_plugin(registry, "BitsdojoWindowPlugin");
17 | bitsdojo_window_plugin_register_with_registrar(bitsdojo_window_linux_registrar);
18 | g_autoptr(FlPluginRegistrar) desktop_window_registrar =
19 | fl_plugin_registry_get_registrar_for_plugin(registry, "DesktopWindowPlugin");
20 | desktop_window_plugin_register_with_registrar(desktop_window_registrar);
21 | g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
22 | fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
23 | file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
24 | g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
25 | fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
26 | url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
27 | }
28 |
--------------------------------------------------------------------------------
/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/flutter/generated_plugins.cmake:
--------------------------------------------------------------------------------
1 | #
2 | # Generated file, do not edit.
3 | #
4 |
5 | list(APPEND FLUTTER_PLUGIN_LIST
6 | bitsdojo_window_linux
7 | desktop_window
8 | file_selector_linux
9 | url_launcher_linux
10 | )
11 |
12 | set(PLUGIN_BUNDLED_LIBRARIES)
13 |
14 | foreach(plugin ${FLUTTER_PLUGIN_LIST})
15 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})
16 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
17 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $)
18 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
19 | endforeach(plugin)
20 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/macos/.gitignore:
--------------------------------------------------------------------------------
1 | # Flutter-related
2 | **/Flutter/ephemeral/
3 | **/Pods/
4 |
5 | # Xcode-related
6 | **/xcuserdata/
7 |
--------------------------------------------------------------------------------
/macos/Flutter/Flutter-Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
2 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
3 | #include "ephemeral/Flutter-Generated.xcconfig"
4 |
--------------------------------------------------------------------------------
/macos/Flutter/Flutter-Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
2 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
3 | #include "ephemeral/Flutter-Generated.xcconfig"
4 |
--------------------------------------------------------------------------------
/macos/Flutter/GeneratedPluginRegistrant.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Generated file. Do not edit.
3 | //
4 |
5 | import FlutterMacOS
6 | import Foundation
7 |
8 | import bitsdojo_window_macos
9 | import cloud_firestore
10 | import desktop_window
11 | import file_selector_macos
12 | import firebase_auth
13 | import firebase_core
14 | import path_provider_macos
15 | import shared_preferences_macos
16 | import sqflite
17 | import url_launcher_macos
18 |
19 | func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
20 | BitsdojoWindowPlugin.register(with: registry.registrar(forPlugin: "BitsdojoWindowPlugin"))
21 | FLTFirebaseFirestorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseFirestorePlugin"))
22 | DesktopWindowPlugin.register(with: registry.registrar(forPlugin: "DesktopWindowPlugin"))
23 | FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
24 | FLTFirebaseAuthPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAuthPlugin"))
25 | FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))
26 | PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
27 | SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
28 | SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
29 | UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
30 | }
31 |
--------------------------------------------------------------------------------
/macos/Podfile:
--------------------------------------------------------------------------------
1 | platform :osx, '10.12'
2 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
3 | ENV['COCOAPODS_DISABLE_STATS'] = 'true'
4 |
5 | project 'Runner', {
6 | 'Debug' => :debug,
7 | 'Profile' => :release,
8 | 'Release' => :release,
9 | }
10 |
11 | def flutter_root
12 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__)
13 | unless File.exist?(generated_xcode_build_settings_path)
14 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first"
15 | end
16 |
17 | File.foreach(generated_xcode_build_settings_path) do |line|
18 | matches = line.match(/FLUTTER_ROOT\=(.*)/)
19 | return matches[1].strip if matches
20 | end
21 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\""
22 | end
23 |
24 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
25 |
26 | flutter_macos_podfile_setup
27 |
28 | target 'Runner' do
29 | use_frameworks!
30 | use_modular_headers!
31 |
32 | flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__))
33 | end
34 |
35 | post_install do |installer|
36 | installer.pods_project.targets.each do |target|
37 | flutter_additional_macos_build_settings(target)
38 | end
39 | end
40 |
--------------------------------------------------------------------------------
/macos/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/macos/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/macos/Runner/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import Cocoa
2 | import FlutterMacOS
3 |
4 | @NSApplicationMain
5 | class AppDelegate: FlutterAppDelegate {
6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
7 | return true
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "folio-icon-16.png",
5 | "idiom" : "mac",
6 | "scale" : "1x",
7 | "size" : "16x16"
8 | },
9 | {
10 | "filename" : "folio-icon-32.png",
11 | "idiom" : "mac",
12 | "scale" : "2x",
13 | "size" : "16x16"
14 | },
15 | {
16 | "filename" : "folio-icon-33.png",
17 | "idiom" : "mac",
18 | "scale" : "1x",
19 | "size" : "32x32"
20 | },
21 | {
22 | "filename" : "folio-icon-64.png",
23 | "idiom" : "mac",
24 | "scale" : "2x",
25 | "size" : "32x32"
26 | },
27 | {
28 | "filename" : "folio-icon-128.png",
29 | "idiom" : "mac",
30 | "scale" : "1x",
31 | "size" : "128x128"
32 | },
33 | {
34 | "filename" : "folio-icon-256.png",
35 | "idiom" : "mac",
36 | "scale" : "2x",
37 | "size" : "128x128"
38 | },
39 | {
40 | "filename" : "folio-icon-257.png",
41 | "idiom" : "mac",
42 | "scale" : "1x",
43 | "size" : "256x256"
44 | },
45 | {
46 | "filename" : "folio-icon-512.png",
47 | "idiom" : "mac",
48 | "scale" : "2x",
49 | "size" : "256x256"
50 | },
51 | {
52 | "filename" : "folio-icon-513.png",
53 | "idiom" : "mac",
54 | "scale" : "1x",
55 | "size" : "512x512"
56 | },
57 | {
58 | "filename" : "folio-icon-1024.png",
59 | "idiom" : "mac",
60 | "scale" : "2x",
61 | "size" : "512x512"
62 | }
63 | ],
64 | "info" : {
65 | "author" : "xcode",
66 | "version" : 1
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png
--------------------------------------------------------------------------------
/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png
--------------------------------------------------------------------------------
/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png
--------------------------------------------------------------------------------
/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png
--------------------------------------------------------------------------------
/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png
--------------------------------------------------------------------------------
/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png
--------------------------------------------------------------------------------
/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png
--------------------------------------------------------------------------------
/macos/Runner/Assets.xcassets/AppIcon.appiconset/folio-icon-1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/macos/Runner/Assets.xcassets/AppIcon.appiconset/folio-icon-1024.png
--------------------------------------------------------------------------------
/macos/Runner/Assets.xcassets/AppIcon.appiconset/folio-icon-128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/macos/Runner/Assets.xcassets/AppIcon.appiconset/folio-icon-128.png
--------------------------------------------------------------------------------
/macos/Runner/Assets.xcassets/AppIcon.appiconset/folio-icon-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/macos/Runner/Assets.xcassets/AppIcon.appiconset/folio-icon-16.png
--------------------------------------------------------------------------------
/macos/Runner/Assets.xcassets/AppIcon.appiconset/folio-icon-256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/macos/Runner/Assets.xcassets/AppIcon.appiconset/folio-icon-256.png
--------------------------------------------------------------------------------
/macos/Runner/Assets.xcassets/AppIcon.appiconset/folio-icon-257.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/macos/Runner/Assets.xcassets/AppIcon.appiconset/folio-icon-257.png
--------------------------------------------------------------------------------
/macos/Runner/Assets.xcassets/AppIcon.appiconset/folio-icon-32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/macos/Runner/Assets.xcassets/AppIcon.appiconset/folio-icon-32.png
--------------------------------------------------------------------------------
/macos/Runner/Assets.xcassets/AppIcon.appiconset/folio-icon-33.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/macos/Runner/Assets.xcassets/AppIcon.appiconset/folio-icon-33.png
--------------------------------------------------------------------------------
/macos/Runner/Assets.xcassets/AppIcon.appiconset/folio-icon-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/macos/Runner/Assets.xcassets/AppIcon.appiconset/folio-icon-512.png
--------------------------------------------------------------------------------
/macos/Runner/Assets.xcassets/AppIcon.appiconset/folio-icon-513.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/macos/Runner/Assets.xcassets/AppIcon.appiconset/folio-icon-513.png
--------------------------------------------------------------------------------
/macos/Runner/Assets.xcassets/AppIcon.appiconset/folio-icon-64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/macos/Runner/Assets.xcassets/AppIcon.appiconset/folio-icon-64.png
--------------------------------------------------------------------------------
/macos/Runner/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/macos/Runner/Configs/AppInfo.xcconfig:
--------------------------------------------------------------------------------
1 | // Application-level settings for the Runner target.
2 | //
3 | // This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the
4 | // future. If not, the values below would default to using the project name when this becomes a
5 | // 'flutter create' template.
6 |
7 | // The application's name. By default this is also the title of the Flutter window.
8 | PRODUCT_NAME = FlutterFolio;
9 |
10 | // The application's bundle identifier
11 | PRODUCT_BUNDLE_IDENTIFIER = com.gskinner.travelApp
12 |
13 | // The copyright displayed in application information
14 | PRODUCT_COPYRIGHT = Copyright © 2020 com.gskinner. All rights reserved.
15 |
--------------------------------------------------------------------------------
/macos/Runner/Configs/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "../../Flutter/Flutter-Debug.xcconfig"
2 | #include "Warnings.xcconfig"
3 |
--------------------------------------------------------------------------------
/macos/Runner/Configs/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "../../Flutter/Flutter-Release.xcconfig"
2 | #include "Warnings.xcconfig"
3 |
--------------------------------------------------------------------------------
/macos/Runner/Configs/Warnings.xcconfig:
--------------------------------------------------------------------------------
1 | WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings
2 | GCC_WARN_UNDECLARED_SELECTOR = YES
3 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES
4 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE
5 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES
6 | CLANG_WARN_PRAGMA_PACK = YES
7 | CLANG_WARN_STRICT_PROTOTYPES = YES
8 | CLANG_WARN_COMMA = YES
9 | GCC_WARN_STRICT_SELECTOR_MATCH = YES
10 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES
11 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES
12 | GCC_WARN_SHADOW = YES
13 | CLANG_WARN_UNREACHABLE_CODE = YES
14 |
--------------------------------------------------------------------------------
/macos/Runner/DebugProfile.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.cs.allow-jit
8 |
9 | com.apple.security.files.user-selected.read-only
10 |
11 | com.apple.security.network.client
12 |
13 | com.apple.security.network.server
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/macos/Runner/GoogleService-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CLIENT_ID
6 | 150221469863-fricoi1555bg294u9j0i61tqfvpoo3th.apps.googleusercontent.com
7 | REVERSED_CLIENT_ID
8 | com.googleusercontent.apps.150221469863-fricoi1555bg294u9j0i61tqfvpoo3th
9 | API_KEY
10 | AIzaSyDV0SE4WzZX--KRsdVZeS2pk8_81VrAJMk
11 | GCM_SENDER_ID
12 | 150221469863
13 | PLIST_VERSION
14 | 1
15 | BUNDLE_ID
16 | com.gskinner.flutterfolio
17 | PROJECT_ID
18 | flutter-folio-demo
19 | STORAGE_BUCKET
20 | flutter-folio-demo.appspot.com
21 | IS_ADS_ENABLED
22 |
23 | IS_ANALYTICS_ENABLED
24 |
25 | IS_APPINVITE_ENABLED
26 |
27 | IS_GCM_ENABLED
28 |
29 | IS_SIGNIN_ENABLED
30 |
31 | GOOGLE_APP_ID
32 | 1:150221469863:ios:67573e189598eff0d6c382
33 |
34 |
--------------------------------------------------------------------------------
/macos/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIconFile
10 |
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | $(FLUTTER_BUILD_NAME)
21 | CFBundleVersion
22 | $(FLUTTER_BUILD_NUMBER)
23 | LSApplicationCategoryType
24 | public.app-category.lifestyle
25 | LSMinimumSystemVersion
26 | $(MACOSX_DEPLOYMENT_TARGET)
27 | NSHumanReadableCopyright
28 | $(PRODUCT_COPYRIGHT)
29 | NSMainNibFile
30 | MainMenu
31 | NSPrincipalClass
32 | NSApplication
33 |
34 |
35 |
--------------------------------------------------------------------------------
/macos/Runner/Release.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.files.user-selected.read-only
8 |
9 | com.apple.security.network.client
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/snap/gui/flutter-folio.desktop:
--------------------------------------------------------------------------------
1 | [Desktop Entry]
2 | Name=Flutter Folio
3 | Comment=A scrapbooking app that feels great on all your devices
4 | Exec=flutter-folio
5 | Icon=${SNAP}/meta/gui/flutter-folio.png
6 | Terminal=false
7 | Type=Application
8 | Categories=Lifestyle
9 |
--------------------------------------------------------------------------------
/snap/gui/flutter-folio.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/snap/gui/flutter-folio.png
--------------------------------------------------------------------------------
/snap/snapcraft.yaml:
--------------------------------------------------------------------------------
1 | name: flutter-folio
2 | version: 1.1.4
3 | summary: A scrapbooking app that feels great on all your devices
4 | description: Make your digital scrapbooks with Flutter Folio! You can upload photos from your phone, edit them on a desktop or tablet and share them with friends and family on the web.
5 |
6 | confinement: strict
7 | base: core18
8 | grade: stable
9 |
10 | apps:
11 | flutter-folio:
12 | command: flutter_folio
13 | extensions: [flutter-dev]
14 | plugs:
15 | - network
16 | - home
17 |
18 | parts:
19 | flutter-folio:
20 | source: .
21 | plugin: flutter
22 | flutter-target: lib/main.dart
23 |
24 |
--------------------------------------------------------------------------------
/source-assets/app-flutter-folio-no-alpha.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/source-assets/app-flutter-folio-no-alpha.png
--------------------------------------------------------------------------------
/source-assets/app-flutter-folio.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/source-assets/app-flutter-folio.png
--------------------------------------------------------------------------------
/source-assets/flutterfolio.dmgCanvas/Disk Image:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/source-assets/flutterfolio.dmgCanvas/Disk Image
--------------------------------------------------------------------------------
/source-assets/flutterfolio.dmgCanvas/QuickLook/Preview.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/source-assets/flutterfolio.dmgCanvas/QuickLook/Preview.jpg
--------------------------------------------------------------------------------
/web/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/web/favicon.ico
--------------------------------------------------------------------------------
/web/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/web/favicon.png
--------------------------------------------------------------------------------
/web/icons/Icon-192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/web/icons/Icon-192.png
--------------------------------------------------------------------------------
/web/icons/Icon-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/web/icons/Icon-512.png
--------------------------------------------------------------------------------
/web/icons/icon-1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/web/icons/icon-1024.png
--------------------------------------------------------------------------------
/web/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Flutter Folio",
3 | "short_name": "Flutter Folio",
4 | "start_url": ".",
5 | "display": "standalone",
6 | "background_color": "#0175C2",
7 | "theme_color": "#0175C2",
8 | "description": "Flutter Folio is a scrapbooking app that was designed to showcase Flutter’s capabilities to create apps that feel at home on every platform and device",
9 | "orientation": "portrait-primary",
10 | "prefer_related_applications": false,
11 | "icons": [
12 | {
13 | "src": "icons/Icon-192.png",
14 | "sizes": "192x192",
15 | "type": "image/png"
16 | },
17 | {
18 | "src": "icons/Icon-512.png",
19 | "sizes": "512x512",
20 | "type": "image/png"
21 | }
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/windows/.gitignore:
--------------------------------------------------------------------------------
1 | flutter/ephemeral/
2 |
3 | # Visual Studio user-specific files.
4 | *.suo
5 | *.user
6 | *.userosscache
7 | *.sln.docstates
8 |
9 | # Visual Studio build-related files.
10 | x64/
11 | x86/
12 |
13 | # Visual Studio cache files
14 | # files ending in .cache can be ignored
15 | *.[Cc]ache
16 | # but keep track of directories ending in .cache
17 | !*.[Cc]ache/
18 |
--------------------------------------------------------------------------------
/windows/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 |
14 | void RegisterPlugins(flutter::PluginRegistry* registry) {
15 | BitsdojoWindowPluginRegisterWithRegistrar(
16 | registry->GetRegistrarForPlugin("BitsdojoWindowPlugin"));
17 | DesktopWindowPluginRegisterWithRegistrar(
18 | registry->GetRegistrarForPlugin("DesktopWindowPlugin"));
19 | FileSelectorPluginRegisterWithRegistrar(
20 | registry->GetRegistrarForPlugin("FileSelectorPlugin"));
21 | UrlLauncherPluginRegisterWithRegistrar(
22 | registry->GetRegistrarForPlugin("UrlLauncherPlugin"));
23 | }
24 |
--------------------------------------------------------------------------------
/windows/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 RegisterPlugins(flutter::PluginRegistry* registry);
14 |
15 | #endif // GENERATED_PLUGIN_REGISTRANT_
16 |
--------------------------------------------------------------------------------
/windows/flutter/generated_plugins.cmake:
--------------------------------------------------------------------------------
1 | #
2 | # Generated file, do not edit.
3 | #
4 |
5 | list(APPEND FLUTTER_PLUGIN_LIST
6 | bitsdojo_window_windows
7 | desktop_window
8 | file_selector_windows
9 | url_launcher_windows
10 | )
11 |
12 | set(PLUGIN_BUNDLED_LIBRARIES)
13 |
14 | foreach(plugin ${FLUTTER_PLUGIN_LIST})
15 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin})
16 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
17 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $)
18 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
19 | endforeach(plugin)
20 |
--------------------------------------------------------------------------------
/windows/runner/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.15)
2 | project(runner LANGUAGES CXX)
3 |
4 | add_executable(${BINARY_NAME} WIN32
5 | "flutter_window.cpp"
6 | "main.cpp"
7 | "run_loop.cpp"
8 | "utils.cpp"
9 | "win32_window.cpp"
10 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
11 | "Runner.rc"
12 | "runner.exe.manifest"
13 | )
14 | apply_standard_settings(${BINARY_NAME})
15 | target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX")
16 | target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)
17 | target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
18 | add_dependencies(${BINARY_NAME} flutter_assemble)
19 |
--------------------------------------------------------------------------------
/windows/runner/flutter_window.h:
--------------------------------------------------------------------------------
1 | #ifndef RUNNER_FLUTTER_WINDOW_H_
2 | #define RUNNER_FLUTTER_WINDOW_H_
3 |
4 | #include
5 | #include
6 |
7 | #include
8 |
9 | #include "run_loop.h"
10 | #include "win32_window.h"
11 |
12 | // A window that does nothing but host a Flutter view.
13 | class FlutterWindow : public Win32Window {
14 | public:
15 | // Creates a new FlutterWindow driven by the |run_loop|, hosting a
16 | // Flutter view running |project|.
17 | explicit FlutterWindow(RunLoop* run_loop,
18 | const flutter::DartProject& project);
19 | virtual ~FlutterWindow();
20 |
21 | protected:
22 | // Win32Window:
23 | bool OnCreate() override;
24 | void OnDestroy() override;
25 | LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam,
26 | LPARAM const lparam) noexcept override;
27 |
28 | private:
29 | // The run loop driving events for this window.
30 | RunLoop* run_loop_;
31 |
32 | // The project to run.
33 | flutter::DartProject project_;
34 |
35 | // The Flutter instance hosted by this window.
36 | std::unique_ptr flutter_controller_;
37 | };
38 |
39 | #endif // RUNNER_FLUTTER_WINDOW_H_
40 |
--------------------------------------------------------------------------------
/windows/runner/main.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | #include "flutter_window.h"
6 | #include "run_loop.h"
7 | #include "utils.h"
8 |
9 | #include
10 | auto bdw = bitsdojo_window_configure(BDW_CUSTOM_FRAME | BDW_HIDE_ON_STARTUP);
11 |
12 | int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
13 | _In_ wchar_t *command_line, _In_ int show_command) {
14 | // Attach to console when present (e.g., 'flutter run') or create a
15 | // new console when running with a debugger.
16 | if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {
17 | CreateAndAttachConsole();
18 | }
19 |
20 | // Initialize COM, so that it is available for use in the library and/or
21 | // plugins.
22 | ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
23 |
24 | RunLoop run_loop;
25 |
26 | flutter::DartProject project(L"data");
27 |
28 | std::vector command_line_arguments =
29 | GetCommandLineArguments();
30 |
31 | project.set_dart_entrypoint_arguments(std::move(command_line_arguments));
32 |
33 | FlutterWindow window(&run_loop, project);
34 | Win32Window::Point origin(10, 10);
35 | Win32Window::Size size(1280, 720);
36 | if (!window.CreateAndShow(L"flutter_folio", origin, size)) {
37 | return EXIT_FAILURE;
38 | }
39 | window.SetQuitOnClose(true);
40 |
41 | run_loop.Run();
42 |
43 | ::CoUninitialize();
44 | return EXIT_SUCCESS;
45 | }
46 |
--------------------------------------------------------------------------------
/windows/runner/resource.h:
--------------------------------------------------------------------------------
1 | //{{NO_DEPENDENCIES}}
2 | // Microsoft Visual C++ generated include file.
3 | // Used by Runner.rc
4 | //
5 | #define IDI_APP_ICON 101
6 |
7 | // Next default values for new objects
8 | //
9 | #ifdef APSTUDIO_INVOKED
10 | #ifndef APSTUDIO_READONLY_SYMBOLS
11 | #define _APS_NEXT_RESOURCE_VALUE 102
12 | #define _APS_NEXT_COMMAND_VALUE 40001
13 | #define _APS_NEXT_CONTROL_VALUE 1001
14 | #define _APS_NEXT_SYMED_VALUE 101
15 | #endif
16 | #endif
17 |
--------------------------------------------------------------------------------
/windows/runner/resources/app_icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gskinnerTeam/flutter-folio/be0cfa2fa54a234d0e91beebda0f88f3c485a850/windows/runner/resources/app_icon.ico
--------------------------------------------------------------------------------
/windows/runner/run_loop.h:
--------------------------------------------------------------------------------
1 | #ifndef RUNNER_RUN_LOOP_H_
2 | #define RUNNER_RUN_LOOP_H_
3 |
4 | #include
5 |
6 | #include
7 | #include
8 |
9 | // A runloop that will service events for Flutter instances as well
10 | // as native messages.
11 | class RunLoop {
12 | public:
13 | RunLoop();
14 | ~RunLoop();
15 |
16 | // Prevent copying
17 | RunLoop(RunLoop const&) = delete;
18 | RunLoop& operator=(RunLoop const&) = delete;
19 |
20 | // Runs the run loop until the application quits.
21 | void Run();
22 |
23 | // Registers the given Flutter instance for event servicing.
24 | void RegisterFlutterInstance(
25 | flutter::FlutterEngine* flutter_instance);
26 |
27 | // Unregisters the given Flutter instance from event servicing.
28 | void UnregisterFlutterInstance(
29 | flutter::FlutterEngine* flutter_instance);
30 |
31 | private:
32 | using TimePoint = std::chrono::steady_clock::time_point;
33 |
34 | // Processes all currently pending messages for registered Flutter instances.
35 | TimePoint ProcessFlutterMessages();
36 |
37 | std::set flutter_instances_;
38 | };
39 |
40 | #endif // RUNNER_RUN_LOOP_H_
41 |
--------------------------------------------------------------------------------
/windows/runner/runner.exe.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PerMonitorV2
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/windows/runner/utils.h:
--------------------------------------------------------------------------------
1 | #ifndef RUNNER_UTILS_H_
2 | #define RUNNER_UTILS_H_
3 |
4 | #include
5 | #include
6 |
7 | // Creates a console for the process, and redirects stdout and stderr to
8 | // it for both the runner and the Flutter library.
9 | void CreateAndAttachConsole();
10 |
11 | // Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string
12 | // encoded in UTF-8. Returns an empty std::string on failure.
13 | std::string Utf8FromUtf16(const wchar_t* utf16_string);
14 |
15 | // Gets the command line arguments passed in as a std::vector,
16 | // encoded in UTF-8. Returns an empty std::vector on failure.
17 | std::vector GetCommandLineArguments();
18 |
19 | #endif // RUNNER_UTILS_H_
20 |
--------------------------------------------------------------------------------