diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..40aaabf
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+.dart_tool/
+.flutter-plugins-dependencies
+.flutter-plugins
+pubspec.lock
\ No newline at end of file
diff --git a/.packages b/.packages
new file mode 100644
index 0000000..de9412b
--- /dev/null
+++ b/.packages
@@ -0,0 +1,169 @@
+# This file is deprecated. Tools should instead consume
+# `.dart_tool/package_config.json`.
+#
+# For more info see: https://dart.dev/go/dot-packages-deprecation
+#
+# Generated by pub on 2022-06-14 17:29:42.696281.
+_fe_analyzer_shared:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/_fe_analyzer_shared-38.0.0/lib/
+analyzer:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/analyzer-3.4.1/lib/
+args:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/args-2.3.1/lib/
+async:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/async-2.8.2/lib/
+auto_size_text:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/auto_size_text-3.0.0/lib/
+boolean_selector:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/boolean_selector-2.1.0/lib/
+build:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/build-2.3.0/lib/
+build_config:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/build_config-1.0.0/lib/
+cached_network_image:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/cached_network_image-3.1.0+1/lib/
+cached_network_image_platform_interface:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/cached_network_image_platform_interface-1.0.0/lib/
+cached_network_image_web:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/cached_network_image_web-1.0.1/lib/
+characters:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/characters-1.2.0/lib/
+charcode:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/charcode-1.3.1/lib/
+checked_yaml:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/checked_yaml-2.0.1/lib/
+chewie:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/chewie-1.2.2/lib/
+chewie_audio:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/chewie_audio-1.2.0/lib/
+clock:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/clock-1.1.0/lib/
+collection:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/collection-1.15.0/lib/
+convert:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/convert-3.0.2/lib/
+crypto:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/crypto-3.0.2/lib/
+csslib:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/csslib-0.17.2/lib/
+cupertino_icons:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/cupertino_icons-1.0.5/lib/
+dart_style:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/dart_style-2.2.3/lib/
+dependency_validator:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/dependency_validator-3.2.0/lib/
+device_info_plus:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/device_info_plus-3.2.4/lib/
+device_info_plus_linux:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/device_info_plus_linux-2.1.1/lib/
+device_info_plus_macos:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/device_info_plus_macos-2.2.3/lib/
+device_info_plus_platform_interface:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/device_info_plus_platform_interface-2.3.0+1/lib/
+device_info_plus_web:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/device_info_plus_web-2.1.0/lib/
+device_info_plus_windows:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/device_info_plus_windows-2.1.1/lib/
+emoji_flag_converter:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/emoji_flag_converter-1.1.0/lib/
+equatable:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/equatable-2.0.3/lib/
+extension:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/extension-0.2.0/lib/
+fake_async:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/fake_async-1.2.0/lib/
+ffi:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/ffi-1.2.1/lib/
+file:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/file-6.1.2/lib/
+fl_chart:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/fl_chart-0.50.6/lib/
+flutter:file:///Users/danieledrisian/flutter/packages/flutter/lib/
+flutter_blurhash:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_blurhash-0.7.0/lib/
+flutter_cache_manager:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_cache_manager-3.3.0/lib/
+flutter_credit_card:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_credit_card-2.0.0/lib/
+flutter_google_places:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_google_places-0.3.0/lib/
+flutter_inappwebview:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_inappwebview-5.4.3+7/lib/
+flutter_layout_grid:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_layout_grid-1.0.6/lib/
+flutter_plugin_android_lifecycle:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_plugin_android_lifecycle-2.0.6/lib/
+flutter_svg:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_svg-0.23.0+1/lib/
+flutter_test:file:///Users/danieledrisian/flutter/packages/flutter_test/lib/
+flutter_web_plugins:file:///Users/danieledrisian/flutter/packages/flutter_web_plugins/lib/
+font_awesome_flutter:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/font_awesome_flutter-10.1.0/lib/
+glob:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/glob-2.0.2/lib/
+google_api_headers:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/google_api_headers-1.3.0/lib/
+google_fonts:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/google_fonts-2.2.0/lib/
+google_maps_flutter:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/google_maps_flutter-2.1.1/lib/
+google_maps_flutter_platform_interface:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/google_maps_flutter_platform_interface-2.2.0/lib/
+google_maps_webservice:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/google_maps_webservice-0.0.20-nullsafety.5/lib/
+graphs:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/graphs-2.1.0/lib/
+html:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/html-0.15.0/lib/
+http:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/http-0.13.4/lib/
+http_parser:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/http_parser-4.0.1/lib/
+internet_file:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/internet_file-1.0.0+2/lib/
+intl:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/intl-0.17.0/lib/
+io:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/io-1.0.3/lib/
+js:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/js-0.6.3/lib/
+json_annotation:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/json_annotation-4.4.0/lib/
+json_path:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/json_path-0.3.1/lib/
+json_serializable:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/json_serializable-6.1.3/lib/
+logging:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/logging-1.0.2/lib/
+mapbox_search:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/mapbox_search-3.0.1+2/lib/
+matcher:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/matcher-0.12.11/lib/
+material_color_utilities:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/material_color_utilities-0.1.3/lib/
+meta:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/meta-1.7.0/lib/
+mime_type:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/mime_type-1.0.0/lib/
+nested:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/nested-1.0.0/lib/
+numerus:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/numerus-1.1.1/lib/
+octo_image:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/octo_image-1.0.2/lib/
+package_config:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/package_config-2.0.2/lib/
+package_info_plus:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/package_info_plus-1.4.2/lib/
+package_info_plus_linux:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/package_info_plus_linux-1.0.5/lib/
+package_info_plus_macos:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/package_info_plus_macos-1.3.0/lib/
+package_info_plus_platform_interface:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/package_info_plus_platform_interface-1.0.2/lib/
+package_info_plus_web:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/package_info_plus_web-1.0.5/lib/
+package_info_plus_windows:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/package_info_plus_windows-1.0.5/lib/
+page_transition:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/page_transition-2.0.4/lib/
+path:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/path-1.8.0/lib/
+path_drawing:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/path_drawing-0.5.1+1/lib/
+path_parsing:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/path_parsing-0.2.1/lib/
+path_provider:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-2.0.11/lib/
+path_provider_android:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_android-2.0.14/lib/
+path_provider_ios:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_ios-2.0.9/lib/
+path_provider_linux:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_linux-2.1.7/lib/
+path_provider_macos:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-2.0.6/lib/
+path_provider_platform_interface:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_platform_interface-2.0.4/lib/
+path_provider_windows:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_windows-2.0.7/lib/
+pdfx:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/pdfx-2.0.1+2/lib/
+pedantic:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/pedantic-1.11.1/lib/
+petitparser:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/petitparser-4.4.0/lib/
+photo_view:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/photo_view-0.13.0/lib/
+platform:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/platform-3.1.0/lib/
+plugin_platform_interface:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/plugin_platform_interface-2.1.2/lib/
+pointer_interceptor:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/pointer_interceptor-0.9.3+2/lib/
+process:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/process-4.2.4/lib/
+provider:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/provider-5.0.0/lib/
+pub_semver:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/pub_semver-2.1.1/lib/
+pubspec_parse:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/pubspec_parse-1.2.0/lib/
+quiver:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/quiver-3.1.0/lib/
+rfc_6901:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/rfc_6901-0.1.1/lib/
+rive:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/rive-0.7.33/lib/
+rxdart:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/rxdart-0.26.0/lib/
+shared_preferences:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/shared_preferences-2.0.11/lib/
+shared_preferences_android:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/shared_preferences_android-2.0.12/lib/
+shared_preferences_ios:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/shared_preferences_ios-2.1.1/lib/
+shared_preferences_linux:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/shared_preferences_linux-2.1.1/lib/
+shared_preferences_macos:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/shared_preferences_macos-2.0.4/lib/
+shared_preferences_platform_interface:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/shared_preferences_platform_interface-2.0.0/lib/
+shared_preferences_web:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/shared_preferences_web-2.0.4/lib/
+shared_preferences_windows:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/shared_preferences_windows-2.1.1/lib/
+simple_gesture_detector:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/simple_gesture_detector-0.2.0/lib/
+sky_engine:file:///Users/danieledrisian/flutter/bin/cache/pkg/sky_engine/lib/
+source_gen:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/source_gen-1.2.2/lib/
+source_helper:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/source_helper-1.3.2/lib/
+source_span:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/source_span-1.8.1/lib/
+sqflite:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/sqflite-2.0.2+1/lib/
+sqflite_common:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/sqflite_common-2.2.1+1/lib/
+stack_trace:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.10.0/lib/
+stream_channel:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/stream_channel-2.1.0/lib/
+stream_transform:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/stream_transform-2.0.0/lib/
+string_scanner:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/string_scanner-1.1.0/lib/
+synchronized:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/synchronized-3.0.0+2/lib/
+table_calendar:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/table_calendar-3.0.5/lib/
+term_glyph:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/term_glyph-1.2.0/lib/
+test_api:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/test_api-0.4.8/lib/
+timeago:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/timeago-3.1.0/lib/
+typed_data:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/typed_data-1.3.0/lib/
+universal_file:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/universal_file-1.0.0/lib/
+universal_platform:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/universal_platform-1.0.0+1/lib/
+url_launcher:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-6.0.15/lib/
+url_launcher_linux:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_linux-2.0.3/lib/
+url_launcher_macos:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_macos-2.0.3/lib/
+url_launcher_platform_interface:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_platform_interface-2.0.5/lib/
+url_launcher_web:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_web-2.0.11/lib/
+url_launcher_windows:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_windows-2.0.2/lib/
+uuid:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/uuid-3.0.6/lib/
+vector_math:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/vector_math-2.1.1/lib/
+video_player:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/video_player-2.2.7/lib/
+video_player_platform_interface:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/video_player_platform_interface-4.2.0/lib/
+video_player_web:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/video_player_web-2.0.10/lib/
+wakelock:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/wakelock-0.5.6/lib/
+wakelock_macos:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/wakelock_macos-0.4.0/lib/
+wakelock_platform_interface:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/wakelock_platform_interface-0.3.0/lib/
+wakelock_web:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/wakelock_web-0.4.0/lib/
+wakelock_windows:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/wakelock_windows-0.2.0/lib/
+watcher:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/watcher-1.0.1/lib/
+webview_flutter:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/webview_flutter-2.8.0/lib/
+webview_flutter_android:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/webview_flutter_android-2.8.11/lib/
+webview_flutter_platform_interface:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/webview_flutter_platform_interface-1.9.1/lib/
+webview_flutter_wkwebview:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/webview_flutter_wkwebview-2.7.5/lib/
+webviewx:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/webviewx-0.2.1/lib/
+win32:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/win32-2.5.2/lib/
+xdg_directories:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/xdg_directories-0.2.0+1/lib/
+xml:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/xml-5.3.1/lib/
+yaml:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/yaml-3.1.1/lib/
+youtube_plyr_iframe:file:///Users/danieledrisian/flutter/.pub-cache/hosted/pub.dartlang.org/youtube_plyr_iframe-2.0.7/lib/
+flutterflow_ui:lib/
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..f906f8f
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,26 @@
+## 0.3.1
+
+- Remove audio player support from the package
+
+## 0.3.0
+
+- Add support for new UI widgets available in current FlutterFlow release
+- Update utility functions to support new FlutterFlow code generation
+- Update dependencies
+
+## 0.2.0
+
+**Breaking changes**
+ - Remove Flutter media plugins such as chewie, video_player, audio_player, google_maps, webview, mapbox, rxdart, shared_preferences
+
+**Features**
+ - Add support for new UI widgets available in current FlutterFlow release
+ - Add generated code for animations support
+
+## 0.1.1
+
+Removed FlutterFlowTheme from package
+
+## 0.1.0+4
+
+Initial beta release
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..1dfb3b9
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,29 @@
+BSD 3-Clause License
+
+Copyright (c) 2024, FlutterFlow
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
index de36594..9d7c9e4 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,101 @@
-## flutterflow-ui
+# FlutterFlow UI
-flutterflow-ui 本地库
+`flutterflow_ui` simplifies the process of adding FlutterFlow generated UI code to your Flutter projects. It streamlines integration, saving you time and effort in the UI development for your Flutter app.
+## Generate code in your FlutterFlow project
+
+In your FlutterFlow project, navigate to the code icon and click on "View Code".
+
+
+
+Here, you will find the FlutterFlow-generated code for your pages and components. Choose the specific page or component you need, then copy the widget code. Paste this code into a new Flutter file within your Flutter project.
+
+Ensure you also include the generated model code in the same file or in a separate file, depending on your directory structure. In some cases, this file may initially be empty, and you can decide whether to keep or remove it later.
+
+After pasting the code, you might encounter some errors, but don't worry. These issues will be resolved through the following steps.
+
+
+## Add Dependency
+
+Now in your Flutter project, open your `pubspec.yaml` file and add `flutterflow_ui` under dependencies:
+
+```yaml
+dependencies:
+ flutterflow_ui:
+```
+Remember to run `flutter pub get`
+
+## Replace the `flutter_flow` dependencies with the package import
+
+In your imports, you will see a bunch of `flutter_flow/flutter_flow...` imports that are usually present in a FlutterFlow project but with this package you can resolve these errors.
+
+Remove such imports:
+```dart
+import '/flutter_flow/flutter_flow_animations.dart';
+import '/flutter_flow/flutter_flow_icon_button.dart';
+import '/flutter_flow/flutter_flow_theme.dart';
+import '/flutter_flow/flutter_flow_util.dart';
+```
+
+And replace it with the package import:
+
+```dart
+import 'package:flutterflow_ui/flutterflow_ui.dart';
+```
+
+## Cleaning up unnecessary code
+
+In the beginning of the build method, you might encounter the line `context.watch();`. This line is beneficial in a FlutterFlow project, but in your Flutter project, you might have a different method for managing global constants and variables. If that's the case, feel free to remove this line of code.
+
+Additionally, if you're not using the Provider package for state management in your project, you can safely remove the import statement related to it.
+
+Lastly, double-check that your model file, if it's located in a separate file, is correctly imported.
+
+With these adjustments, you're ready to run the FlutterFlow-generated code in your Flutter project.
+__________________________________________
+
+
+## Some usecases
+
+### How to add a widget with animation to an existing Flutter screen?
+
+* Begin by right-clicking on the component or widget within your FlutterFlow canvas. Then, select "Copy Widget Code."
+
+
+
+Alternatively, you can follow similar steps as mentioned above, but click on "View Code" from the Developer Menu. After that, click on the widget in the preview that you want to copy. The code will be displayed on the left-hand side.
+
+* Next, paste the widget code into your Flutter widget file wherever you'd like to place it.
+* If you encounter errors related to `animationMap`, don't worry. This is located in your Stateful Widget of the screen where it's currently placed. You can now copy the `animationsMap` to your widget body. Once you've done this, the errors will disappear, and you can run your code without any issues.
+
+
+## Supports the following FlutterFlow widgets
+
+* Layout Elements supported by Material/Cupertino package
+* Ad Banner
+* Audio Player
+* Calendar
+* Charts
+* Checkbox Group
+* Choice Chips
+* Counter Button
+* Credit Card
+* Data Table
+* Drop Down
+* Expandable Image & Circle Image
+* Google Map
+* Icon Button
+* Language Selector
+* Media Display
+* Mux Broadcast
+* Radio Button
+* Rive
+* Static Map
+* Swipeable Stack
+* Timer
+* Toggle Icon
+* Tab Bar
+* Web View
+
+## Documentation & more usages
+You can check out our [documentation](https://docs.flutterflow.io/flutter/export-flutterflow-ui-code-to-your-flutter-project) for more examples.
diff --git a/analysis_options.yaml b/analysis_options.yaml
new file mode 100644
index 0000000..ecf893a
--- /dev/null
+++ b/analysis_options.yaml
@@ -0,0 +1,115 @@
+include: package:flutter_lints/flutter.yaml
+analyzer:
+ errors:
+ missing_required_param: error
+ missing_return: warning
+ todo: ignore
+ exclude:
+ - "bin/cache/**"
+linter:
+ rules:
+ always_declare_return_types: true
+ always_put_control_body_on_new_line: true
+ annotate_overrides: true
+ avoid_bool_literals_in_conditional_expressions: true
+ avoid_classes_with_only_static_members: true
+ avoid_empty_else: true
+ avoid_field_initializers_in_const_classes: true
+ avoid_function_literals_in_foreach_calls: false
+ avoid_init_to_null: true
+ avoid_null_checks_in_equality_operators: true
+ avoid_relative_lib_imports: true
+ avoid_renaming_method_parameters: true
+ avoid_return_types_on_setters: true
+ avoid_returning_null_for_void: true
+ avoid_shadowing_type_parameters: true
+ avoid_slow_async_io: true
+ avoid_types_as_parameter_names: true
+ avoid_types_on_closure_parameters: true
+ avoid_unnecessary_containers: true
+ avoid_unused_constructor_parameters: true
+ avoid_void_async: true
+ await_only_futures: true
+ camel_case_extensions: true
+ camel_case_types: true
+ cancel_subscriptions: true
+ cascade_invocations: true
+ close_sinks: true
+ collection_methods_unrelated_type: true
+ constant_identifier_names: false
+ control_flow_in_finally: true
+ depend_on_referenced_packages: false
+ directives_ordering: true
+ empty_catches: true
+ empty_constructor_bodies: true
+ empty_statements: true
+ hash_and_equals: true
+ implementation_imports: true
+ library_names: true
+ library_prefixes: true
+ library_private_types_in_public_api: true
+ no_adjacent_strings_in_list: true
+ no_duplicate_case_values: true
+ no_leading_underscores_for_local_identifiers: false
+ overridden_fields: true
+ package_api_docs: true
+ package_names: true
+ package_prefixed_library_names: true
+ prefer_adjacent_string_concatenation: true
+ prefer_asserts_in_initializer_lists: true
+ prefer_collection_literals: true
+ prefer_conditional_assignment: true
+ prefer_const_constructors: true
+ prefer_const_constructors_in_immutables: true
+ prefer_const_declarations: true
+ prefer_const_literals_to_create_immutables: true
+ prefer_contains: true
+ prefer_final_fields: true
+ prefer_final_in_for_each: true
+ prefer_for_elements_to_map_fromIterable: true
+ prefer_foreach: true
+ prefer_function_declarations_over_variables: false
+ prefer_generic_function_type_aliases: true
+ prefer_if_null_operators: true
+ prefer_initializing_formals: true
+ prefer_inlined_adds: true
+ prefer_interpolation_to_compose_strings: true
+ prefer_is_empty: true
+ prefer_is_not_empty: true
+ prefer_is_not_operator: true
+ prefer_iterable_whereType: true
+ prefer_mixin: true
+ prefer_null_aware_operators: true
+ prefer_spread_collections: true
+ prefer_typing_uninitialized_variables: true
+ prefer_void_to_null: true
+ recursive_getters: true
+ slash_for_doc_comments: true
+ sort_child_properties_last: true
+ sort_constructors_first: true
+ sort_pub_dependencies: true
+ sort_unnamed_constructors_first: true
+ test_types_in_equals: true
+ throw_in_finally: true
+ type_init_formals: true
+ unawaited_futures: true
+ unnecessary_await_in_return: true
+ unnecessary_brace_in_string_interps: true
+ unnecessary_const: true
+ unnecessary_getters_setters: true
+ unnecessary_lambdas: true
+ unnecessary_new: true
+ unnecessary_null_aware_assignments: true
+ unnecessary_null_in_if_null_operators: true
+ unnecessary_overrides: true
+ unnecessary_parenthesis: true
+ unnecessary_statements: true
+ unnecessary_this: true
+ unrelated_type_equality_checks: true
+ use_build_context_synchronously: false
+ use_full_hex_values_for_flutter_colors: true
+ use_function_type_syntax_for_parameters: true
+ use_rethrow_when_possible: true
+ use_to_and_as_if_applicable: true
+ valid_regexps: true
+ void_checks: true
diff --git a/assets/package1.gif b/assets/package1.gif
new file mode 100644
index 0000000..6417704
Binary files /dev/null and b/assets/package1.gif differ
diff --git a/assets/right-click.png b/assets/right-click.png
new file mode 100644
index 0000000..e9cf6e3
Binary files /dev/null and b/assets/right-click.png differ
diff --git a/lib/flutterflow_ui.dart b/lib/flutterflow_ui.dart
new file mode 100644
index 0000000..d7f4f7c
--- /dev/null
+++ b/lib/flutterflow_ui.dart
@@ -0,0 +1,4 @@
+library flutterflow_ui;
+
+export 'src/utils/flutter_flow_utils.dart';
+export 'src/widgets/flutter_flow_widgets.dart';
diff --git a/lib/src/constants.dart b/lib/src/constants.dart
new file mode 100644
index 0000000..8f074c0
--- /dev/null
+++ b/lib/src/constants.dart
@@ -0,0 +1,155 @@
+import 'package:flutter/material.dart';
+
+/// Constants that we should use throughout the app for widget dimension/stylings.
+
+/// Dimensions
+
+const double kPadding2px = 2.0;
+const double kPadding4px = 4.0;
+const double kPadding8px = 8.0;
+const double kPadding12px = 12.0;
+const double kPadding16px = 16.0;
+const double kPadding20px = 20.0;
+const double kPadding24px = 24.0;
+const double kPadding28px = 28.0;
+
+const double kBaseSize8px = 8.0;
+const double kBaseSize12px = 12.0;
+const double kBaseSize16px = 16.0;
+const double kBaseSize20px = 20.0;
+const double kBaseSize24px = 24.0;
+const double kBaseSize28px = 28.0;
+const double kBaseSize32px = 32.0;
+const double kBaseSize36px = 36.0;
+const double kBaseSize48px = 48.0;
+const double kBaseSize60px = 60.0;
+const double kBaseSize72px = 72.0;
+const double kBaseSize96px = 96.0;
+const double kBaseSize120px = 120.0;
+const double kBaseSize160px = 160.0;
+
+const double kWidth48px = 48.0;
+const double kWidth56px = 56.0;
+//* Width for a property editor that is a quarter of the width of panel.
+const double kWidth60px = 60.0;
+const double kWidth64px = 64.0;
+const double kWidth72px = 72.0;
+const double kWidth80px = 80.0;
+const double kWidth96px = 96.0;
+const double kWidth108px = 108.0;
+const double kWidth120px = 120.0;
+//* Width for a property editor that is half the width of panel.
+const double kWidth132px = 132.0;
+const double kWidth144px = 144.0;
+const double kWidth160px = 160.0;
+//* Width for a property editor that is total the width of panel.
+const double kWidth276px = 276.0;
+
+const double kWidth440px = 440.0;
+const double kWidth480px = 480.0;
+const double kWidth600px = 600.0;
+const double kWidth720px = 720.0;
+const double kWidth840px = 840.0;
+
+const double kHeight16px = 16.0;
+const double kHeight20px = 20.0;
+const double kHeight24px = 24.0;
+const double kHeight28px = 28.0;
+//* Typically used for input fields' height.
+const double kHeight32px = 32.0;
+const double kHeight36px = 36.0;
+const double kHeight40px = 40.0;
+const double kHeight48px = 48.0;
+const double kHeight56px = 56.0;
+const double kHeight64px = 64.0;
+const double kHeight72px = 72.0;
+const double kHeight80px = 80.0;
+const double kHeight88px = 88.0;
+const double kHeight96px = 96.0;
+
+const double kHeight240px = 240.0;
+const double kHeight320px = 320.0;
+const double kHeight400px = 400.0;
+const double kHeight480px = 480.0;
+
+/// Border Radius
+
+const double kBorderRadius4px = 4.0;
+const double kBorderRadius8px = 8.0;
+const double kBorderRadius12px = 12.0;
+
+/// Border Width
+
+const double kBorderWidth1px = 1.0;
+const double kBorderWidth1_5px = 1.5;
+const double kBorderWidth2px = 2.0;
+
+/// Line Width
+
+const double kLineWidth1px = 1.0;
+const double kLineWidth2px = 2.0;
+const double kLineWidth4px = 4.0;
+
+//* Typically used for icon size in property name line.
+const double kIconSize14px = 14.0;
+const double kIconSize16px = 16.0;
+const double kIconSize20px = 20.0;
+const double kIconSize22px = 22.0;
+const double kIconSize24px = 24.0;
+const double kIconSize32px = 32.0;
+
+/// Durations
+
+const Duration kDuration250ms = Duration(milliseconds: 250);
+const Duration kDuration500ms = Duration(milliseconds: 500);
+
+/// Durations
+
+const Curve kCurveEase = Curves.ease;
+
+/// Elevations and shadowing
+
+const double kElevation0 = 0.0;
+const double kElevation2 = 2.0;
+const double kElevation4 = 4.0;
+const double kElevation8 = 8.0;
+
+final kBoxShadow2 = kElevationToShadow[2]!;
+final kBoxShadow4 = kElevationToShadow[4]!;
+final kBoxShadow8 = kElevationToShadow[8]!;
+
+/// Blur
+
+const double kBlur4 = 4.0;
+const double kBlur8 = 8.0;
+
+/// Opacity
+
+const double kOpacity0_2 = 0.2;
+const double kOpacity0_4 = 0.4;
+const double kOpacity0_6 = 0.6;
+const double kOpacity0_8 = 0.8;
+
+/// Color
+
+const kErrorColor = Color(0xFFDF3F3F);
+const kPrimaryColor = Color(0xFF4B39EF);
+const kSecondaryColor = Color(0xFFEE8B60);
+const kTertiaryColor = Color(0xFF1D2429);
+const kSuccessToastColor = Color(0xFF39D2C0);
+const kGrey250 = Color(0xFFE3E7ED);
+const kGrey800 = Color(0xFF424E5A);
+const kDiffAddedColor = Colors.green;
+const kDiffDeletedColor = Color.fromARGB(255, 255, 129, 129);
+
+/// Font Sizes
+
+const double kFontSize12px = 12.0;
+const double kFontSize13px = 13.0;
+const double kFontSize14px = 14.0;
+const double kFontSize16px = 16.0;
+const double kFontSize18px = 18.0;
+const double kFontSize20px = 20.0;
+const double kFontSize24px = 24.0;
+const double kFontSize32px = 32.0;
+const double kFontSize36px = 36.0;
diff --git a/lib/src/utils/flutter_flow_animations.dart b/lib/src/utils/flutter_flow_animations.dart
new file mode 100644
index 0000000..570c82c
--- /dev/null
+++ b/lib/src/utils/flutter_flow_animations.dart
@@ -0,0 +1,103 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_animate/flutter_animate.dart';
+
+enum AnimationTrigger {
+ onPageLoad,
+ onActionTrigger,
+}
+
+class AnimationInfo {
+ AnimationInfo({
+ required this.trigger,
+ required this.effectsBuilder,
+ this.loop = false,
+ this.reverse = false,
+ this.applyInitialState = true,
+ });
+
+ final AnimationTrigger trigger;
+ final List Function()? effectsBuilder;
+ final bool applyInitialState;
+ final bool loop;
+ final bool reverse;
+ late AnimationController controller;
+
+ List? _effects;
+
+ List get effects => _effects ??= effectsBuilder!();
+
+ void maybeUpdateEffects(List? updatedEffects) {
+ if (updatedEffects != null) {
+ _effects = updatedEffects;
+ }
+ }
+}
+
+void createAnimation(AnimationInfo animation, TickerProvider vsync) {
+ final newController = AnimationController(vsync: vsync);
+ animation.controller = newController;
+}
+
+void setupAnimations(Iterable animations, TickerProvider vsync) {
+ animations.forEach((animation) => createAnimation(animation, vsync));
+}
+
+extension AnimatedWidgetExtension on Widget {
+ Widget animateOnPageLoad(
+ AnimationInfo animationInfo, {
+ List? effects,
+ }) {
+ animationInfo.maybeUpdateEffects(effects);
+ return Animate(
+ effects: animationInfo.effects,
+ child: this,
+ onPlay: (controller) => animationInfo.loop ? controller.repeat(reverse: animationInfo.reverse) : null,
+ onComplete: (controller) => !animationInfo.loop && animationInfo.reverse ? controller.reverse() : null,
+ );
+ }
+
+ Widget animateOnActionTrigger(
+ AnimationInfo animationInfo, {
+ List? effects,
+ bool hasBeenTriggered = false,
+ }) {
+ animationInfo.maybeUpdateEffects(effects);
+ return hasBeenTriggered || animationInfo.applyInitialState
+ ? Animate(controller: animationInfo.controller, autoPlay: false, effects: animationInfo.effects, child: this)
+ : this;
+ }
+}
+
+class TiltEffect extends Effect {
+ const TiltEffect({
+ super.delay,
+ super.duration,
+ super.curve,
+ Offset? begin,
+ Offset? end,
+ }) : super(
+ begin: begin ?? const Offset(0.0, 0.0),
+ end: end ?? const Offset(0.0, 0.0),
+ );
+
+ @override
+ Widget build(
+ BuildContext context,
+ Widget child,
+ AnimationController controller,
+ EffectEntry entry,
+ ) {
+ Animation animation = buildAnimation(controller, entry);
+ return getOptimizedBuilder(
+ animation: animation,
+ builder: (_, __) => Transform(
+ transform: Matrix4.identity()
+ ..setEntry(3, 2, 0.001)
+ ..rotateX(animation.value.dx)
+ ..rotateY(animation.value.dy),
+ alignment: Alignment.center,
+ child: child,
+ ),
+ );
+ }
+}
diff --git a/lib/src/utils/flutter_flow_helpers.dart b/lib/src/utils/flutter_flow_helpers.dart
new file mode 100644
index 0000000..29713ce
--- /dev/null
+++ b/lib/src/utils/flutter_flow_helpers.dart
@@ -0,0 +1,279 @@
+import 'dart:io';
+
+import 'package:flutter/foundation.dart' show kIsWeb;
+import 'package:flutter/material.dart';
+import 'package:from_css_color/from_css_color.dart';
+import 'package:intl/intl.dart';
+import 'package:json_path/json_path.dart';
+import 'package:timeago/timeago.dart' as timeago;
+
+export 'dart:convert' show jsonEncode, jsonDecode;
+export 'dart:math' show min, max;
+export 'dart:typed_data' show Uint8List;
+
+export 'package:intl/intl.dart';
+export 'package:page_transition/page_transition.dart';
+
+export 'flutter_flow_model.dart';
+export 'lat_lng.dart';
+export 'place.dart';
+
+final RouteObserver routeObserver = RouteObserver();
+
+T valueOrDefault(T? value, T defaultValue) =>
+ (value is String && value.isEmpty) || value == null ? defaultValue : value;
+
+String dateTimeFormat(String format, DateTime? dateTime, {String? locale}) {
+ if (dateTime == null) {
+ return '';
+ }
+ if (format == 'relative') {
+ return timeago.format(dateTime, locale: locale, allowFromNow: true);
+ }
+ return DateFormat(format, locale).format(dateTime);
+}
+
+Color colorFromCssString(String color, {Color? defaultColor}) {
+ try {
+ return fromCssColor(color);
+ } catch (_) {}
+ return defaultColor ?? Colors.black;
+}
+
+enum FormatType {
+ decimal,
+ percent,
+ scientific,
+ compact,
+ compactLong,
+ custom,
+}
+
+enum DecimalType {
+ automatic,
+ periodDecimal,
+ commaDecimal,
+}
+
+String formatNumber(
+ num? value, {
+ required FormatType formatType,
+ DecimalType? decimalType,
+ String? currency,
+ bool toLowerCase = false,
+ String? format,
+ String? locale,
+}) {
+ if (value == null) {
+ return '';
+ }
+ var formattedValue = '';
+ switch (formatType) {
+ case FormatType.decimal:
+ switch (decimalType!) {
+ case DecimalType.automatic:
+ formattedValue = NumberFormat.decimalPattern().format(value);
+ break;
+ case DecimalType.periodDecimal:
+ formattedValue = NumberFormat.decimalPattern('en_US').format(value);
+ break;
+ case DecimalType.commaDecimal:
+ formattedValue = NumberFormat.decimalPattern('es_PA').format(value);
+ break;
+ }
+ break;
+ case FormatType.percent:
+ formattedValue = NumberFormat.percentPattern().format(value);
+ break;
+ case FormatType.scientific:
+ formattedValue = NumberFormat.scientificPattern().format(value);
+ if (toLowerCase) {
+ formattedValue = formattedValue.toLowerCase();
+ }
+ break;
+ case FormatType.compact:
+ formattedValue = NumberFormat.compact().format(value);
+ break;
+ case FormatType.compactLong:
+ formattedValue = NumberFormat.compactLong().format(value);
+ break;
+ case FormatType.custom:
+ final hasLocale = locale != null && locale.isNotEmpty;
+ formattedValue =
+ NumberFormat(format, hasLocale ? locale : null).format(value);
+ }
+
+ if (formattedValue.isEmpty) {
+ return value.toString();
+ }
+
+ if (currency != null) {
+ final currencySymbol = currency.isNotEmpty
+ ? currency
+ : NumberFormat.simpleCurrency().format(0.0).substring(0, 1);
+ formattedValue = '$currencySymbol$formattedValue';
+ }
+
+ return formattedValue;
+}
+
+DateTime get getCurrentTimestamp => DateTime.now();
+DateTime dateTimeFromSecondsSinceEpoch(int seconds) {
+ return DateTime.fromMillisecondsSinceEpoch(seconds * 1000);
+}
+
+extension DateTimeConversionExtension on DateTime {
+ int get secondsSinceEpoch => (millisecondsSinceEpoch / 1000).round();
+}
+
+extension DateTimeComparisonOperators on DateTime {
+ bool operator <(DateTime other) => isBefore(other);
+ bool operator >(DateTime other) => isAfter(other);
+ bool operator <=(DateTime other) => this < other || isAtSameMomentAs(other);
+ bool operator >=(DateTime other) => this > other || isAtSameMomentAs(other);
+}
+
+dynamic getJsonField(
+ dynamic response,
+ String jsonPath, [
+ bool isForList = false,
+]) {
+ final field = JsonPath(jsonPath).read(response);
+ if (field.isEmpty) {
+ return null;
+ }
+ if (field.length > 1) {
+ return field.map((f) => f.value).toList();
+ }
+ final value = field.first.value;
+ return isForList && value is! Iterable ? [value] : value;
+}
+
+Rect? getWidgetBoundingBox(BuildContext context) {
+ try {
+ final renderBox = context.findRenderObject() as RenderBox?;
+ return renderBox!.localToGlobal(Offset.zero) & renderBox.size;
+ } catch (_) {
+ return null;
+ }
+}
+
+bool get isAndroid => !kIsWeb && Platform.isAndroid;
+bool get isiOS => !kIsWeb && Platform.isIOS;
+bool get isWeb => kIsWeb;
+
+const kBreakpointSmall = 479.0;
+const kBreakpointMedium = 767.0;
+const kBreakpointLarge = 991.0;
+bool isMobileWidth(BuildContext context) =>
+ MediaQuery.sizeOf(context).width < kBreakpointSmall;
+bool responsiveVisibility({
+ required BuildContext context,
+ bool phone = true,
+ bool tablet = true,
+ bool tabletLandscape = true,
+ bool desktop = true,
+}) {
+ final width = MediaQuery.sizeOf(context).width;
+ if (width < kBreakpointSmall) {
+ return phone;
+ } else if (width < kBreakpointMedium) {
+ return tablet;
+ } else if (width < kBreakpointLarge) {
+ return tabletLandscape;
+ } else {
+ return desktop;
+ }
+}
+
+const kTextValidatorUsernameRegex = r'^[a-zA-Z][a-zA-Z0-9_-]{2,16}$';
+// https://stackoverflow.com/a/201378
+const kTextValidatorEmailRegex =
+ "^(?:[a-z0-9!#\$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#\$%&'*+/=?^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])\$";
+const kTextValidatorWebsiteRegex =
+ r'(https?:\/\/)?(www\.)[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,10}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)|(https?:\/\/)?(www\.)?(?!ww)[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,10}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)';
+
+extension FFTextEditingControllerExt on TextEditingController? {
+ String get text => this == null ? '' : this!.text;
+ set text(String newText) => this?.text = newText;
+}
+
+extension IterableExt on Iterable {
+ List mapIndexed(S Function(int, T) func) => toList()
+ .asMap()
+ .map((index, value) => MapEntry(index, func(index, value)))
+ .values
+ .toList();
+}
+
+void showSnackbar(
+ BuildContext context,
+ String message, {
+ bool loading = false,
+ int duration = 4,
+}) {
+ ScaffoldMessenger.of(context).hideCurrentSnackBar();
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(
+ content: Row(
+ children: [
+ if (loading)
+ const Padding(
+ padding: EdgeInsetsDirectional.only(end: 10.0),
+ child: SizedBox(
+ height: 20,
+ width: 20,
+ child: CircularProgressIndicator(
+ color: Colors.white,
+ ),
+ ),
+ ),
+ Text(message),
+ ],
+ ),
+ duration: Duration(seconds: duration),
+ ),
+ );
+}
+
+extension FFStringExt on String {
+ String maybeHandleOverflow({int? maxChars, String replacement = ''}) =>
+ maxChars != null && length > maxChars
+ ? replaceRange(maxChars, null, replacement)
+ : this;
+}
+
+extension ListFilterExt on Iterable {
+ List get withoutNulls => where((s) => s != null).map((e) => e!).toList();
+}
+
+extension ListDivideExt on Iterable {
+ Iterable> get enumerate => toList().asMap().entries;
+
+ List divide(Widget t) => isEmpty
+ ? []
+ : (enumerate.map((e) => [e.value, t]).expand((i) => i).toList()
+ ..removeLast());
+
+ List around(Widget t) => addToStart(t).addToEnd(t);
+
+ List addToStart(Widget t) =>
+ enumerate.map((e) => e.value).toList()..insert(0, t);
+
+ List addToEnd(Widget t) =>
+ enumerate.map((e) => e.value).toList()..add(t);
+
+ List paddingTopEach(double val) =>
+ map((w) => Padding(padding: EdgeInsets.only(top: val), child: w))
+ .toList();
+}
+
+extension StatefulWidgetExtensions on State {
+ /// Check if the widget exist before safely setting state.
+ void safeSetState(VoidCallback fn) {
+ if (mounted) {
+ // ignore: invalid_use_of_protected_member
+ setState(fn);
+ }
+ }
+}
diff --git a/lib/src/utils/flutter_flow_model.dart b/lib/src/utils/flutter_flow_model.dart
new file mode 100644
index 0000000..c38a4fa
--- /dev/null
+++ b/lib/src/utils/flutter_flow_model.dart
@@ -0,0 +1,172 @@
+import 'package:collection/collection.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/scheduler.dart';
+import 'package:provider/provider.dart';
+
+Widget wrapWithModel({
+ required T model,
+ required Widget child,
+ required VoidCallback updateCallback,
+ bool updateOnChange = false,
+}) {
+ // Set the component to optionally update the page on updates.
+ model
+ ..setOnUpdate(
+ onUpdate: updateCallback,
+ updateOnChange: updateOnChange,
+ )
+ // Models for components within a page will be disposed by the page's model,
+ // so we don't want the component widget to dispose them until the page is
+ // itself disposed.
+ ..disposeOnWidgetDisposal = false;
+ // Wrap in a Provider so that the model can be accessed by the component.
+ return Provider.value(
+ value: model,
+ child: child,
+ );
+}
+
+T createModel(
+ BuildContext context,
+ T Function() defaultBuilder,
+) {
+ final model = context.read() ?? defaultBuilder()
+ .._init(context);
+ return model;
+}
+
+abstract class FlutterFlowModel {
+ // Initialization methods
+ bool _isInitialized = false;
+ void initState(BuildContext context);
+ void _init(BuildContext context) {
+ if (!_isInitialized) {
+ initState(context);
+ _isInitialized = true;
+ }
+ if (context.widget is W) {
+ _widget = context.widget as W;
+ }
+ }
+
+ // The widget associated with this model. This is useful for accessing the
+ // parameters of the widget, for example.
+ W? _widget;
+ // This will always be non-null when used, but is nullable to allow us to
+ // dispose of the widget in the [dispose] method (for garbage collection).
+ W get widget => _widget!;
+
+ // Dispose methods
+ // Whether to dispose this model when the corresponding widget is
+ // disposed. By default this is true for pages and false for components,
+ // as page/component models handle the disposal of their children.
+ bool disposeOnWidgetDisposal = true;
+ void dispose();
+ void maybeDispose() {
+ if (disposeOnWidgetDisposal) {
+ dispose();
+ }
+ // Remove reference to widget for garbage collection purposes.
+ _widget = null;
+ }
+
+ // Whether to update the containing page / component on updates.
+ bool updateOnChange = false;
+ // Function to call when the model receives an update.
+ VoidCallback _updateCallback = () {};
+ void onUpdate() => updateOnChange ? _updateCallback() : () {};
+ FlutterFlowModel setOnUpdate({
+ bool updateOnChange = false,
+ required VoidCallback onUpdate,
+ }) =>
+ this
+ .._updateCallback = onUpdate
+ ..updateOnChange = updateOnChange;
+ // Update the containing page when this model received an update.
+ void updatePage(VoidCallback callback) {
+ callback();
+ _updateCallback();
+ }
+}
+
+class FlutterFlowDynamicModels {
+ FlutterFlowDynamicModels(this.defaultBuilder);
+
+ final T Function() defaultBuilder;
+ final Map _childrenModels = {};
+ final Map _childrenIndexes = {};
+ Set? _activeKeys;
+
+ T getModel(String uniqueKey, int index) {
+ _updateActiveKeys(uniqueKey);
+ _childrenIndexes[uniqueKey] = index;
+ return _childrenModels[uniqueKey] ??= defaultBuilder();
+ }
+
+ List getValues(S? Function(T) getValue) {
+ return _childrenIndexes.entries
+ // Sort keys by index.
+ .sorted((a, b) => a.value.compareTo(b.value))
+ .where((e) => _childrenModels[e.key] != null)
+ // Map each model to the desired value and return as list. In order
+ // to preserve index order, rather than removing null values we provide
+ // default values (for types with reasonable defaults).
+ .map((e) => getValue(_childrenModels[e.key]!) ?? _getDefaultValue()!)
+ .toList();
+ }
+
+ S? getValueAtIndex(int index, S? Function(T) getValue) {
+ final uniqueKey =
+ _childrenIndexes.entries.firstWhereOrNull((e) => e.value == index)?.key;
+ return getValueForKey(uniqueKey, getValue);
+ }
+
+ S? getValueForKey(String? uniqueKey, S? Function(T) getValue) {
+ final model = _childrenModels[uniqueKey];
+ return model != null ? getValue(model) : null;
+ }
+
+ void dispose() => _childrenModels.values.forEach((model) => model.dispose());
+
+ void _updateActiveKeys(String uniqueKey) {
+ final shouldResetActiveKeys = _activeKeys == null;
+ _activeKeys ??= {};
+ _activeKeys!.add(uniqueKey);
+
+ if (shouldResetActiveKeys) {
+ // Add a post-frame callback to remove and dispose of unused models after
+ // we're done building, then reset `_activeKeys` to null so we know to do
+ // this again next build.
+ SchedulerBinding.instance.addPostFrameCallback((_) {
+ _childrenIndexes.removeWhere((k, _) => !_activeKeys!.contains(k));
+ _childrenModels.keys
+ .toSet()
+ .difference(_activeKeys!)
+ // Remove and dispose of unused models since they are not being used
+ // elsewhere and would not otherwise be disposed.
+ .forEach((k) => _childrenModels.remove(k)?.maybeDispose());
+ _activeKeys = null;
+ });
+ }
+ }
+}
+
+T? _getDefaultValue() {
+ switch (T) {
+ case const (int):
+ return 0 as T;
+ case const (double):
+ return 0.0 as T;
+ case const (String):
+ return '' as T;
+ case const (bool):
+ return false as T;
+ default:
+ return null as T;
+ }
+}
+
+extension TextValidationExtensions on String? Function(BuildContext, String?)? {
+ String? Function(String?)? asValidator(BuildContext context) =>
+ this != null ? (val) => this!(context, val) : null;
+}
diff --git a/lib/src/utils/flutter_flow_rive_controller.dart b/lib/src/utils/flutter_flow_rive_controller.dart
new file mode 100644
index 0000000..1719e44
--- /dev/null
+++ b/lib/src/utils/flutter_flow_rive_controller.dart
@@ -0,0 +1,71 @@
+import 'package:flutter/foundation.dart';
+import 'package:rive/rive.dart';
+
+class FlutterFlowRiveController extends SimpleAnimation {
+ FlutterFlowRiveController(
+ super.animationName, {
+ super.mix,
+ super.autoplay,
+ this.shouldLoop = false,
+ });
+
+ bool shouldLoop;
+ final _reactivate = ValueNotifier(false);
+ ValueListenable get changeReactivate => _reactivate;
+
+ bool get reactivate => _reactivate.value;
+ set reactivate(bool value) {
+ if (_reactivate.value != value) {
+ _reactivate.value = value;
+ }
+ }
+
+ bool endOfAnimation(LinearAnimationInstance? instance) {
+ if (instance == null) {
+ return false;
+ }
+ return instance.time == instance.animation.endTime;
+ }
+
+ @override
+ bool init(RuntimeArtboard artboard) {
+ reactivate = false;
+ changeReactivate.addListener(() {
+ if (reactivate) {
+ isActive = true;
+ }
+ });
+ return super.init(artboard);
+ }
+
+ @override
+ void apply(RuntimeArtboard artboard, double elapsedSeconds) {
+ if (instance == null) {
+ return;
+ }
+
+ /// Reset on button press
+ if (reactivate) {
+ if (endOfAnimation(instance)) {
+ instance?.time = 0;
+ }
+ reactivate = false;
+ }
+
+ if (instance == null || endOfAnimation(instance)) {
+ isActive = false;
+ }
+
+ /// Stop after one loop if not a continuous animation
+ if (!shouldLoop &&
+ (instance?.animation.loop == Loop.loop ||
+ instance?.animation.loop == Loop.pingPong) &&
+ instance!.didLoop) {
+ isActive = false;
+ }
+
+ instance!
+ ..animation.apply(instance!.time, coreContext: artboard, mix: mix)
+ ..advance(elapsedSeconds);
+ }
+}
diff --git a/lib/src/utils/flutter_flow_utils.dart b/lib/src/utils/flutter_flow_utils.dart
new file mode 100644
index 0000000..ecc3fb4
--- /dev/null
+++ b/lib/src/utils/flutter_flow_utils.dart
@@ -0,0 +1,11 @@
+library flutterflow_ui;
+
+export 'flutter_flow_animations.dart';
+export 'flutter_flow_helpers.dart';
+export 'flutter_flow_rive_controller.dart';
+export 'form_field_controller.dart';
+export 'internationalization.dart';
+export 'lat_lng.dart';
+export 'place.dart';
+export 'random_data.dart';
+export 'uploaded_file.dart';
diff --git a/lib/src/utils/form_field_controller.dart b/lib/src/utils/form_field_controller.dart
new file mode 100644
index 0000000..1ec25a4
--- /dev/null
+++ b/lib/src/utils/form_field_controller.dart
@@ -0,0 +1,24 @@
+import 'package:flutter/foundation.dart';
+
+class FormFieldController extends ValueNotifier {
+ FormFieldController(this.initialValue) : super(initialValue);
+
+ final T? initialValue;
+
+ void reset() => value = initialValue;
+
+ void update() => notifyListeners();
+}
+
+// If the initial value is a list (which it is for multiselect),
+// we need to use this controller to avoid a pass by reference issue
+// that can result in the initial value being modified.
+class FormListFieldController extends FormFieldController> {
+ FormListFieldController(super.initialValue)
+ : _initialListValue = List.from(initialValue ?? []);
+
+ final List? _initialListValue;
+
+ @override
+ void reset() => value = List.from(_initialListValue ?? []);
+}
diff --git a/lib/src/utils/internationalization.dart b/lib/src/utils/internationalization.dart
new file mode 100644
index 0000000..21cfcfe
--- /dev/null
+++ b/lib/src/utils/internationalization.dart
@@ -0,0 +1,47 @@
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
+
+//Helper class for internationalization
+class FFLocalizations {
+ FFLocalizations(this.locale);
+
+ final Locale locale;
+
+ //Initializing
+ static FFLocalizations of(BuildContext context) =>
+ FFLocalizations(const Locale('en'));
+
+ static List languages() => ['en'];
+
+ String get languageCode => locale.languageCode;
+
+ int get languageIndex => languages().contains(languageCode)
+ ? languages().indexOf(languageCode)
+ : 0;
+
+ String getText(String key) =>
+ (kTranslationsMap[key] ?? {})[locale.languageCode] ?? '';
+
+ String getVariableText({
+ String? enText = '',
+ }) =>
+ [enText][languageIndex] ?? '';
+}
+
+class FFLocalizationsDelegate extends LocalizationsDelegate {
+ const FFLocalizationsDelegate();
+
+ @override
+ bool isSupported(Locale locale) =>
+ FFLocalizations.languages().contains(locale.languageCode);
+
+ @override
+ Future load(Locale locale) =>
+ SynchronousFuture(FFLocalizations(locale));
+
+ @override
+ bool shouldReload(FFLocalizationsDelegate old) => false;
+}
+
+final kTranslationsMap =
+