Пачка экранов и логики #3
@@ -6,22 +6,26 @@ analyzer
|
||||
3.3
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/analyzer-7.3.0/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/analyzer-7.3.0/lib/
|
||||
ansicolor
|
||||
3.0
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/ansicolor-2.0.3/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/ansicolor-2.0.3/lib/
|
||||
archive
|
||||
3.0
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/archive-4.0.4/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/archive-4.0.4/lib/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/archive-4.0.5/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/archive-4.0.5/lib/
|
||||
args
|
||||
3.3
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/args-2.6.0/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/args-2.6.0/lib/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/args-2.7.0/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/args-2.7.0/lib/
|
||||
async
|
||||
3.4
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/async-2.12.0/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/async-2.12.0/lib/
|
||||
auto_route
|
||||
3.0
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/auto_route-10.0.0/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/auto_route-10.0.0/lib/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/auto_route-10.0.1/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/auto_route-10.0.1/lib/
|
||||
auto_route_generator
|
||||
3.4
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/auto_route_generator-10.0.1/
|
||||
@@ -68,8 +72,8 @@ file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/built_collection-5.1.1/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/built_collection-5.1.1/lib/
|
||||
built_value
|
||||
3.0
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/built_value-8.9.4/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/built_value-8.9.4/lib/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/built_value-8.9.5/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/built_value-8.9.5/lib/
|
||||
characters
|
||||
3.4
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/characters-1.4.0/
|
||||
@@ -126,10 +130,6 @@ cupertino_icons
|
||||
3.1
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/cupertino_icons-1.0.8/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/cupertino_icons-1.0.8/lib/
|
||||
curl_logger_dio_interceptor
|
||||
2.12
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/curl_logger_dio_interceptor-1.0.0/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/curl_logger_dio_interceptor-1.0.0/lib/
|
||||
dart_style
|
||||
3.4
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/dart_style-3.0.1/
|
||||
@@ -156,16 +156,16 @@ file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/dio_web_adapter-2.1.1/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/dio_web_adapter-2.1.1/lib/
|
||||
drift
|
||||
3.4
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/drift-2.25.1/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/drift-2.25.1/lib/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/drift-2.26.0/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/drift-2.26.0/lib/
|
||||
drift_db_viewer
|
||||
2.13
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/drift_db_viewer-2.1.0/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/drift_db_viewer-2.1.0/lib/
|
||||
drift_dev
|
||||
3.4
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/drift_dev-2.25.2/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/drift_dev-2.25.2/lib/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/drift_dev-2.26.0/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/drift_dev-2.26.0/lib/
|
||||
event_bus
|
||||
2.12
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/event_bus-2.0.1/
|
||||
@@ -184,8 +184,8 @@ file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/file-7.0.1/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/file-7.0.1/lib/
|
||||
file_picker
|
||||
3.4
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/file_picker-9.2.1/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/file_picker-9.2.1/lib/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/file_picker-10.0.0/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/file_picker-10.0.0/lib/
|
||||
fixnum
|
||||
3.1
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/fixnum-1.1.1/
|
||||
@@ -210,6 +210,10 @@ flutter_keyboard_size
|
||||
2.18
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/flutter_keyboard_size-1.0.1/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/flutter_keyboard_size-1.0.1/lib/
|
||||
flutter_launcher_icons
|
||||
3.0
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/flutter_launcher_icons-0.14.3/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/flutter_launcher_icons-0.14.3/lib/
|
||||
flutter_lints
|
||||
3.5
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/flutter_lints-5.0.0/
|
||||
@@ -294,6 +298,10 @@ graphs
|
||||
3.4
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/graphs-2.3.2/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/graphs-2.3.2/lib/
|
||||
group_button
|
||||
2.12
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/group_button-5.3.4/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/group_button-5.3.4/lib/
|
||||
hashcodes
|
||||
2.12
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/hashcodes-2.0.0/
|
||||
@@ -322,6 +330,10 @@ http_parser
|
||||
3.4
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/http_parser-4.1.2/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/http_parser-4.1.2/lib/
|
||||
image
|
||||
3.0
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/image-4.5.4/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/image-4.5.4/lib/
|
||||
image_size_getter
|
||||
2.12
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/image_size_getter-2.4.0/
|
||||
@@ -412,8 +424,8 @@ file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/node_preamble-2.0.2/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/node_preamble-2.0.2/lib/
|
||||
package_config
|
||||
3.4
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/package_config-2.1.1/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/package_config-2.1.1/lib/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/package_config-2.2.0/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/package_config-2.2.0/lib/
|
||||
package_info_plus
|
||||
3.3
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/package_info_plus-8.3.0/
|
||||
@@ -435,9 +447,9 @@ path_provider
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/path_provider-2.1.5/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/path_provider-2.1.5/lib/
|
||||
path_provider_android
|
||||
3.5
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/path_provider_android-2.2.15/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/path_provider_android-2.2.15/lib/
|
||||
3.6
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/path_provider_android-2.2.16/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/path_provider_android-2.2.16/lib/
|
||||
path_provider_foundation
|
||||
3.3
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.1/
|
||||
@@ -478,18 +490,14 @@ posix
|
||||
3.0
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/posix-6.0.1/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/posix-6.0.1/lib/
|
||||
pretty_dio_logger
|
||||
3.0
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/pretty_dio_logger-1.4.0/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/pretty_dio_logger-1.4.0/lib/
|
||||
provider
|
||||
2.12
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/provider-6.1.2/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/provider-6.1.2/lib/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/provider-6.1.4/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/provider-6.1.4/lib/
|
||||
pub_semver
|
||||
3.4
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/pub_semver-2.1.5/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/pub_semver-2.1.5/lib/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/pub_semver-2.2.0/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/pub_semver-2.2.0/lib/
|
||||
pubspec_parse
|
||||
3.6
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/pubspec_parse-1.5.0/
|
||||
@@ -502,10 +510,6 @@ recase
|
||||
2.12
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/recase-4.1.0/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/recase-4.1.0/lib/
|
||||
settings_ui
|
||||
2.12
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/settings_ui-2.0.2/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/settings_ui-2.0.2/lib/
|
||||
share_plus
|
||||
3.4
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/share_plus-10.1.4/
|
||||
@@ -532,16 +536,16 @@ file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/shelf_web_socket-3.0.0/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/shelf_web_socket-3.0.0/lib/
|
||||
slang
|
||||
3.3
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/slang-4.5.0/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/slang-4.5.0/lib/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/slang-4.6.0/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/slang-4.6.0/lib/
|
||||
slang_build_runner
|
||||
3.3
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/slang_build_runner-4.5.0/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/slang_build_runner-4.5.0/lib/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/slang_build_runner-4.6.0/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/slang_build_runner-4.6.0/lib/
|
||||
slang_flutter
|
||||
3.3
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/slang_flutter-4.5.0/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/slang_flutter-4.5.0/lib/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/slang_flutter-4.6.0/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/slang_flutter-4.6.0/lib/
|
||||
source_gen
|
||||
3.6
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/source_gen-2.0.0/
|
||||
@@ -568,12 +572,12 @@ file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/sprintf-7.0.0/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/sprintf-7.0.0/lib/
|
||||
sqlite3
|
||||
3.5
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/sqlite3-2.7.4/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/sqlite3-2.7.4/lib/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/sqlite3-2.7.5/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/sqlite3-2.7.5/lib/
|
||||
sqlite3_flutter_libs
|
||||
2.12
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/sqlite3_flutter_libs-0.5.31/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/sqlite3_flutter_libs-0.5.31/lib/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/sqlite3_flutter_libs-0.5.32/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/sqlite3_flutter_libs-0.5.32/lib/
|
||||
sqlparser
|
||||
3.0
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/sqlparser-0.41.0/
|
||||
@@ -594,6 +598,26 @@ string_scanner
|
||||
3.1
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/string_scanner-1.4.1/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/string_scanner-1.4.1/lib/
|
||||
talker
|
||||
2.17
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/talker-4.7.1/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/talker-4.7.1/lib/
|
||||
talker_bloc_logger
|
||||
2.15
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/talker_bloc_logger-4.7.1/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/talker_bloc_logger-4.7.1/lib/
|
||||
talker_dio_logger
|
||||
2.15
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/talker_dio_logger-4.7.1/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/talker_dio_logger-4.7.1/lib/
|
||||
talker_flutter
|
||||
3.6
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/talker_flutter-4.7.1/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/talker_flutter-4.7.1/lib/
|
||||
talker_logger
|
||||
2.15
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/talker_logger-4.7.1/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/talker_logger-4.7.1/lib/
|
||||
term_glyph
|
||||
3.1
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/term_glyph-1.2.2/
|
||||
@@ -684,8 +708,8 @@ file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/webkit_inspection_proto
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/webkit_inspection_protocol-1.2.1/lib/
|
||||
win32
|
||||
3.7
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/win32-5.11.0/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/win32-5.11.0/lib/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/win32-5.12.0/
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/win32-5.12.0/lib/
|
||||
xdg_directories
|
||||
3.3
|
||||
file:///Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/xdg_directories-1.1.0/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# This is a generated file; do not edit or check into version control.
|
||||
file_picker=/Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/file_picker-9.2.1/
|
||||
file_picker=/Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/file_picker-10.0.0/
|
||||
flutter_displaymode=/Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/flutter_displaymode-0.6.0/
|
||||
flutter_plugin_android_lifecycle=/Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/flutter_plugin_android_lifecycle-2.0.27/
|
||||
flutter_secure_storage=/Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/flutter_secure_storage-9.2.4/
|
||||
@@ -10,12 +10,12 @@ flutter_secure_storage_windows=/Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev
|
||||
fluttertoast=/Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/fluttertoast-8.2.12/
|
||||
package_info_plus=/Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/package_info_plus-8.3.0/
|
||||
path_provider=/Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/path_provider-2.1.5/
|
||||
path_provider_android=/Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/path_provider_android-2.2.15/
|
||||
path_provider_android=/Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/path_provider_android-2.2.16/
|
||||
path_provider_foundation=/Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.1/
|
||||
path_provider_linux=/Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/
|
||||
path_provider_windows=/Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/path_provider_windows-2.3.0/
|
||||
share_plus=/Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/share_plus-10.1.4/
|
||||
sqlite3_flutter_libs=/Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/sqlite3_flutter_libs-0.5.31/
|
||||
sqlite3_flutter_libs=/Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/sqlite3_flutter_libs-0.5.32/
|
||||
url_launcher_linux=/Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/url_launcher_linux-3.2.1/
|
||||
url_launcher_web=/Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/url_launcher_web-2.4.0/
|
||||
url_launcher_windows=/Users/vitalijnecitajlo/.pub-cache/hosted/pub.dev/url_launcher_windows-3.1.4/
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<application
|
||||
android:label="remever"
|
||||
android:name="${applicationName}"
|
||||
android:icon="@mipmap/ic_launcher">
|
||||
android:icon="@mipmap/launcher_icon">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
@@ -20,6 +20,7 @@
|
||||
android:name="io.flutter.embedding.android.NormalTheme"
|
||||
android:resource="@style/NormalTheme"
|
||||
/>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
@@ -30,6 +31,10 @@
|
||||
<meta-data
|
||||
android:name="flutterEmbedding"
|
||||
android:value="2" />
|
||||
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.android.EnableImpeller"
|
||||
android:value="false" />
|
||||
</application>
|
||||
<!-- Required to query activities that can process text, see:
|
||||
https://developer.android.com/training/package-visibility and
|
||||
|
||||
|
After Width: | Height: | Size: 6.0 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 8.1 KiB |
|
After Width: | Height: | Size: 8.1 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 17 KiB |
14
android/app/src/main/res/mipmap-anydpi-v26/launcher_icon.xml
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground>
|
||||
<inset
|
||||
android:drawable="@drawable/ic_launcher_foreground"
|
||||
android:inset="16%" />
|
||||
</foreground>
|
||||
<monochrome>
|
||||
<inset
|
||||
android:drawable="@drawable/ic_launcher_monochrome"
|
||||
android:inset="16%" />
|
||||
</monochrome>
|
||||
</adaptive-icon>
|
||||
BIN
android/app/src/main/res/mipmap-hdpi/launcher_icon.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
android/app/src/main/res/mipmap-mdpi/launcher_icon.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
android/app/src/main/res/mipmap-xhdpi/launcher_icon.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png
Normal file
|
After Width: | Height: | Size: 5.3 KiB |
BIN
android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png
Normal file
|
After Width: | Height: | Size: 7.1 KiB |
4
android/app/src/main/res/values/colors.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#ffffff</color>
|
||||
</resources>
|
||||
BIN
assets/.DS_Store
vendored
BIN
assets/images/app_icon.png
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
assets/images/app_icon_monochrome.png
Normal file
|
After Width: | Height: | Size: 56 KiB |
@@ -428,7 +428,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
@@ -485,7 +485,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
|
||||
@@ -1,122 +1 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"size" : "20x20",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-20x20@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "20x20",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-20x20@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-29x29@1x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-29x29@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-29x29@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"size" : "40x40",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-40x40@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "40x40",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-40x40@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"size" : "60x60",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-60x60@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "60x60",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-60x60@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"size" : "20x20",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-20x20@1x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "20x20",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-20x20@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-29x29@1x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-29x29@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "40x40",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-40x40@1x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "40x40",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-40x40@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "76x76",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-76x76@1x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "76x76",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-76x76@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "83.5x83.5",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-83.5x83.5@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "1024x1024",
|
||||
"idiom" : "ios-marketing",
|
||||
"filename" : "Icon-App-1024x1024@1x.png",
|
||||
"scale" : "1x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
{"images":[{"size":"20x20","idiom":"iphone","filename":"Icon-App-20x20@2x.png","scale":"2x"},{"size":"20x20","idiom":"iphone","filename":"Icon-App-20x20@3x.png","scale":"3x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@2x.png","scale":"2x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@3x.png","scale":"3x"},{"size":"40x40","idiom":"iphone","filename":"Icon-App-40x40@2x.png","scale":"2x"},{"size":"40x40","idiom":"iphone","filename":"Icon-App-40x40@3x.png","scale":"3x"},{"size":"57x57","idiom":"iphone","filename":"Icon-App-57x57@1x.png","scale":"1x"},{"size":"57x57","idiom":"iphone","filename":"Icon-App-57x57@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"Icon-App-60x60@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"Icon-App-60x60@3x.png","scale":"3x"},{"size":"20x20","idiom":"ipad","filename":"Icon-App-20x20@1x.png","scale":"1x"},{"size":"20x20","idiom":"ipad","filename":"Icon-App-20x20@2x.png","scale":"2x"},{"size":"29x29","idiom":"ipad","filename":"Icon-App-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"ipad","filename":"Icon-App-29x29@2x.png","scale":"2x"},{"size":"40x40","idiom":"ipad","filename":"Icon-App-40x40@1x.png","scale":"1x"},{"size":"40x40","idiom":"ipad","filename":"Icon-App-40x40@2x.png","scale":"2x"},{"size":"50x50","idiom":"ipad","filename":"Icon-App-50x50@1x.png","scale":"1x"},{"size":"50x50","idiom":"ipad","filename":"Icon-App-50x50@2x.png","scale":"2x"},{"size":"72x72","idiom":"ipad","filename":"Icon-App-72x72@1x.png","scale":"1x"},{"size":"72x72","idiom":"ipad","filename":"Icon-App-72x72@2x.png","scale":"2x"},{"size":"76x76","idiom":"ipad","filename":"Icon-App-76x76@1x.png","scale":"1x"},{"size":"76x76","idiom":"ipad","filename":"Icon-App-76x76@2x.png","scale":"2x"},{"size":"83.5x83.5","idiom":"ipad","filename":"Icon-App-83.5x83.5@2x.png","scale":"2x"},{"size":"1024x1024","idiom":"ios-marketing","filename":"Icon-App-1024x1024@1x.png","scale":"1x"}],"info":{"version":1,"author":"xcode"}}
|
||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 295 B After Width: | Height: | Size: 447 B |
|
Before Width: | Height: | Size: 406 B After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 450 B After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 282 B After Width: | Height: | Size: 737 B |
|
Before Width: | Height: | Size: 462 B After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 704 B After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 406 B After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 586 B After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 862 B After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 862 B After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 762 B After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 6.1 KiB |
14
lib/app.dart
@@ -18,6 +18,7 @@ import 'package:remever/components/notifiers/app_settings.dart';
|
||||
import 'package:remever/components/listeners/theme_listener.dart';
|
||||
import 'package:remever/i18n/strings.g.dart';
|
||||
import 'package:remever/theme/custom_theme.dart';
|
||||
import 'package:talker_flutter/talker_flutter.dart';
|
||||
|
||||
final Completer<GlobalKey<NavigatorState>> navKeyCompleter =
|
||||
Completer<GlobalKey<NavigatorState>>();
|
||||
@@ -142,13 +143,13 @@ class _MyAppState extends State<MyApp>
|
||||
locale: TranslationProvider.of(context).flutterLocale,
|
||||
supportedLocales: AppLocaleUtils.supportedLocales,
|
||||
localizationsDelegates: GlobalMaterialLocalizations.delegates,
|
||||
routerConfig: globalRouter.config(),
|
||||
|
||||
routerConfig: globalRouter.config(
|
||||
navigatorObservers:
|
||||
() => <NavigatorObserver>[TalkerRouteObserver(talker)],
|
||||
),
|
||||
builder: (BuildContext context, Widget? child) {
|
||||
return MediaQuery(
|
||||
data: MediaQuery.of(
|
||||
context,
|
||||
).copyWith(textScaler: TextScaler.noScaling),
|
||||
child: ChangeNotifierProvider<AppSettingsNotifier>(
|
||||
return ChangeNotifierProvider<AppSettingsNotifier>(
|
||||
create: (_) => settingsNotifier,
|
||||
builder: (BuildContext context, Widget? nchild) {
|
||||
if (nchild != null) return nchild;
|
||||
@@ -177,7 +178,6 @@ class _MyAppState extends State<MyApp>
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -8,6 +8,8 @@ import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
|
||||
import 'package:remever/common/resources.dart';
|
||||
import 'package:remever/components/extensions/context.dart';
|
||||
import 'package:remever/router.dart';
|
||||
import 'package:remever/services/logs/logs_service.dart';
|
||||
import 'package:talker/talker.dart';
|
||||
import 'events/events.dart';
|
||||
|
||||
///
|
||||
@@ -17,6 +19,17 @@ AppRouter get globalRouter {
|
||||
return GetIt.I.get<AppRouter>();
|
||||
}
|
||||
|
||||
///
|
||||
/// Логирование
|
||||
///
|
||||
LogsService get logger {
|
||||
return GetIt.I.get<LogsService>();
|
||||
}
|
||||
|
||||
Talker get talker {
|
||||
return GetIt.I.get<Talker>();
|
||||
}
|
||||
|
||||
///
|
||||
/// Показ тоста
|
||||
///
|
||||
@@ -38,7 +51,19 @@ void showSuccessToast(String text) {
|
||||
toastLength: Toast.LENGTH_SHORT,
|
||||
gravity: ToastGravity.TOP,
|
||||
timeInSecForIosWeb: 1,
|
||||
backgroundColor: AppColors.additional_blue,
|
||||
backgroundColor: Colors.green,
|
||||
textColor: AppColors.white,
|
||||
fontSize: 16,
|
||||
);
|
||||
}
|
||||
|
||||
void showInfoToast(String text) {
|
||||
Fluttertoast.showToast(
|
||||
msg: text,
|
||||
toastLength: Toast.LENGTH_SHORT,
|
||||
gravity: ToastGravity.TOP,
|
||||
timeInSecForIosWeb: 1,
|
||||
backgroundColor: AppColors.disabled,
|
||||
textColor: AppColors.white,
|
||||
fontSize: 16,
|
||||
);
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
// Package imports:
|
||||
import 'package:curl_logger_dio_interceptor/curl_logger_dio_interceptor.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:dio_smart_retry/dio_smart_retry.dart';
|
||||
import 'package:pretty_dio_logger/pretty_dio_logger.dart';
|
||||
import 'package:remever/common/functions.dart';
|
||||
import 'package:talker_dio_logger/talker_dio_logger_interceptor.dart';
|
||||
import 'package:talker_dio_logger/talker_dio_logger_settings.dart';
|
||||
|
||||
// Project imports:
|
||||
import '../../components/env.dart';
|
||||
@@ -42,6 +43,14 @@ InterceptorsWrapper get _error {
|
||||
// String? token = await getIt<AuthService>().refresh();
|
||||
}
|
||||
|
||||
if (error.response != null) {
|
||||
try {
|
||||
showErrorToast(error.response?.data['message']);
|
||||
} catch (_) {
|
||||
showErrorToast(error.response?.data);
|
||||
}
|
||||
}
|
||||
|
||||
handler.next(error);
|
||||
},
|
||||
);
|
||||
@@ -62,15 +71,11 @@ Dio get apiClient {
|
||||
..add(_auth)
|
||||
..add(_error)
|
||||
..add(
|
||||
PrettyDioLogger(
|
||||
request: true,
|
||||
requestBody: true,
|
||||
requestHeader: true,
|
||||
responseBody: true,
|
||||
error: true,
|
||||
TalkerDioLogger(
|
||||
talker: talker,
|
||||
settings: const TalkerDioLoggerSettings(printRequestHeaders: true),
|
||||
),
|
||||
)
|
||||
..add(CurlLoggerDioInterceptor())
|
||||
..add(
|
||||
RetryInterceptor(
|
||||
dio: client,
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
// Package imports:
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:remever/common/functions.dart';
|
||||
import 'package:remever/database/database.dart';
|
||||
import 'package:remever/database/tables.dart';
|
||||
import 'package:remever/models/collection_dto.dart';
|
||||
import 'package:remever/models/crud_collection_dto.dart';
|
||||
|
||||
part 'collections_dao.g.dart';
|
||||
|
||||
@@ -15,24 +16,37 @@ class CollectionsDao extends DatabaseAccessor<AppDatabase>
|
||||
CollectionsDao(super.attachedDatabase);
|
||||
|
||||
/// Получение коллекций из базы данных
|
||||
Stream<List<Collection>> getCollections() {
|
||||
return db.managers.collections.watch();
|
||||
Stream<List<Collection>> getCollections(String? search) {
|
||||
try {
|
||||
return db.managers.collections
|
||||
.filter((f) => f.title.contains(search ?? ''))
|
||||
.watch();
|
||||
} catch (e, st) {
|
||||
logger.logError('Ошибка в методе getCollections', e, st);
|
||||
|
||||
throw ('EXEPTION');
|
||||
}
|
||||
}
|
||||
|
||||
/// Создание коллекции
|
||||
Future<void> createCollection(CollectionDto dto) async {
|
||||
Future<void> createCollection(CrudCollectionDto dto) async {
|
||||
try {
|
||||
await db.managers.collections.create(
|
||||
(o) => o(
|
||||
title: dto.title,
|
||||
desc: dto.desc,
|
||||
isPublic: Value<bool>(dto.isPublic),
|
||||
image: Value<String?>(dto.avatar),
|
||||
image: Value<Uint8List?>(dto.avatar),
|
||||
),
|
||||
);
|
||||
} catch (e, st) {
|
||||
logger.logError('Ошибка в методе createCollection', e, st);
|
||||
}
|
||||
}
|
||||
|
||||
/// Обновление коллекции
|
||||
Future<void> updateCollection(CollectionDto dto, String id) async {
|
||||
Future<void> updateCollection(CrudCollectionDto dto, String id) async {
|
||||
try {
|
||||
await db.managers.collections
|
||||
.filter((f) => f.id(id))
|
||||
.update(
|
||||
@@ -40,13 +54,20 @@ class CollectionsDao extends DatabaseAccessor<AppDatabase>
|
||||
title: Value<String>(dto.title),
|
||||
desc: Value<String>(dto.desc),
|
||||
isPublic: Value<bool>(dto.isPublic),
|
||||
image: Value<String?>(dto.avatar),
|
||||
image: Value<Uint8List?>(dto.avatar),
|
||||
),
|
||||
);
|
||||
} catch (e, st) {
|
||||
logger.logError('Ошибка в методе updateCollection', e, st);
|
||||
}
|
||||
}
|
||||
|
||||
/// Удаление коллекции
|
||||
Future<void> deleteCollection(String id) async {
|
||||
try {
|
||||
await db.managers.collections.filter((f) => f.id(id)).delete();
|
||||
} catch (e, st) {
|
||||
logger.logError('Ошибка в методе deleteCollection', e, st);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
82
lib/database/dao/tickets_dao.dart
Normal file
@@ -0,0 +1,82 @@
|
||||
// Package imports:
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:remever/common/functions.dart';
|
||||
import 'package:remever/database/database.dart';
|
||||
import 'package:remever/database/tables.dart';
|
||||
import 'package:remever/models/create_ticket_dto.dart';
|
||||
|
||||
part 'tickets_dao.g.dart';
|
||||
|
||||
@DriftAccessor(tables: <Type>[Tickets])
|
||||
class TicketsDao extends DatabaseAccessor<AppDatabase> with _$TicketsDaoMixin {
|
||||
///
|
||||
/// Репозиторий для работы с билетами
|
||||
///
|
||||
TicketsDao(super.attachedDatabase);
|
||||
|
||||
/// Получение билетов из базы данных
|
||||
Stream<List<Ticket>> getTickets(String collectionId) {
|
||||
try {
|
||||
return db.managers.tickets
|
||||
.filter((f) => f.collectionId.id(collectionId))
|
||||
.watch();
|
||||
} catch (e, st) {
|
||||
logger.logError('Ошибка в методе getTickets', e, st);
|
||||
|
||||
throw ('EXEPTION');
|
||||
}
|
||||
}
|
||||
|
||||
/// Удаление билета
|
||||
Future<void> removeTicket(String id) async {
|
||||
try {
|
||||
await db.transaction(() {
|
||||
return db.managers.tickets.filter((f) => f.id(id)).delete();
|
||||
});
|
||||
} catch (e, st) {
|
||||
logger.logError('Ошибка в методе deleteTicket', e, st);
|
||||
}
|
||||
}
|
||||
|
||||
/// Создание билета
|
||||
Future<void> createTicket(CreateTicketDto dto) async {
|
||||
try {
|
||||
final TicketsCompanion companion = TicketsCompanion.insert(
|
||||
question: dto.question!,
|
||||
answer: dto.answer!,
|
||||
collectionId: dto.collection!.id,
|
||||
questionImage: Value<Uint8List?>(dto.questionImage),
|
||||
answerImage: Value<Uint8List?>(dto.answerImage),
|
||||
);
|
||||
|
||||
await db.managers.tickets.create((o) => companion);
|
||||
|
||||
if (dto.needRevert != null && dto.needRevert!) {
|
||||
/// Создаем обратную карточку где ответ == вопрос и вопрос == ответ
|
||||
|
||||
final TicketsCompanion revertCompanion = TicketsCompanion.insert(
|
||||
answer: dto.question!,
|
||||
question: dto.answer!,
|
||||
collectionId: dto.collection!.id,
|
||||
answerImage: Value<Uint8List?>(dto.questionImage),
|
||||
questionImage: Value<Uint8List?>(dto.answerImage),
|
||||
);
|
||||
|
||||
await db.managers.tickets.create((o) => revertCompanion);
|
||||
}
|
||||
} catch (e, st) {
|
||||
logger.logError('Ошибка в методе createTicket', e, st);
|
||||
}
|
||||
}
|
||||
|
||||
/// Перенос билета с одной коллекции в другую
|
||||
Future<void> transferTicket(String ticketId, String newCollectionId) async {
|
||||
try {
|
||||
await db.managers.tickets
|
||||
.filter((f) => f.id(ticketId))
|
||||
.update((o) => o(collectionId: Value<String>(newCollectionId)));
|
||||
} catch (e, st) {
|
||||
logger.logError('Ошибка в методе transferTicket', e, st);
|
||||
}
|
||||
}
|
||||
}
|
||||
9
lib/database/dao/tickets_dao.g.dart
Normal file
@@ -0,0 +1,9 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'tickets_dao.dart';
|
||||
|
||||
// ignore_for_file: type=lint
|
||||
mixin _$TicketsDaoMixin on DatabaseAccessor<AppDatabase> {
|
||||
$CollectionsTable get collections => attachedDatabase.collections;
|
||||
$TicketsTable get tickets => attachedDatabase.tickets;
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import 'package:drift/drift.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:remever/database/dao/collections_dao.dart';
|
||||
import 'package:remever/database/dao/tickets_dao.dart';
|
||||
import 'package:remever/database/tables.dart';
|
||||
import 'connection/connection.dart' as impl;
|
||||
import 'package:uuid/uuid.dart';
|
||||
@@ -10,8 +11,8 @@ part 'database.g.dart';
|
||||
|
||||
@DriftDatabase(
|
||||
include: <String>{'sql.drift'},
|
||||
daos: <Type>[CollectionsDao],
|
||||
tables: <Type>[Collections],
|
||||
daos: <Type>[CollectionsDao, TicketsDao],
|
||||
tables: <Type>[Collections, Tickets],
|
||||
)
|
||||
@Singleton()
|
||||
final class AppDatabase extends _$AppDatabase {
|
||||
|
||||
@@ -64,10 +64,24 @@ mixin _Deletable on Table {
|
||||
class Collections extends Table with _UuidPrimaryKey, _Timestampable {
|
||||
TextColumn get title => text()();
|
||||
TextColumn get desc => text()();
|
||||
TextColumn get image => text().nullable()();
|
||||
BlobColumn get image => blob().nullable()();
|
||||
TextColumn get payload => text().nullable()();
|
||||
IntColumn get likesCount => integer().withDefault(Constant(0))();
|
||||
BoolColumn get isLiked => boolean().withDefault(Constant(false))();
|
||||
BoolColumn get isPublic => boolean().withDefault(Constant(false))();
|
||||
BoolColumn get includeInTraining => boolean().withDefault(Constant(false))();
|
||||
}
|
||||
|
||||
///
|
||||
///
|
||||
///
|
||||
@DataClassName('Ticket')
|
||||
class Tickets extends Table with _UuidPrimaryKey, _Timestampable {
|
||||
TextColumn get question => text()();
|
||||
BlobColumn get questionImage => blob().named('question_image').nullable()();
|
||||
TextColumn get answer => text()();
|
||||
BlobColumn get answerImage => blob().named('answer_image').nullable()();
|
||||
TextColumn get collectionId =>
|
||||
text().references(Collections, #id, onDelete: KeyAction.cascade)();
|
||||
RealColumn get progress => real().withDefault(Constant(0))();
|
||||
}
|
||||
|
||||
@@ -307,6 +307,14 @@ class $AssetsImagesGen {
|
||||
AssetGenImage get propertyYA =>
|
||||
const AssetGenImage('assets/images/Property YA.png');
|
||||
|
||||
/// File path: assets/images/app_icon.png
|
||||
AssetGenImage get appIcon =>
|
||||
const AssetGenImage('assets/images/app_icon.png');
|
||||
|
||||
/// File path: assets/images/app_icon_monochrome.png
|
||||
AssetGenImage get appIconMonochrome =>
|
||||
const AssetGenImage('assets/images/app_icon_monochrome.png');
|
||||
|
||||
/// File path: assets/images/img.png
|
||||
AssetGenImage get img => const AssetGenImage('assets/images/img.png');
|
||||
|
||||
@@ -333,6 +341,8 @@ class $AssetsImagesGen {
|
||||
propertyTG,
|
||||
propertyVK,
|
||||
propertyYA,
|
||||
appIcon,
|
||||
appIconMonochrome,
|
||||
img,
|
||||
imgCard,
|
||||
logo,
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
/// Locales: 2
|
||||
/// Strings: 20 (10 per locale)
|
||||
///
|
||||
/// Built on 2025-03-25 at 18:21 UTC
|
||||
/// Built on 2025-04-02 at 18:04 UTC
|
||||
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: type=lint, unused_import
|
||||
|
||||
@@ -13,9 +13,9 @@ import 'strings.g.dart';
|
||||
class TranslationsEn extends Translations {
|
||||
/// You can call this constructor and build your own translation instance of this locale.
|
||||
/// Constructing via the enum [AppLocale.build] is preferred.
|
||||
TranslationsEn({Map<String, Node>? overrides, PluralResolver? cardinalResolver, PluralResolver? ordinalResolver})
|
||||
TranslationsEn({Map<String, Node>? overrides, PluralResolver? cardinalResolver, PluralResolver? ordinalResolver, TranslationMetadata<AppLocale, Translations>? meta})
|
||||
: assert(overrides == null, 'Set "translation_overrides: true" in order to enable this feature.'),
|
||||
$meta = TranslationMetadata(
|
||||
$meta = meta ?? TranslationMetadata(
|
||||
locale: AppLocale.en,
|
||||
overrides: overrides ?? {},
|
||||
cardinalResolver: cardinalResolver,
|
||||
@@ -34,6 +34,9 @@ class TranslationsEn extends Translations {
|
||||
|
||||
late final TranslationsEn _root = this; // ignore: unused_field
|
||||
|
||||
@override
|
||||
TranslationsEn $copyWith({TranslationMetadata<AppLocale, Translations>? meta}) => TranslationsEn(meta: meta ?? this.$meta);
|
||||
|
||||
// Translations
|
||||
@override String get app_name => 'Tesmit';
|
||||
@override late final _TranslationsHomeScreenEn home_screen = _TranslationsHomeScreenEn._(_root);
|
||||
|
||||
@@ -17,9 +17,9 @@ class Translations implements BaseTranslations<AppLocale, Translations> {
|
||||
|
||||
/// You can call this constructor and build your own translation instance of this locale.
|
||||
/// Constructing via the enum [AppLocale.build] is preferred.
|
||||
Translations({Map<String, Node>? overrides, PluralResolver? cardinalResolver, PluralResolver? ordinalResolver})
|
||||
Translations({Map<String, Node>? overrides, PluralResolver? cardinalResolver, PluralResolver? ordinalResolver, TranslationMetadata<AppLocale, Translations>? meta})
|
||||
: assert(overrides == null, 'Set "translation_overrides: true" in order to enable this feature.'),
|
||||
$meta = TranslationMetadata(
|
||||
$meta = meta ?? TranslationMetadata(
|
||||
locale: AppLocale.ru,
|
||||
overrides: overrides ?? {},
|
||||
cardinalResolver: cardinalResolver,
|
||||
@@ -36,6 +36,8 @@ class Translations implements BaseTranslations<AppLocale, Translations> {
|
||||
|
||||
late final Translations _root = this; // ignore: unused_field
|
||||
|
||||
Translations $copyWith({TranslationMetadata<AppLocale, Translations>? meta}) => Translations(meta: meta ?? this.$meta);
|
||||
|
||||
// Translations
|
||||
String get app_name => 'Tesmit';
|
||||
late final TranslationsHomeScreenRu home_screen = TranslationsHomeScreenRu.internal(_root);
|
||||
|
||||
@@ -20,6 +20,9 @@ import 'services/collection/collections_service.dart' as _i1001;
|
||||
import 'services/core/enc_keys_service.dart' as _i439;
|
||||
import 'services/core/lang_service.dart' as _i68;
|
||||
import 'services/core/theme_service.dart' as _i84;
|
||||
import 'services/logs/logs_service.dart' as _i393;
|
||||
import 'services/tickets/tickets_interface.dart' as _i147;
|
||||
import 'services/tickets/tickets_service.dart' as _i548;
|
||||
import 'services/warmup_service.dart' as _i564;
|
||||
|
||||
extension GetItInjectableX on _i174.GetIt {
|
||||
@@ -33,6 +36,8 @@ extension GetItInjectableX on _i174.GetIt {
|
||||
gh.factory<_i439.EncKeysService>(() => _i439.EncKeysService());
|
||||
gh.factory<_i84.ThemeService>(() => _i84.ThemeService());
|
||||
gh.singleton<_i565.AppDatabase>(() => _i565.AppDatabase());
|
||||
gh.singleton<_i393.LogsService>(() => _i393.LogsService());
|
||||
gh.singleton<_i147.TicketsInterface>(() => _i548.TicketsService());
|
||||
gh.singleton<_i764.CollectionsInterface>(() => _i1001.CollectionsService());
|
||||
gh.singleton<_i580.AuthInterface>(() => _i975.AuthService());
|
||||
await gh.singletonAsync<_i564.WarmupService>(() {
|
||||
|
||||
@@ -2,27 +2,36 @@ import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:hive_ce_flutter/hive_flutter.dart';
|
||||
import 'package:remever/app.dart';
|
||||
import 'package:remever/common/functions.dart';
|
||||
import 'package:remever/env.dart';
|
||||
import 'package:remever/i18n/strings.g.dart';
|
||||
import 'package:remever/inject.dart';
|
||||
import 'package:remever/router.dart';
|
||||
import 'package:remever/services/logs/custom_history.dart';
|
||||
import 'package:remever/services/warmup_service.dart';
|
||||
import 'package:talker/talker.dart';
|
||||
import 'package:talker_bloc_logger/talker_bloc_logger_observer.dart';
|
||||
import 'package:talker_bloc_logger/talker_bloc_logger_settings.dart';
|
||||
|
||||
void _onError(Object error, StackTrace trace) {
|
||||
debugPrint('error ${error.toString()}');
|
||||
logger.logError('main _onError', error, trace);
|
||||
}
|
||||
|
||||
void _flutterError(FlutterErrorDetails details) {
|
||||
debugPrint('error ${details.context}');
|
||||
logger.logError('main _flutterError', details.exception, details.stack);
|
||||
}
|
||||
|
||||
bool _platformDispatcher(Object exception, StackTrace stackTrace) {
|
||||
if ('$exception'.toLowerCase().contains('dio')) return true;
|
||||
|
||||
debugPrint('error ${exception.toString()}');
|
||||
logger.logError('main _platformDispatcher', exception, stackTrace);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -43,10 +52,26 @@ void main() {
|
||||
FlutterError.onError = _flutterError;
|
||||
PlatformDispatcher.instance.onError = _platformDispatcher;
|
||||
|
||||
final Talker talker = Talker(
|
||||
history: CustomHistory(
|
||||
TalkerSettings(),
|
||||
history: await logger.getHistory(),
|
||||
),
|
||||
);
|
||||
|
||||
getIt.registerSingleton<AppRouter>(AppRouter());
|
||||
getIt.registerSingleton<Talker>(talker);
|
||||
|
||||
await getIt<WarmupService>().firstStart();
|
||||
|
||||
Bloc.observer = TalkerBlocObserver(
|
||||
talker: talker,
|
||||
settings: const TalkerBlocLoggerSettings(
|
||||
printCreations: true,
|
||||
printClosings: true,
|
||||
),
|
||||
);
|
||||
|
||||
runApp(TranslationProvider(child: const MyApp()));
|
||||
}, _onError);
|
||||
}
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
// To parse this JSON data, do
|
||||
//
|
||||
// final collectionDto = collectionDtoFromJson(jsonString);
|
||||
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'dart:convert';
|
||||
|
||||
part 'collection_dto.freezed.dart';
|
||||
part 'collection_dto.g.dart';
|
||||
|
||||
CollectionDto collectionDtoFromJson(String str) =>
|
||||
CollectionDto.fromJson(json.decode(str));
|
||||
|
||||
String collectionDtoToJson(CollectionDto data) => json.encode(data.toJson());
|
||||
|
||||
@freezed
|
||||
class CollectionDto with _$CollectionDto {
|
||||
const factory CollectionDto({
|
||||
required String desc,
|
||||
required String title,
|
||||
required bool isPublic,
|
||||
String? avatar,
|
||||
}) = _CollectionDto;
|
||||
|
||||
factory CollectionDto.fromJson(Map<String, dynamic> json) =>
|
||||
_$CollectionDtoFromJson(json);
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'collection_dto.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_$CollectionDtoImpl _$$CollectionDtoImplFromJson(Map<String, dynamic> json) =>
|
||||
_$CollectionDtoImpl(
|
||||
desc: json['desc'] as String,
|
||||
title: json['title'] as String,
|
||||
isPublic: json['isPublic'] as bool,
|
||||
avatar: json['avatar'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$CollectionDtoImplToJson(_$CollectionDtoImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'desc': instance.desc,
|
||||
'title': instance.title,
|
||||
'isPublic': instance.isPublic,
|
||||
'avatar': instance.avatar,
|
||||
};
|
||||
22
lib/models/create_ticket_dto.dart
Normal file
@@ -0,0 +1,22 @@
|
||||
// To parse this JSON data, do
|
||||
//
|
||||
// final collectionDto = collectionDtoFromJson(jsonString);
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:remever/database/database.dart';
|
||||
|
||||
part 'create_ticket_dto.freezed.dart';
|
||||
|
||||
@Freezed(copyWith: true, equal: true, fromJson: false, toJson: false)
|
||||
abstract class CreateTicketDto with _$CreateTicketDto {
|
||||
const factory CreateTicketDto({
|
||||
Collection? collection,
|
||||
String? question,
|
||||
Uint8List? questionImage,
|
||||
String? answer,
|
||||
Uint8List? answerImage,
|
||||
bool? needRevert,
|
||||
}) = _CreateTicketDto;
|
||||
}
|
||||
294
lib/models/create_ticket_dto.freezed.dart
Normal file
@@ -0,0 +1,294 @@
|
||||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'create_ticket_dto.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
final _privateConstructorUsedError = UnsupportedError(
|
||||
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models',
|
||||
);
|
||||
|
||||
/// @nodoc
|
||||
mixin _$CreateTicketDto {
|
||||
Collection? get collection => throw _privateConstructorUsedError;
|
||||
String? get question => throw _privateConstructorUsedError;
|
||||
Uint8List? get questionImage => throw _privateConstructorUsedError;
|
||||
String? get answer => throw _privateConstructorUsedError;
|
||||
Uint8List? get answerImage => throw _privateConstructorUsedError;
|
||||
bool? get needRevert => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of CreateTicketDto
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$CreateTicketDtoCopyWith<CreateTicketDto> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $CreateTicketDtoCopyWith<$Res> {
|
||||
factory $CreateTicketDtoCopyWith(
|
||||
CreateTicketDto value,
|
||||
$Res Function(CreateTicketDto) then,
|
||||
) = _$CreateTicketDtoCopyWithImpl<$Res, CreateTicketDto>;
|
||||
@useResult
|
||||
$Res call({
|
||||
Collection? collection,
|
||||
String? question,
|
||||
Uint8List? questionImage,
|
||||
String? answer,
|
||||
Uint8List? answerImage,
|
||||
bool? needRevert,
|
||||
});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$CreateTicketDtoCopyWithImpl<$Res, $Val extends CreateTicketDto>
|
||||
implements $CreateTicketDtoCopyWith<$Res> {
|
||||
_$CreateTicketDtoCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of CreateTicketDto
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? collection = freezed,
|
||||
Object? question = freezed,
|
||||
Object? questionImage = freezed,
|
||||
Object? answer = freezed,
|
||||
Object? answerImage = freezed,
|
||||
Object? needRevert = freezed,
|
||||
}) {
|
||||
return _then(
|
||||
_value.copyWith(
|
||||
collection:
|
||||
freezed == collection
|
||||
? _value.collection
|
||||
: collection // ignore: cast_nullable_to_non_nullable
|
||||
as Collection?,
|
||||
question:
|
||||
freezed == question
|
||||
? _value.question
|
||||
: question // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
questionImage:
|
||||
freezed == questionImage
|
||||
? _value.questionImage
|
||||
: questionImage // ignore: cast_nullable_to_non_nullable
|
||||
as Uint8List?,
|
||||
answer:
|
||||
freezed == answer
|
||||
? _value.answer
|
||||
: answer // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
answerImage:
|
||||
freezed == answerImage
|
||||
? _value.answerImage
|
||||
: answerImage // ignore: cast_nullable_to_non_nullable
|
||||
as Uint8List?,
|
||||
needRevert:
|
||||
freezed == needRevert
|
||||
? _value.needRevert
|
||||
: needRevert // ignore: cast_nullable_to_non_nullable
|
||||
as bool?,
|
||||
)
|
||||
as $Val,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$CreateTicketDtoImplCopyWith<$Res>
|
||||
implements $CreateTicketDtoCopyWith<$Res> {
|
||||
factory _$$CreateTicketDtoImplCopyWith(
|
||||
_$CreateTicketDtoImpl value,
|
||||
$Res Function(_$CreateTicketDtoImpl) then,
|
||||
) = __$$CreateTicketDtoImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({
|
||||
Collection? collection,
|
||||
String? question,
|
||||
Uint8List? questionImage,
|
||||
String? answer,
|
||||
Uint8List? answerImage,
|
||||
bool? needRevert,
|
||||
});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$CreateTicketDtoImplCopyWithImpl<$Res>
|
||||
extends _$CreateTicketDtoCopyWithImpl<$Res, _$CreateTicketDtoImpl>
|
||||
implements _$$CreateTicketDtoImplCopyWith<$Res> {
|
||||
__$$CreateTicketDtoImplCopyWithImpl(
|
||||
_$CreateTicketDtoImpl _value,
|
||||
$Res Function(_$CreateTicketDtoImpl) _then,
|
||||
) : super(_value, _then);
|
||||
|
||||
/// Create a copy of CreateTicketDto
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? collection = freezed,
|
||||
Object? question = freezed,
|
||||
Object? questionImage = freezed,
|
||||
Object? answer = freezed,
|
||||
Object? answerImage = freezed,
|
||||
Object? needRevert = freezed,
|
||||
}) {
|
||||
return _then(
|
||||
_$CreateTicketDtoImpl(
|
||||
collection:
|
||||
freezed == collection
|
||||
? _value.collection
|
||||
: collection // ignore: cast_nullable_to_non_nullable
|
||||
as Collection?,
|
||||
question:
|
||||
freezed == question
|
||||
? _value.question
|
||||
: question // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
questionImage:
|
||||
freezed == questionImage
|
||||
? _value.questionImage
|
||||
: questionImage // ignore: cast_nullable_to_non_nullable
|
||||
as Uint8List?,
|
||||
answer:
|
||||
freezed == answer
|
||||
? _value.answer
|
||||
: answer // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
answerImage:
|
||||
freezed == answerImage
|
||||
? _value.answerImage
|
||||
: answerImage // ignore: cast_nullable_to_non_nullable
|
||||
as Uint8List?,
|
||||
needRevert:
|
||||
freezed == needRevert
|
||||
? _value.needRevert
|
||||
: needRevert // ignore: cast_nullable_to_non_nullable
|
||||
as bool?,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$CreateTicketDtoImpl implements _CreateTicketDto {
|
||||
const _$CreateTicketDtoImpl({
|
||||
this.collection,
|
||||
this.question,
|
||||
this.questionImage,
|
||||
this.answer,
|
||||
this.answerImage,
|
||||
this.needRevert,
|
||||
});
|
||||
|
||||
@override
|
||||
final Collection? collection;
|
||||
@override
|
||||
final String? question;
|
||||
@override
|
||||
final Uint8List? questionImage;
|
||||
@override
|
||||
final String? answer;
|
||||
@override
|
||||
final Uint8List? answerImage;
|
||||
@override
|
||||
final bool? needRevert;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'CreateTicketDto(collection: $collection, question: $question, questionImage: $questionImage, answer: $answer, answerImage: $answerImage, needRevert: $needRevert)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$CreateTicketDtoImpl &&
|
||||
const DeepCollectionEquality().equals(
|
||||
other.collection,
|
||||
collection,
|
||||
) &&
|
||||
(identical(other.question, question) ||
|
||||
other.question == question) &&
|
||||
const DeepCollectionEquality().equals(
|
||||
other.questionImage,
|
||||
questionImage,
|
||||
) &&
|
||||
(identical(other.answer, answer) || other.answer == answer) &&
|
||||
const DeepCollectionEquality().equals(
|
||||
other.answerImage,
|
||||
answerImage,
|
||||
) &&
|
||||
(identical(other.needRevert, needRevert) ||
|
||||
other.needRevert == needRevert));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
const DeepCollectionEquality().hash(collection),
|
||||
question,
|
||||
const DeepCollectionEquality().hash(questionImage),
|
||||
answer,
|
||||
const DeepCollectionEquality().hash(answerImage),
|
||||
needRevert,
|
||||
);
|
||||
|
||||
/// Create a copy of CreateTicketDto
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$CreateTicketDtoImplCopyWith<_$CreateTicketDtoImpl> get copyWith =>
|
||||
__$$CreateTicketDtoImplCopyWithImpl<_$CreateTicketDtoImpl>(
|
||||
this,
|
||||
_$identity,
|
||||
);
|
||||
}
|
||||
|
||||
abstract class _CreateTicketDto implements CreateTicketDto {
|
||||
const factory _CreateTicketDto({
|
||||
final Collection? collection,
|
||||
final String? question,
|
||||
final Uint8List? questionImage,
|
||||
final String? answer,
|
||||
final Uint8List? answerImage,
|
||||
final bool? needRevert,
|
||||
}) = _$CreateTicketDtoImpl;
|
||||
|
||||
@override
|
||||
Collection? get collection;
|
||||
@override
|
||||
String? get question;
|
||||
@override
|
||||
Uint8List? get questionImage;
|
||||
@override
|
||||
String? get answer;
|
||||
@override
|
||||
Uint8List? get answerImage;
|
||||
@override
|
||||
bool? get needRevert;
|
||||
|
||||
/// Create a copy of CreateTicketDto
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$CreateTicketDtoImplCopyWith<_$CreateTicketDtoImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
19
lib/models/crud_collection_dto.dart
Normal file
@@ -0,0 +1,19 @@
|
||||
// To parse this JSON data, do
|
||||
//
|
||||
// final collectionDto = collectionDtoFromJson(jsonString);
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'crud_collection_dto.freezed.dart';
|
||||
|
||||
@Freezed(copyWith: true, equal: true, fromJson: false, toJson: false)
|
||||
abstract class CrudCollectionDto with _$CrudCollectionDto {
|
||||
const factory CrudCollectionDto({
|
||||
required String desc,
|
||||
required String title,
|
||||
required bool isPublic,
|
||||
Uint8List? avatar,
|
||||
}) = _CrudCollectionDto;
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'collection_dto.dart';
|
||||
part of 'crud_collection_dto.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
@@ -15,48 +15,41 @@ final _privateConstructorUsedError = UnsupportedError(
|
||||
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models',
|
||||
);
|
||||
|
||||
CollectionDto _$CollectionDtoFromJson(Map<String, dynamic> json) {
|
||||
return _CollectionDto.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$CollectionDto {
|
||||
mixin _$CrudCollectionDto {
|
||||
String get desc => throw _privateConstructorUsedError;
|
||||
String get title => throw _privateConstructorUsedError;
|
||||
bool get isPublic => throw _privateConstructorUsedError;
|
||||
String? get avatar => throw _privateConstructorUsedError;
|
||||
Uint8List? get avatar => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this CollectionDto to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of CollectionDto
|
||||
/// Create a copy of CrudCollectionDto
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$CollectionDtoCopyWith<CollectionDto> get copyWith =>
|
||||
$CrudCollectionDtoCopyWith<CrudCollectionDto> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $CollectionDtoCopyWith<$Res> {
|
||||
factory $CollectionDtoCopyWith(
|
||||
CollectionDto value,
|
||||
$Res Function(CollectionDto) then,
|
||||
) = _$CollectionDtoCopyWithImpl<$Res, CollectionDto>;
|
||||
abstract class $CrudCollectionDtoCopyWith<$Res> {
|
||||
factory $CrudCollectionDtoCopyWith(
|
||||
CrudCollectionDto value,
|
||||
$Res Function(CrudCollectionDto) then,
|
||||
) = _$CrudCollectionDtoCopyWithImpl<$Res, CrudCollectionDto>;
|
||||
@useResult
|
||||
$Res call({String desc, String title, bool isPublic, String? avatar});
|
||||
$Res call({String desc, String title, bool isPublic, Uint8List? avatar});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$CollectionDtoCopyWithImpl<$Res, $Val extends CollectionDto>
|
||||
implements $CollectionDtoCopyWith<$Res> {
|
||||
_$CollectionDtoCopyWithImpl(this._value, this._then);
|
||||
class _$CrudCollectionDtoCopyWithImpl<$Res, $Val extends CrudCollectionDto>
|
||||
implements $CrudCollectionDtoCopyWith<$Res> {
|
||||
_$CrudCollectionDtoCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of CollectionDto
|
||||
/// Create a copy of CrudCollectionDto
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
@@ -87,7 +80,7 @@ class _$CollectionDtoCopyWithImpl<$Res, $Val extends CollectionDto>
|
||||
freezed == avatar
|
||||
? _value.avatar
|
||||
: avatar // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
as Uint8List?,
|
||||
)
|
||||
as $Val,
|
||||
);
|
||||
@@ -95,27 +88,27 @@ class _$CollectionDtoCopyWithImpl<$Res, $Val extends CollectionDto>
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$CollectionDtoImplCopyWith<$Res>
|
||||
implements $CollectionDtoCopyWith<$Res> {
|
||||
factory _$$CollectionDtoImplCopyWith(
|
||||
_$CollectionDtoImpl value,
|
||||
$Res Function(_$CollectionDtoImpl) then,
|
||||
) = __$$CollectionDtoImplCopyWithImpl<$Res>;
|
||||
abstract class _$$CrudCollectionDtoImplCopyWith<$Res>
|
||||
implements $CrudCollectionDtoCopyWith<$Res> {
|
||||
factory _$$CrudCollectionDtoImplCopyWith(
|
||||
_$CrudCollectionDtoImpl value,
|
||||
$Res Function(_$CrudCollectionDtoImpl) then,
|
||||
) = __$$CrudCollectionDtoImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({String desc, String title, bool isPublic, String? avatar});
|
||||
$Res call({String desc, String title, bool isPublic, Uint8List? avatar});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$CollectionDtoImplCopyWithImpl<$Res>
|
||||
extends _$CollectionDtoCopyWithImpl<$Res, _$CollectionDtoImpl>
|
||||
implements _$$CollectionDtoImplCopyWith<$Res> {
|
||||
__$$CollectionDtoImplCopyWithImpl(
|
||||
_$CollectionDtoImpl _value,
|
||||
$Res Function(_$CollectionDtoImpl) _then,
|
||||
class __$$CrudCollectionDtoImplCopyWithImpl<$Res>
|
||||
extends _$CrudCollectionDtoCopyWithImpl<$Res, _$CrudCollectionDtoImpl>
|
||||
implements _$$CrudCollectionDtoImplCopyWith<$Res> {
|
||||
__$$CrudCollectionDtoImplCopyWithImpl(
|
||||
_$CrudCollectionDtoImpl _value,
|
||||
$Res Function(_$CrudCollectionDtoImpl) _then,
|
||||
) : super(_value, _then);
|
||||
|
||||
/// Create a copy of CollectionDto
|
||||
/// Create a copy of CrudCollectionDto
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
@@ -126,7 +119,7 @@ class __$$CollectionDtoImplCopyWithImpl<$Res>
|
||||
Object? avatar = freezed,
|
||||
}) {
|
||||
return _then(
|
||||
_$CollectionDtoImpl(
|
||||
_$CrudCollectionDtoImpl(
|
||||
desc:
|
||||
null == desc
|
||||
? _value.desc
|
||||
@@ -146,25 +139,22 @@ class __$$CollectionDtoImplCopyWithImpl<$Res>
|
||||
freezed == avatar
|
||||
? _value.avatar
|
||||
: avatar // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
as Uint8List?,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$CollectionDtoImpl implements _CollectionDto {
|
||||
const _$CollectionDtoImpl({
|
||||
|
||||
class _$CrudCollectionDtoImpl implements _CrudCollectionDto {
|
||||
const _$CrudCollectionDtoImpl({
|
||||
required this.desc,
|
||||
required this.title,
|
||||
required this.isPublic,
|
||||
this.avatar,
|
||||
});
|
||||
|
||||
factory _$CollectionDtoImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$CollectionDtoImplFromJson(json);
|
||||
|
||||
@override
|
||||
final String desc;
|
||||
@override
|
||||
@@ -172,53 +162,53 @@ class _$CollectionDtoImpl implements _CollectionDto {
|
||||
@override
|
||||
final bool isPublic;
|
||||
@override
|
||||
final String? avatar;
|
||||
final Uint8List? avatar;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'CollectionDto(desc: $desc, title: $title, isPublic: $isPublic, avatar: $avatar)';
|
||||
return 'CrudCollectionDto(desc: $desc, title: $title, isPublic: $isPublic, avatar: $avatar)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$CollectionDtoImpl &&
|
||||
other is _$CrudCollectionDtoImpl &&
|
||||
(identical(other.desc, desc) || other.desc == desc) &&
|
||||
(identical(other.title, title) || other.title == title) &&
|
||||
(identical(other.isPublic, isPublic) ||
|
||||
other.isPublic == isPublic) &&
|
||||
(identical(other.avatar, avatar) || other.avatar == avatar));
|
||||
const DeepCollectionEquality().equals(other.avatar, avatar));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, desc, title, isPublic, avatar);
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
desc,
|
||||
title,
|
||||
isPublic,
|
||||
const DeepCollectionEquality().hash(avatar),
|
||||
);
|
||||
|
||||
/// Create a copy of CollectionDto
|
||||
/// Create a copy of CrudCollectionDto
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$CollectionDtoImplCopyWith<_$CollectionDtoImpl> get copyWith =>
|
||||
__$$CollectionDtoImplCopyWithImpl<_$CollectionDtoImpl>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$CollectionDtoImplToJson(this);
|
||||
}
|
||||
_$$CrudCollectionDtoImplCopyWith<_$CrudCollectionDtoImpl> get copyWith =>
|
||||
__$$CrudCollectionDtoImplCopyWithImpl<_$CrudCollectionDtoImpl>(
|
||||
this,
|
||||
_$identity,
|
||||
);
|
||||
}
|
||||
|
||||
abstract class _CollectionDto implements CollectionDto {
|
||||
const factory _CollectionDto({
|
||||
abstract class _CrudCollectionDto implements CrudCollectionDto {
|
||||
const factory _CrudCollectionDto({
|
||||
required final String desc,
|
||||
required final String title,
|
||||
required final bool isPublic,
|
||||
final String? avatar,
|
||||
}) = _$CollectionDtoImpl;
|
||||
|
||||
factory _CollectionDto.fromJson(Map<String, dynamic> json) =
|
||||
_$CollectionDtoImpl.fromJson;
|
||||
final Uint8List? avatar,
|
||||
}) = _$CrudCollectionDtoImpl;
|
||||
|
||||
@override
|
||||
String get desc;
|
||||
@@ -227,12 +217,12 @@ abstract class _CollectionDto implements CollectionDto {
|
||||
@override
|
||||
bool get isPublic;
|
||||
@override
|
||||
String? get avatar;
|
||||
Uint8List? get avatar;
|
||||
|
||||
/// Create a copy of CollectionDto
|
||||
/// Create a copy of CrudCollectionDto
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$CollectionDtoImplCopyWith<_$CollectionDtoImpl> get copyWith =>
|
||||
_$$CrudCollectionDtoImplCopyWith<_$CrudCollectionDtoImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
15
lib/models/logs/build_log.dart
Normal file
@@ -0,0 +1,15 @@
|
||||
// Package imports:
|
||||
import 'package:talker/talker.dart';
|
||||
|
||||
class BuildLog extends TalkerLog {
|
||||
BuildLog(String super.message);
|
||||
|
||||
@override
|
||||
String get title => 'Build';
|
||||
|
||||
@override
|
||||
String? get key => 'build_log_key';
|
||||
|
||||
@override
|
||||
AnsiPen get pen => AnsiPen()..xterm(121);
|
||||
}
|
||||
@@ -23,6 +23,7 @@ class AppRouter extends RootStackRouter {
|
||||
),
|
||||
|
||||
AutoRoute(path: '/crud_collection', page: CrudCollectionRoute.page),
|
||||
AutoRoute(path: '/collection_search', page: CollectionSearchRoute.page),
|
||||
AutoRoute(path: '/crudFullField', page: CrudCollectionFullscreenField.page),
|
||||
AutoRoute(path: '/collection_details', page: CollectionDetailRoute.page),
|
||||
// AutoRoute(path: '/addTags', page: AddTagsRoute.page),
|
||||
|
||||
@@ -9,32 +9,34 @@
|
||||
// coverage:ignore-file
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'package:auto_route/auto_route.dart' as _i12;
|
||||
import 'package:flutter/cupertino.dart' as _i13;
|
||||
import 'package:remever/database/database.dart' as _i14;
|
||||
import 'package:auto_route/auto_route.dart' as _i13;
|
||||
import 'package:flutter/cupertino.dart' as _i14;
|
||||
import 'package:remever/database/database.dart' as _i15;
|
||||
import 'package:remever/screens/auth/auth_screen.dart' as _i1;
|
||||
import 'package:remever/screens/collections/collection_detail_screen.dart'
|
||||
as _i2;
|
||||
import 'package:remever/screens/collections/collection_search_screen.dart'
|
||||
as _i4;
|
||||
import 'package:remever/screens/collections/collections_screen.dart' as _i3;
|
||||
import 'package:remever/screens/create_card/create_screen.dart' as _i4;
|
||||
import 'package:remever/screens/crud_collection/crud_collection.dart' as _i6;
|
||||
import 'package:remever/screens/create_card/create_screen.dart' as _i5;
|
||||
import 'package:remever/screens/crud_collection/crud_collection.dart' as _i7;
|
||||
import 'package:remever/screens/crud_collection/widgets/crud_collection_fullscreen_field.dart'
|
||||
as _i5;
|
||||
import 'package:remever/screens/home/home_screen.dart' as _i7;
|
||||
import 'package:remever/screens/sandbox/sandbox_screen.dart' as _i8;
|
||||
import 'package:remever/screens/settings/settings_screen.dart' as _i9;
|
||||
import 'package:remever/screens/splash/splash_screen.dart' as _i10;
|
||||
import 'package:remever/screens/statistick/statistick_screen.dart' as _i11;
|
||||
as _i6;
|
||||
import 'package:remever/screens/home/home_screen.dart' as _i8;
|
||||
import 'package:remever/screens/sandbox/sandbox_screen.dart' as _i9;
|
||||
import 'package:remever/screens/settings/settings_screen.dart' as _i10;
|
||||
import 'package:remever/screens/splash/splash_screen.dart' as _i11;
|
||||
import 'package:remever/screens/statistick/statistick_screen.dart' as _i12;
|
||||
|
||||
/// generated route for
|
||||
/// [_i1.AuthScreen]
|
||||
class AuthRoute extends _i12.PageRouteInfo<void> {
|
||||
const AuthRoute({List<_i12.PageRouteInfo>? children})
|
||||
class AuthRoute extends _i13.PageRouteInfo<void> {
|
||||
const AuthRoute({List<_i13.PageRouteInfo>? children})
|
||||
: super(AuthRoute.name, initialChildren: children);
|
||||
|
||||
static const String name = 'AuthRoute';
|
||||
|
||||
static _i12.PageInfo page = _i12.PageInfo(
|
||||
static _i13.PageInfo page = _i13.PageInfo(
|
||||
name,
|
||||
builder: (data) {
|
||||
return const _i1.AuthScreen();
|
||||
@@ -44,29 +46,54 @@ class AuthRoute extends _i12.PageRouteInfo<void> {
|
||||
|
||||
/// generated route for
|
||||
/// [_i2.CollectionDetailScreen]
|
||||
class CollectionDetailRoute extends _i12.PageRouteInfo<void> {
|
||||
const CollectionDetailRoute({List<_i12.PageRouteInfo>? children})
|
||||
: super(CollectionDetailRoute.name, initialChildren: children);
|
||||
class CollectionDetailRoute
|
||||
extends _i13.PageRouteInfo<CollectionDetailRouteArgs> {
|
||||
CollectionDetailRoute({
|
||||
_i14.Key? key,
|
||||
required _i15.Collection collection,
|
||||
List<_i13.PageRouteInfo>? children,
|
||||
}) : super(
|
||||
CollectionDetailRoute.name,
|
||||
args: CollectionDetailRouteArgs(key: key, collection: collection),
|
||||
initialChildren: children,
|
||||
);
|
||||
|
||||
static const String name = 'CollectionDetailRoute';
|
||||
|
||||
static _i12.PageInfo page = _i12.PageInfo(
|
||||
static _i13.PageInfo page = _i13.PageInfo(
|
||||
name,
|
||||
builder: (data) {
|
||||
return const _i2.CollectionDetailScreen();
|
||||
final args = data.argsAs<CollectionDetailRouteArgs>();
|
||||
return _i2.CollectionDetailScreen(
|
||||
key: args.key,
|
||||
collection: args.collection,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
class CollectionDetailRouteArgs {
|
||||
const CollectionDetailRouteArgs({this.key, required this.collection});
|
||||
|
||||
final _i14.Key? key;
|
||||
|
||||
final _i15.Collection collection;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'CollectionDetailRouteArgs{key: $key, collection: $collection}';
|
||||
}
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [_i3.CollectionScreen]
|
||||
class CollectionRoute extends _i12.PageRouteInfo<void> {
|
||||
const CollectionRoute({List<_i12.PageRouteInfo>? children})
|
||||
class CollectionRoute extends _i13.PageRouteInfo<void> {
|
||||
const CollectionRoute({List<_i13.PageRouteInfo>? children})
|
||||
: super(CollectionRoute.name, initialChildren: children);
|
||||
|
||||
static const String name = 'CollectionRoute';
|
||||
|
||||
static _i12.PageInfo page = _i12.PageInfo(
|
||||
static _i13.PageInfo page = _i13.PageInfo(
|
||||
name,
|
||||
builder: (data) {
|
||||
return const _i3.CollectionScreen();
|
||||
@@ -75,33 +102,77 @@ class CollectionRoute extends _i12.PageRouteInfo<void> {
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [_i4.CreateScreen]
|
||||
class CreateRoute extends _i12.PageRouteInfo<void> {
|
||||
const CreateRoute({List<_i12.PageRouteInfo>? children})
|
||||
/// [_i4.CollectionSearchScreen]
|
||||
class CollectionSearchRoute
|
||||
extends _i13.PageRouteInfo<CollectionSearchRouteArgs> {
|
||||
CollectionSearchRoute({
|
||||
_i14.Key? key,
|
||||
required void Function(_i15.Collection) onCollectionSelect,
|
||||
List<_i13.PageRouteInfo>? children,
|
||||
}) : super(
|
||||
CollectionSearchRoute.name,
|
||||
args: CollectionSearchRouteArgs(
|
||||
key: key,
|
||||
onCollectionSelect: onCollectionSelect,
|
||||
),
|
||||
initialChildren: children,
|
||||
);
|
||||
|
||||
static const String name = 'CollectionSearchRoute';
|
||||
|
||||
static _i13.PageInfo page = _i13.PageInfo(
|
||||
name,
|
||||
builder: (data) {
|
||||
final args = data.argsAs<CollectionSearchRouteArgs>();
|
||||
return _i4.CollectionSearchScreen(
|
||||
key: args.key,
|
||||
onCollectionSelect: args.onCollectionSelect,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
class CollectionSearchRouteArgs {
|
||||
const CollectionSearchRouteArgs({this.key, required this.onCollectionSelect});
|
||||
|
||||
final _i14.Key? key;
|
||||
|
||||
final void Function(_i15.Collection) onCollectionSelect;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'CollectionSearchRouteArgs{key: $key, onCollectionSelect: $onCollectionSelect}';
|
||||
}
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [_i5.CreateScreen]
|
||||
class CreateRoute extends _i13.PageRouteInfo<void> {
|
||||
const CreateRoute({List<_i13.PageRouteInfo>? children})
|
||||
: super(CreateRoute.name, initialChildren: children);
|
||||
|
||||
static const String name = 'CreateRoute';
|
||||
|
||||
static _i12.PageInfo page = _i12.PageInfo(
|
||||
static _i13.PageInfo page = _i13.PageInfo(
|
||||
name,
|
||||
builder: (data) {
|
||||
return const _i4.CreateScreen();
|
||||
return const _i5.CreateScreen();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [_i5.CrudCollectionFullscreenField]
|
||||
/// [_i6.CrudCollectionFullscreenField]
|
||||
class CrudCollectionFullscreenField
|
||||
extends _i12.PageRouteInfo<CrudCollectionFullscreenFieldArgs> {
|
||||
extends _i13.PageRouteInfo<CrudCollectionFullscreenFieldArgs> {
|
||||
CrudCollectionFullscreenField({
|
||||
_i13.Key? key,
|
||||
_i14.Key? key,
|
||||
String title = '',
|
||||
String? hint,
|
||||
String? content,
|
||||
double height = 92,
|
||||
required void Function(String?) onEditingComplete,
|
||||
List<_i12.PageRouteInfo>? children,
|
||||
List<_i13.PageRouteInfo>? children,
|
||||
}) : super(
|
||||
CrudCollectionFullscreenField.name,
|
||||
args: CrudCollectionFullscreenFieldArgs(
|
||||
@@ -117,11 +188,11 @@ class CrudCollectionFullscreenField
|
||||
|
||||
static const String name = 'CrudCollectionFullscreenField';
|
||||
|
||||
static _i12.PageInfo page = _i12.PageInfo(
|
||||
static _i13.PageInfo page = _i13.PageInfo(
|
||||
name,
|
||||
builder: (data) {
|
||||
final args = data.argsAs<CrudCollectionFullscreenFieldArgs>();
|
||||
return _i5.CrudCollectionFullscreenField(
|
||||
return _i6.CrudCollectionFullscreenField(
|
||||
key: args.key,
|
||||
title: args.title,
|
||||
hint: args.hint,
|
||||
@@ -143,7 +214,7 @@ class CrudCollectionFullscreenFieldArgs {
|
||||
required this.onEditingComplete,
|
||||
});
|
||||
|
||||
final _i13.Key? key;
|
||||
final _i14.Key? key;
|
||||
|
||||
final String title;
|
||||
|
||||
@@ -162,12 +233,12 @@ class CrudCollectionFullscreenFieldArgs {
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [_i6.CrudCollectionScreen]
|
||||
class CrudCollectionRoute extends _i12.PageRouteInfo<CrudCollectionRouteArgs> {
|
||||
/// [_i7.CrudCollectionScreen]
|
||||
class CrudCollectionRoute extends _i13.PageRouteInfo<CrudCollectionRouteArgs> {
|
||||
CrudCollectionRoute({
|
||||
_i13.Key? key,
|
||||
_i14.Collection? editedCollection,
|
||||
List<_i12.PageRouteInfo>? children,
|
||||
_i14.Key? key,
|
||||
_i15.Collection? editedCollection,
|
||||
List<_i13.PageRouteInfo>? children,
|
||||
}) : super(
|
||||
CrudCollectionRoute.name,
|
||||
args: CrudCollectionRouteArgs(
|
||||
@@ -179,13 +250,13 @@ class CrudCollectionRoute extends _i12.PageRouteInfo<CrudCollectionRouteArgs> {
|
||||
|
||||
static const String name = 'CrudCollectionRoute';
|
||||
|
||||
static _i12.PageInfo page = _i12.PageInfo(
|
||||
static _i13.PageInfo page = _i13.PageInfo(
|
||||
name,
|
||||
builder: (data) {
|
||||
final args = data.argsAs<CrudCollectionRouteArgs>(
|
||||
orElse: () => const CrudCollectionRouteArgs(),
|
||||
);
|
||||
return _i6.CrudCollectionScreen(
|
||||
return _i7.CrudCollectionScreen(
|
||||
key: args.key,
|
||||
editedCollection: args.editedCollection,
|
||||
);
|
||||
@@ -196,9 +267,9 @@ class CrudCollectionRoute extends _i12.PageRouteInfo<CrudCollectionRouteArgs> {
|
||||
class CrudCollectionRouteArgs {
|
||||
const CrudCollectionRouteArgs({this.key, this.editedCollection});
|
||||
|
||||
final _i13.Key? key;
|
||||
final _i14.Key? key;
|
||||
|
||||
final _i14.Collection? editedCollection;
|
||||
final _i15.Collection? editedCollection;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
@@ -207,81 +278,81 @@ class CrudCollectionRouteArgs {
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [_i7.HomeScreen]
|
||||
class HomeRoute extends _i12.PageRouteInfo<void> {
|
||||
const HomeRoute({List<_i12.PageRouteInfo>? children})
|
||||
/// [_i8.HomeScreen]
|
||||
class HomeRoute extends _i13.PageRouteInfo<void> {
|
||||
const HomeRoute({List<_i13.PageRouteInfo>? children})
|
||||
: super(HomeRoute.name, initialChildren: children);
|
||||
|
||||
static const String name = 'HomeRoute';
|
||||
|
||||
static _i12.PageInfo page = _i12.PageInfo(
|
||||
static _i13.PageInfo page = _i13.PageInfo(
|
||||
name,
|
||||
builder: (data) {
|
||||
return const _i7.HomeScreen();
|
||||
return const _i8.HomeScreen();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [_i8.SandboxScreen]
|
||||
class SandboxRoute extends _i12.PageRouteInfo<void> {
|
||||
const SandboxRoute({List<_i12.PageRouteInfo>? children})
|
||||
/// [_i9.SandboxScreen]
|
||||
class SandboxRoute extends _i13.PageRouteInfo<void> {
|
||||
const SandboxRoute({List<_i13.PageRouteInfo>? children})
|
||||
: super(SandboxRoute.name, initialChildren: children);
|
||||
|
||||
static const String name = 'SandboxRoute';
|
||||
|
||||
static _i12.PageInfo page = _i12.PageInfo(
|
||||
static _i13.PageInfo page = _i13.PageInfo(
|
||||
name,
|
||||
builder: (data) {
|
||||
return const _i8.SandboxScreen();
|
||||
return const _i9.SandboxScreen();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [_i9.SettingsScreen]
|
||||
class SettingsRoute extends _i12.PageRouteInfo<void> {
|
||||
const SettingsRoute({List<_i12.PageRouteInfo>? children})
|
||||
/// [_i10.SettingsScreen]
|
||||
class SettingsRoute extends _i13.PageRouteInfo<void> {
|
||||
const SettingsRoute({List<_i13.PageRouteInfo>? children})
|
||||
: super(SettingsRoute.name, initialChildren: children);
|
||||
|
||||
static const String name = 'SettingsRoute';
|
||||
|
||||
static _i12.PageInfo page = _i12.PageInfo(
|
||||
static _i13.PageInfo page = _i13.PageInfo(
|
||||
name,
|
||||
builder: (data) {
|
||||
return const _i9.SettingsScreen();
|
||||
return const _i10.SettingsScreen();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [_i10.SplashScreen]
|
||||
class SplashRoute extends _i12.PageRouteInfo<void> {
|
||||
const SplashRoute({List<_i12.PageRouteInfo>? children})
|
||||
/// [_i11.SplashScreen]
|
||||
class SplashRoute extends _i13.PageRouteInfo<void> {
|
||||
const SplashRoute({List<_i13.PageRouteInfo>? children})
|
||||
: super(SplashRoute.name, initialChildren: children);
|
||||
|
||||
static const String name = 'SplashRoute';
|
||||
|
||||
static _i12.PageInfo page = _i12.PageInfo(
|
||||
static _i13.PageInfo page = _i13.PageInfo(
|
||||
name,
|
||||
builder: (data) {
|
||||
return const _i10.SplashScreen();
|
||||
return const _i11.SplashScreen();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [_i11.StatistickScreen]
|
||||
class StatistickRoute extends _i12.PageRouteInfo<void> {
|
||||
const StatistickRoute({List<_i12.PageRouteInfo>? children})
|
||||
/// [_i12.StatistickScreen]
|
||||
class StatistickRoute extends _i13.PageRouteInfo<void> {
|
||||
const StatistickRoute({List<_i13.PageRouteInfo>? children})
|
||||
: super(StatistickRoute.name, initialChildren: children);
|
||||
|
||||
static const String name = 'StatistickRoute';
|
||||
|
||||
static _i12.PageInfo page = _i12.PageInfo(
|
||||
static _i13.PageInfo page = _i13.PageInfo(
|
||||
name,
|
||||
builder: (data) {
|
||||
return const _i11.StatistickScreen();
|
||||
return const _i12.StatistickScreen();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -31,12 +31,13 @@ class AuthScreen extends StatelessWidget {
|
||||
return PopScope(
|
||||
canPop: false,
|
||||
child: BlocBuilder<AuthCubit, AuthState>(
|
||||
builder:
|
||||
(BuildContext context, AuthState state) => state.when(
|
||||
builder: (BuildContext context, AuthState state) {
|
||||
return state.when(
|
||||
initial: () => InitialAuth(),
|
||||
email: () => EmailAuth(),
|
||||
code: (email, uuid) => CodeAuth(email: email, uuid: uuid),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@ class EmailAuthState extends State<EmailAuth> {
|
||||
padding: _padding,
|
||||
child: SingleChildScrollView(
|
||||
physics: BouncingScrollPhysics(),
|
||||
child: Expanded(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
@@ -67,7 +66,6 @@ class EmailAuthState extends State<EmailAuth> {
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,6 @@ class InitialAuth extends StatelessWidget {
|
||||
padding: _padding,
|
||||
child: SingleChildScrollView(
|
||||
physics: BouncingScrollPhysics(),
|
||||
child: Expanded(
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
HSpace(148),
|
||||
@@ -50,7 +49,6 @@ class InitialAuth extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
@@ -6,16 +7,32 @@ import 'package:flutter/material.dart';
|
||||
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
|
||||
import 'package:remever/common/resources.dart';
|
||||
import 'package:remever/common/widgets/typography.dart';
|
||||
import 'package:remever/common/widgets/w_if.dart';
|
||||
import 'package:remever/common/widgets/wspace.dart';
|
||||
import 'package:remever/components/extensions/context.dart';
|
||||
import 'package:remever/database/database.dart';
|
||||
import 'package:remever/gen/assets.gen.dart';
|
||||
import 'package:remever/screens/collections/widgets/learning_card.dart';
|
||||
import 'package:remever/inject.dart';
|
||||
import 'package:remever/screens/collections/collections_screen.dart';
|
||||
import 'package:remever/screens/collections/widgets/ticket_card.dart';
|
||||
import 'package:remever/screens/dialogs/info_dialog.dart';
|
||||
import 'package:remever/services/tickets/tickets_interface.dart';
|
||||
import 'package:remever/widgets/primary_button.dart';
|
||||
|
||||
@RoutePage()
|
||||
class CollectionDetailScreen extends StatelessWidget {
|
||||
const CollectionDetailScreen({super.key});
|
||||
const CollectionDetailScreen({super.key, required this.collection});
|
||||
|
||||
final Collection collection;
|
||||
|
||||
void _onInfoTap(BuildContext context) {
|
||||
showCupertinoModalBottomSheet(
|
||||
topRadius: const Radius.circular(24).r,
|
||||
backgroundColor: AppColors.white,
|
||||
context: context,
|
||||
builder: (BuildContext _) => InfoDialog(collection: collection),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -34,7 +51,6 @@ class CollectionDetailScreen extends StatelessWidget {
|
||||
shadowColor: Colors.transparent,
|
||||
leading: IconButton(
|
||||
onPressed: () async {
|
||||
// context.read<HomeCubit>().toCollection();
|
||||
context.back();
|
||||
},
|
||||
icon: const Icon(CupertinoIcons.left_chevron, color: Colors.black),
|
||||
@@ -43,14 +59,7 @@ class CollectionDetailScreen extends StatelessWidget {
|
||||
title: _buildTitle(),
|
||||
actions: <Widget>[
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
showCupertinoModalBottomSheet(
|
||||
topRadius: const Radius.circular(24).r,
|
||||
backgroundColor: AppColors.white,
|
||||
context: context,
|
||||
builder: (BuildContext _) => const InfoDialog(),
|
||||
);
|
||||
},
|
||||
onTap: () => _onInfoTap(context),
|
||||
child: Assets.icons.typeDescription.image(height: 24.h, width: 24.w),
|
||||
),
|
||||
const WSpace(16),
|
||||
@@ -95,10 +104,11 @@ class CollectionDetailScreen extends StatelessWidget {
|
||||
color: AppColors.disabled,
|
||||
),
|
||||
const WSpace(2),
|
||||
AppTypography(
|
||||
0.toString(),
|
||||
type: Regular14px(),
|
||||
color: AppColors.disabled,
|
||||
StreamBuilder<List<Ticket>>(
|
||||
stream: getIt<TicketsInterface>().watchTicketsList(collection.id),
|
||||
builder: (context, snapshot) {
|
||||
return _buildCount(snapshot.data?.length ?? 0);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -107,12 +117,20 @@ class CollectionDetailScreen extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCount(int count) {
|
||||
return AppTypography(
|
||||
'$count',
|
||||
type: Regular14px(),
|
||||
color: AppColors.disabled,
|
||||
);
|
||||
}
|
||||
|
||||
///
|
||||
/// Название коллекции
|
||||
///
|
||||
Widget _buildCollectionTitle() {
|
||||
return AppTypography(
|
||||
'Астрономия и тайная комната Харли Хоттера',
|
||||
collection.title,
|
||||
type: Medium16px(),
|
||||
maxLines: 1,
|
||||
softWrap: true,
|
||||
@@ -127,9 +145,21 @@ class CollectionDetailScreen extends StatelessWidget {
|
||||
return SizedBox.square(
|
||||
dimension: 40.r,
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
image: DecorationImage(image: Assets.images.img.provider()),
|
||||
decoration: BoxDecoration(shape: BoxShape.circle, color: AppColors.bg),
|
||||
|
||||
child: Wif(
|
||||
condition: collection.image != null,
|
||||
builder:
|
||||
(context) => ClipOval(
|
||||
child: Image.memory(collection.image!, fit: BoxFit.cover),
|
||||
),
|
||||
fallback:
|
||||
(context) => Center(
|
||||
child: AppTypography(
|
||||
collection.title.substring(0, 1),
|
||||
type: Bold34px(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -142,7 +172,34 @@ class CollectionDetailScreen extends StatelessWidget {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16).r,
|
||||
|
||||
child: _buildList(context),
|
||||
child: StreamBuilder<List<Ticket>>(
|
||||
stream: getIt<TicketsInterface>().watchTicketsList(collection.id),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return const LoadingList();
|
||||
}
|
||||
|
||||
if (snapshot.hasError) {
|
||||
return const ErrorList();
|
||||
}
|
||||
|
||||
final tickets = snapshot.data;
|
||||
if (tickets == null || tickets.isEmpty) {
|
||||
return _buildEmptyList(context);
|
||||
}
|
||||
|
||||
return ListView.builder(
|
||||
itemCount: tickets.length,
|
||||
physics: BouncingScrollPhysics(),
|
||||
itemBuilder:
|
||||
(context, index) => TicketCard(
|
||||
currentCollection: collection,
|
||||
ticket: tickets[index],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
// child: _buildList(context),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -171,16 +228,4 @@ class CollectionDetailScreen extends StatelessWidget {
|
||||
child: AppTypography('Создать карточку', type: Regular14px()),
|
||||
);
|
||||
}
|
||||
|
||||
///
|
||||
/// Построение списка карточек
|
||||
///
|
||||
Widget _buildList(BuildContext context) {
|
||||
return ListView.builder(
|
||||
physics: BouncingScrollPhysics(),
|
||||
itemCount: 10,
|
||||
itemBuilder:
|
||||
(BuildContext context, int index) => LearningCard(index: index),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
274
lib/screens/collections/collection_search_screen.dart
Normal file
@@ -0,0 +1,274 @@
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:remever/common/resources.dart';
|
||||
import 'package:remever/common/utils.dart';
|
||||
import 'package:remever/common/widgets/loose_focus.dart';
|
||||
import 'package:remever/common/widgets/typography.dart';
|
||||
import 'package:remever/common/widgets/w_if.dart';
|
||||
import 'package:remever/common/widgets/wspace.dart';
|
||||
import 'package:remever/components/extensions/context.dart';
|
||||
import 'package:remever/components/extensions/state.dart';
|
||||
import 'package:remever/database/database.dart';
|
||||
import 'package:remever/gen/assets.gen.dart';
|
||||
import 'package:remever/inject.dart';
|
||||
import 'package:remever/router.gr.dart';
|
||||
import 'package:remever/screens/collections/collections_screen.dart';
|
||||
import 'package:remever/screens/collections/widgets/collection_progress_bar.dart';
|
||||
import 'package:remever/services/collection/collections_interface.dart';
|
||||
|
||||
@RoutePage()
|
||||
class CollectionSearchScreen extends StatefulWidget {
|
||||
const CollectionSearchScreen({super.key, required this.onCollectionSelect});
|
||||
|
||||
final void Function(Collection) onCollectionSelect;
|
||||
|
||||
@override
|
||||
State<CollectionSearchScreen> createState() => _CollectionSearchScreenState();
|
||||
}
|
||||
|
||||
class _CollectionSearchScreenState extends State<CollectionSearchScreen> {
|
||||
final TextEditingController _searchController = TextEditingController();
|
||||
|
||||
String? _search;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_searchController.addListener(() {
|
||||
safeSetState(() {
|
||||
_search = _searchController.text;
|
||||
});
|
||||
});
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_searchController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _onCreateTap() {
|
||||
context.pushRoute(CrudCollectionRoute());
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return LooseFocus(
|
||||
child: Scaffold(
|
||||
backgroundColor: AppColors.bg,
|
||||
appBar: _buildAppBar(context),
|
||||
body: _buildBody(context),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
AppBar _buildAppBar(BuildContext context) {
|
||||
return AppBar(
|
||||
toolbarHeight: 66.h,
|
||||
backgroundColor: AppColors.white,
|
||||
shadowColor: Colors.transparent,
|
||||
title: SizedBox(
|
||||
height: 48.h,
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.white,
|
||||
borderRadius: BorderRadius.circular(12).r,
|
||||
border: Border.all(color: AppColors.bg),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12).r,
|
||||
child: Center(
|
||||
child: TextField(
|
||||
autofocus: true,
|
||||
controller: _searchController,
|
||||
textCapitalization: TextCapitalization.sentences,
|
||||
maxLines: 1,
|
||||
cursorColor: AppColors.danger,
|
||||
decoration: const InputDecoration.collapsed(
|
||||
hintText: 'Поиск',
|
||||
hintStyle: TextStyle(color: AppColors.gray),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
centerTitle: true,
|
||||
leading: IconButton(
|
||||
onPressed: () async {
|
||||
context.back();
|
||||
},
|
||||
icon: const Icon(CupertinoIcons.left_chevron, color: Colors.black),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBody(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
_buildCreateButton(),
|
||||
Expanded(
|
||||
child: StreamBuilder<List<Collection>>(
|
||||
stream: getIt<CollectionsInterface>().watchCollectionsList(
|
||||
search: _search,
|
||||
),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return const LoadingList();
|
||||
}
|
||||
|
||||
if (snapshot.hasError) {
|
||||
return const ErrorList();
|
||||
}
|
||||
|
||||
final collections = snapshot.data;
|
||||
if (collections == null || collections.isEmpty) {
|
||||
return const EmptyList();
|
||||
}
|
||||
|
||||
return ListView.builder(
|
||||
itemCount: collections.length,
|
||||
physics: BouncingScrollPhysics(),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16).r,
|
||||
itemBuilder:
|
||||
(context, index) => Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8).r,
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
widget.onCollectionSelect(collections[index]);
|
||||
|
||||
context.back();
|
||||
},
|
||||
child: _Collection(collection: collections[index]),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Padding _buildCreateButton() {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 28).r,
|
||||
child: GestureDetector(
|
||||
onTap: () => _onCreateTap(),
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox.square(
|
||||
dimension: 20.r,
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.white,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Center(
|
||||
child: Icon(Icons.add, color: AppColors.disabled, size: 15.r),
|
||||
),
|
||||
),
|
||||
),
|
||||
WSpace(4),
|
||||
AppTypography('Создать новую коллекцию', type: Regular16px()),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _Collection extends StatelessWidget {
|
||||
const _Collection({required this.collection});
|
||||
|
||||
final Collection collection;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
constraints: BoxConstraints(minHeight: 66.h, maxHeight: 84.h),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12).r,
|
||||
color: AppColors.white,
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8).r,
|
||||
child: Row(
|
||||
children: <Widget>[_buildAvatar(), const WSpace(5), _buildInfo()],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
///
|
||||
/// Построение основной информации
|
||||
///
|
||||
Widget _buildInfo() {
|
||||
return SizedBox(
|
||||
width: 230.w,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
_buildTitle(),
|
||||
const HSpace(4),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Assets.icons.typeCards.image(
|
||||
height: 18.h,
|
||||
width: 18.w,
|
||||
color: AppColors.disabled,
|
||||
),
|
||||
const WSpace(2),
|
||||
AppTypography(
|
||||
'${collection.likesCount.toString()} ${Utils.declOfNum(collection.likesCount, ['карточек', 'карточки', 'карточек'])}',
|
||||
type: Regular14px(),
|
||||
color: AppColors.disabled,
|
||||
),
|
||||
],
|
||||
),
|
||||
const HSpace(6),
|
||||
const CollectionProgressBar(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
///
|
||||
/// Название коллекции
|
||||
///
|
||||
Widget _buildTitle() {
|
||||
return AppTypography(
|
||||
collection.title,
|
||||
type: Medium16px(),
|
||||
maxLines: 2,
|
||||
softWrap: true,
|
||||
);
|
||||
}
|
||||
|
||||
///
|
||||
/// Обложка коллекции
|
||||
///
|
||||
Widget _buildAvatar() {
|
||||
return SizedBox.square(
|
||||
dimension: 50.r,
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(shape: BoxShape.circle, color: AppColors.bg),
|
||||
|
||||
child: Wif(
|
||||
condition: collection.image != null,
|
||||
builder:
|
||||
(context) => ClipOval(
|
||||
child: Image.memory(collection.image!, fit: BoxFit.cover),
|
||||
),
|
||||
fallback:
|
||||
(context) => Center(
|
||||
child: AppTypography(
|
||||
collection.title.substring(0, 1),
|
||||
type: Bold34px(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:remever/common/functions.dart';
|
||||
import 'package:remever/common/resources.dart';
|
||||
import 'package:remever/common/widgets/typography.dart';
|
||||
import 'package:remever/components/extensions/context.dart';
|
||||
@@ -54,6 +55,8 @@ class _CollectionScreenState extends State<CollectionScreen> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
logger.logBuild('build collection screen');
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.bg,
|
||||
appBar: const CollectionsAppBar(),
|
||||
@@ -80,19 +83,20 @@ class _CollectionScreenState extends State<CollectionScreen> {
|
||||
stream: getIt<CollectionsInterface>().watchCollectionsList(),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return const _LoadingList();
|
||||
return const LoadingList();
|
||||
}
|
||||
|
||||
if (snapshot.hasError) {
|
||||
return const _ErrorList();
|
||||
return const ErrorList();
|
||||
}
|
||||
|
||||
final collections = snapshot.data;
|
||||
if (collections == null || collections.isEmpty) {
|
||||
return const _EmptyList();
|
||||
return const EmptyList();
|
||||
}
|
||||
|
||||
return ListView.builder(
|
||||
physics: BouncingScrollPhysics(),
|
||||
controller: _scrollController,
|
||||
itemCount: collections.length,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16).r,
|
||||
@@ -110,8 +114,8 @@ class _CollectionScreenState extends State<CollectionScreen> {
|
||||
}
|
||||
}
|
||||
|
||||
class _LoadingList extends StatelessWidget {
|
||||
const _LoadingList();
|
||||
class LoadingList extends StatelessWidget {
|
||||
const LoadingList({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -122,8 +126,8 @@ class _LoadingList extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _ErrorList extends StatelessWidget {
|
||||
const _ErrorList();
|
||||
class ErrorList extends StatelessWidget {
|
||||
const ErrorList({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -136,8 +140,8 @@ class _ErrorList extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _EmptyList extends StatelessWidget {
|
||||
const _EmptyList();
|
||||
class EmptyList extends StatelessWidget {
|
||||
const EmptyList({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
@@ -20,6 +20,10 @@ class CollectionCard extends StatelessWidget {
|
||||
|
||||
final Collection collection;
|
||||
|
||||
void _onCollectionTap(BuildContext context) {
|
||||
context.pushRoute(CollectionDetailRoute(collection: collection));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Slidable(
|
||||
@@ -33,7 +37,9 @@ class CollectionCard extends StatelessWidget {
|
||||
backgroundColor: const Color(0xFFD7E6F4),
|
||||
foregroundColor: const Color(0xFF0058AB),
|
||||
icon: Icons.info_outline,
|
||||
onPressed: () {},
|
||||
onPressed: () {
|
||||
showInfoToast('Пока недоступно');
|
||||
},
|
||||
),
|
||||
const WSpace(8),
|
||||
_buildSlidableAction(
|
||||
@@ -44,7 +50,9 @@ class CollectionCard extends StatelessWidget {
|
||||
collection.isLiked
|
||||
? Icons.favorite_outlined
|
||||
: Icons.favorite_border,
|
||||
onPressed: () {},
|
||||
onPressed: () {
|
||||
showInfoToast('Пока недоступно');
|
||||
},
|
||||
),
|
||||
const WSpace(8),
|
||||
_buildSlidableAction(
|
||||
@@ -52,12 +60,14 @@ class CollectionCard extends StatelessWidget {
|
||||
backgroundColor: AppColors.secondary,
|
||||
foregroundColor: AppColors.primary,
|
||||
icon: Icons.visibility_off_outlined,
|
||||
onPressed: () {},
|
||||
onPressed: () {
|
||||
showInfoToast('Пока недоступно');
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
child: GestureDetector(
|
||||
onTap: () => context.pushRoute(CollectionDetailRoute()),
|
||||
onTap: () => _onCollectionTap(context),
|
||||
child: Container(
|
||||
constraints: BoxConstraints(minHeight: 66.h, maxHeight: 84.h),
|
||||
decoration: BoxDecoration(
|
||||
@@ -162,10 +172,7 @@ class CollectionCard extends StatelessWidget {
|
||||
condition: collection.image != null,
|
||||
builder:
|
||||
(context) => ClipOval(
|
||||
child: Image.memory(
|
||||
base64Decode(collection.image!),
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
child: Image.memory(collection.image!, fit: BoxFit.cover),
|
||||
),
|
||||
fallback:
|
||||
(context) => Center(
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
|
||||
import 'package:remever/common/functions.dart';
|
||||
import 'package:remever/common/resources.dart';
|
||||
import 'package:remever/common/widgets/typography.dart';
|
||||
import 'package:remever/common/widgets/wspace.dart';
|
||||
import 'package:remever/components/extensions/context.dart';
|
||||
import 'package:remever/gen/assets.gen.dart';
|
||||
import 'package:remever/router.gr.dart';
|
||||
import 'package:remever/screens/dialogs/filters_dialog.dart';
|
||||
|
||||
class CollectionsAppBar extends StatelessWidget implements PreferredSizeWidget {
|
||||
const CollectionsAppBar({super.key});
|
||||
|
||||
@override
|
||||
Size get preferredSize => Size.fromHeight(66.h);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AppBar(
|
||||
toolbarHeight: 66.h,
|
||||
backgroundColor: AppColors.white,
|
||||
shadowColor: Colors.transparent,
|
||||
title: Row(
|
||||
children: <Widget>[
|
||||
GestureDetector(
|
||||
onLongPress: () => context.pushRoute(const SandboxRoute()),
|
||||
child: AppTypography(
|
||||
'Коллекции',
|
||||
type: SemiBold28px(),
|
||||
color: AppColors.body_text,
|
||||
),
|
||||
),
|
||||
const WSpace(2),
|
||||
Container(
|
||||
height: 22.h,
|
||||
width: 38.w,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.secondary,
|
||||
borderRadius: BorderRadius.circular(40).r,
|
||||
),
|
||||
child: Center(
|
||||
child: AppTypography(
|
||||
'2213',
|
||||
type: Regular12px(),
|
||||
color: AppColors.body_text,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: <Widget>[
|
||||
AppBarIconButton(icon: Assets.icons.typeSearch, onTap: () {}),
|
||||
AppBarIconButton(icon: Assets.icons.typeDownload, onTap: () {}),
|
||||
AppBarIconButton(
|
||||
icon: Assets.icons.typeSort,
|
||||
onTap: () {
|
||||
showCuperModalBottomSheet(
|
||||
context: context,
|
||||
height: 424.h,
|
||||
builder: (BuildContext context) => const FiltersDialog(),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AppBarIconButton extends StatelessWidget {
|
||||
const AppBarIconButton({required this.icon, required this.onTap, super.key});
|
||||
|
||||
final AssetGenImage icon;
|
||||
final void Function()? onTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: SizedBox(
|
||||
width: 48.h,
|
||||
child: Center(child: icon.image(height: 24.h, width: 24.w)),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -5,9 +5,13 @@ import 'package:remever/common/resources.dart';
|
||||
import 'package:remever/common/widgets/typography.dart';
|
||||
import 'package:remever/common/widgets/wspace.dart';
|
||||
import 'package:remever/components/extensions/context.dart';
|
||||
import 'package:remever/database/database.dart';
|
||||
import 'package:remever/gen/assets.gen.dart';
|
||||
import 'package:remever/inject.dart';
|
||||
import 'package:remever/router.gr.dart';
|
||||
import 'package:remever/screens/dialogs/filters_dialog.dart';
|
||||
import 'package:remever/services/collection/collections_interface.dart';
|
||||
import 'package:remever/widgets/debug/app_debug.dart';
|
||||
|
||||
class CollectionsAppBar extends StatelessWidget implements PreferredSizeWidget {
|
||||
const CollectionsAppBar({super.key});
|
||||
@@ -42,16 +46,22 @@ class CollectionsAppBar extends StatelessWidget implements PreferredSizeWidget {
|
||||
borderRadius: BorderRadius.circular(40).r,
|
||||
),
|
||||
child: Center(
|
||||
child: AppTypography(
|
||||
'2213',
|
||||
type: Regular12px(),
|
||||
color: AppColors.body_text,
|
||||
child: StreamBuilder<List<Collection>>(
|
||||
stream: getIt<CollectionsInterface>().watchCollectionsList(),
|
||||
builder: (context, snapshot) {
|
||||
return _buildCount(snapshot.data?.length ?? 0);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: <Widget>[
|
||||
AppDebug(
|
||||
builder: (context, isDebug) {
|
||||
if (!isDebug) return SizedBox();
|
||||
return Row(
|
||||
children: [
|
||||
AppBarIconButton(icon: Assets.icons.typeSearch, onTap: () {}),
|
||||
AppBarIconButton(icon: Assets.icons.typeDownload, onTap: () {}),
|
||||
AppBarIconButton(
|
||||
@@ -66,6 +76,18 @@ class CollectionsAppBar extends StatelessWidget implements PreferredSizeWidget {
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCount(int count) {
|
||||
return AppTypography(
|
||||
'$count',
|
||||
type: Regular12px(),
|
||||
color: AppColors.body_text,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:remever/common/resources.dart';
|
||||
import 'package:remever/common/widgets/typography.dart';
|
||||
import 'package:remever/common/widgets/wspace.dart';
|
||||
import 'package:remever/components/extensions/context.dart';
|
||||
import 'package:remever/components/extensions/state.dart';
|
||||
import 'package:remever/widgets/debug/app_debug.dart';
|
||||
|
||||
class CollectionsFilters extends StatefulWidget {
|
||||
const CollectionsFilters({super.key});
|
||||
@@ -15,6 +18,10 @@ class CollectionsFilters extends StatefulWidget {
|
||||
class _CollectionsFiltersState extends State<CollectionsFilters> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AppDebug(
|
||||
builder: (context, isDebug) {
|
||||
if (!isDebug) return HSpace(20);
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 28).r,
|
||||
child: Row(
|
||||
@@ -32,6 +39,8 @@ class _CollectionsFiltersState extends State<CollectionsFilters> {
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
///
|
||||
|
||||
@@ -1,249 +0,0 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||
import 'package:readmore/readmore.dart';
|
||||
import 'package:remever/common/functions.dart';
|
||||
import 'package:remever/common/resources.dart';
|
||||
import 'package:remever/common/typography.dart';
|
||||
import 'package:remever/common/widgets/w_if.dart';
|
||||
import 'package:remever/common/widgets/wspace.dart';
|
||||
import 'package:remever/components/extensions/context.dart';
|
||||
import 'package:remever/gen/assets.gen.dart';
|
||||
import 'package:remever/screens/dialogs/alert_dialog.dart';
|
||||
import 'package:remever/screens/dialogs/replace_diaog.dart';
|
||||
|
||||
enum CardType { CREATE, SHOW }
|
||||
|
||||
class LearningCard extends StatelessWidget {
|
||||
LearningCard({
|
||||
required this.index,
|
||||
super.key,
|
||||
this.type = CardType.SHOW,
|
||||
this.onTextTap,
|
||||
});
|
||||
|
||||
final int index;
|
||||
final CardType type;
|
||||
void Function()? onTextTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 8).r,
|
||||
child: Slidable(
|
||||
enabled: type == CardType.SHOW ? true : false,
|
||||
endActionPane: ActionPane(
|
||||
extentRatio: 0.62,
|
||||
motion: const StretchMotion(),
|
||||
children: <Widget>[
|
||||
const WSpace(8),
|
||||
_buildSlidableAction(
|
||||
context: context,
|
||||
backgroundColor: const Color(0xFFFFE4E6),
|
||||
foregroundColor: const Color(0xFFFF5C69),
|
||||
icon: CupertinoIcons.trash,
|
||||
onPressed: () {
|
||||
showCuperModalBottomSheet(
|
||||
context: context,
|
||||
height: 262.h,
|
||||
builder:
|
||||
(BuildContext context) => const AlertInfoDialog(
|
||||
title:
|
||||
'Вы хотите удалить карточку?\nЭто действие необратимо',
|
||||
acceptTitle: 'Да, удалить',
|
||||
declineTitle: 'Нет, оставить',
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
const WSpace(8),
|
||||
_buildSlidableAction(
|
||||
context: context,
|
||||
backgroundColor: const Color(0xFFD7E6F4),
|
||||
foregroundColor: const Color(0xFF0058AB),
|
||||
icon: CupertinoIcons.repeat,
|
||||
onPressed: () {},
|
||||
),
|
||||
const WSpace(8),
|
||||
_buildSlidableAction(
|
||||
context: context,
|
||||
backgroundColor: AppColors.secondary,
|
||||
foregroundColor: AppColors.primary,
|
||||
icon: CupertinoIcons.move,
|
||||
onPressed: () {
|
||||
showCuperModalBottomSheet(
|
||||
context: context,
|
||||
height: 394.h,
|
||||
backgroundColor: AppColors.gray_bg,
|
||||
builder: (BuildContext context) => const ReplaceDialog(),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)).r,
|
||||
color: Colors.white,
|
||||
),
|
||||
constraints: BoxConstraints(minHeight: 50.h),
|
||||
child: Stack(
|
||||
children: <Widget>[
|
||||
SizedBox(
|
||||
height: 50.h,
|
||||
width: double.infinity,
|
||||
child: DecoratedBox(decoration: getDecoration()),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(12).r,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
_buildImage(),
|
||||
_buildText(context),
|
||||
Wif(
|
||||
condition: type == CardType.SHOW,
|
||||
builder: (BuildContext context) => _editBtm(),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Кнопка редактирования
|
||||
Widget _editBtm() {
|
||||
return GestureDetector(
|
||||
onTap: () {},
|
||||
child: Assets.icons.typeEdit.image(height: 24.h, width: 24.w),
|
||||
);
|
||||
}
|
||||
|
||||
///
|
||||
/// Кнопка в меню свайпа
|
||||
///
|
||||
Widget _buildSlidableAction({
|
||||
required BuildContext context,
|
||||
required Color backgroundColor,
|
||||
required Color foregroundColor,
|
||||
required IconData icon,
|
||||
required VoidCallback onPressed,
|
||||
}) {
|
||||
return SlidableAction(
|
||||
borderRadius: BorderRadius.circular(12).r,
|
||||
onPressed: (_) => onPressed(),
|
||||
backgroundColor: backgroundColor,
|
||||
foregroundColor: foregroundColor,
|
||||
icon: icon,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildText(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: onTextTap,
|
||||
child: SizedBox(
|
||||
width:
|
||||
type == CardType.CREATE
|
||||
? 250.w
|
||||
: index % 4 == 0
|
||||
? 228.w
|
||||
: 300.w,
|
||||
child: ReadMoreText(
|
||||
isExpandable: true,
|
||||
index % 2 == 0
|
||||
? 'Наше дело не так однозначно, как может показаться: высокотехнологичная концепция общественного уклада напрямую зависит от вывода текущих активов. Ясность нашей позиции очевидна: высокотехнологичная концепция общественного уклада влечет за собой процесс внедрения и модернизации первоочередных требований. Лишь многие известные личности будут указаны как претенденты на роль ключевых факторов.'
|
||||
: 'Аргумент или не аргумент, вот вообще хз',
|
||||
trimMode: TrimMode.Line,
|
||||
trimLines: 3,
|
||||
trimCollapsedText: '\nРазвернуть',
|
||||
trimExpandedText: '\nСвернуть',
|
||||
style: Regular16px().style,
|
||||
moreStyle: Regular12px().style.copyWith(
|
||||
color: AppColors.primary_blue,
|
||||
),
|
||||
lessStyle: Regular12px().style.copyWith(
|
||||
color: AppColors.primary_blue,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Картинка
|
||||
Widget _buildImage() {
|
||||
return Wif(
|
||||
condition: type == CardType.SHOW,
|
||||
builder: (BuildContext context) {
|
||||
return Wif(
|
||||
condition: index % 4 == 0,
|
||||
builder: (BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(right: 8).r,
|
||||
child: SizedBox.square(
|
||||
dimension: 64.r,
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(8)).r,
|
||||
image: DecorationImage(
|
||||
image: Assets.images.imgCard.provider(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
fallback: (BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(right: 8).r,
|
||||
child: SizedBox.square(
|
||||
dimension: 64.r,
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(8)).r,
|
||||
gradient: const LinearGradient(
|
||||
colors: <Color>[Color(0xFFDBD7F4), Color(0xFFB6AAFE)],
|
||||
begin: Alignment.topRight,
|
||||
end: Alignment.bottomLeft,
|
||||
),
|
||||
),
|
||||
child: Center(
|
||||
child: Assets.icons.typePhoto.image(
|
||||
height: 24.h,
|
||||
width: 24.w,
|
||||
color: AppColors.primary,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Декорирование контейнера
|
||||
BoxDecoration getDecoration() {
|
||||
return BoxDecoration(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)).r,
|
||||
gradient: LinearGradient(
|
||||
colors: <Color>[
|
||||
index % 3 == 0 ? AppColors.question : AppColors.answer,
|
||||
Colors.white,
|
||||
],
|
||||
begin: Alignment.topLeft,
|
||||
end: const Alignment(-0.6, 1),
|
||||
stops: const <double>[0.25, 0.25],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
204
lib/screens/collections/widgets/ticket_card.dart
Normal file
@@ -0,0 +1,204 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||
import 'package:readmore/readmore.dart';
|
||||
import 'package:remever/common/functions.dart';
|
||||
import 'package:remever/common/resources.dart';
|
||||
import 'package:remever/common/typography.dart';
|
||||
import 'package:remever/common/widgets/w_if.dart';
|
||||
import 'package:remever/common/widgets/wspace.dart';
|
||||
import 'package:remever/components/extensions/context.dart';
|
||||
import 'package:remever/components/extensions/state.dart';
|
||||
import 'package:remever/database/database.dart';
|
||||
import 'package:remever/gen/assets.gen.dart';
|
||||
import 'package:remever/inject.dart';
|
||||
import 'package:remever/screens/dialogs/alert_dialog.dart';
|
||||
import 'package:remever/screens/dialogs/replace_diaog.dart';
|
||||
import 'package:remever/services/tickets/tickets_interface.dart';
|
||||
|
||||
class TicketCard extends StatefulWidget {
|
||||
const TicketCard({
|
||||
required this.ticket,
|
||||
required this.currentCollection,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final Collection currentCollection;
|
||||
final Ticket ticket;
|
||||
|
||||
@override
|
||||
State<TicketCard> createState() => _TicketCardState();
|
||||
}
|
||||
|
||||
class _TicketCardState extends State<TicketCard> {
|
||||
bool _isRolled = false;
|
||||
|
||||
void _onDeleteTap() async {
|
||||
final bool? res = await showCuperModalBottomSheet(
|
||||
context: context,
|
||||
height: 262.h,
|
||||
builder:
|
||||
(_) => const AlertInfoDialog(
|
||||
title: 'Вы хотите удалить карточку?\nЭто действие необратимо',
|
||||
acceptTitle: 'Да, удалить',
|
||||
declineTitle: 'Нет, оставить',
|
||||
),
|
||||
);
|
||||
|
||||
if (res != null && res) {
|
||||
await getIt<TicketsInterface>().removeTicket(widget.ticket.id);
|
||||
}
|
||||
}
|
||||
|
||||
void _onReplaceTap() {
|
||||
showCuperModalBottomSheet(
|
||||
context: context,
|
||||
height: 420.h,
|
||||
backgroundColor: AppColors.gray_bg,
|
||||
builder:
|
||||
(_) => ReplaceDialog(
|
||||
currentCollection: widget.currentCollection,
|
||||
ticket: widget.ticket,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _onRollTap() {
|
||||
safeSetState(() => _isRolled = !_isRolled);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 8).r,
|
||||
child: Slidable(
|
||||
enabled: true,
|
||||
endActionPane: ActionPane(
|
||||
extentRatio: 0.62,
|
||||
motion: const StretchMotion(),
|
||||
children: [
|
||||
const WSpace(8),
|
||||
_buildSlidableAction(
|
||||
Colors.red.shade50,
|
||||
Colors.red,
|
||||
CupertinoIcons.trash,
|
||||
_onDeleteTap,
|
||||
),
|
||||
const WSpace(8),
|
||||
_buildSlidableAction(
|
||||
Colors.blue.shade50,
|
||||
Colors.blue,
|
||||
CupertinoIcons.repeat,
|
||||
_onRollTap,
|
||||
),
|
||||
const WSpace(8),
|
||||
_buildSlidableAction(
|
||||
AppColors.secondary,
|
||||
AppColors.primary,
|
||||
CupertinoIcons.move,
|
||||
_onReplaceTap,
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12).r,
|
||||
color: Colors.white,
|
||||
),
|
||||
constraints: BoxConstraints(minHeight: 50.h),
|
||||
child: Stack(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 50.h,
|
||||
width: double.infinity,
|
||||
child: DecoratedBox(decoration: getDecoration()),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(12).r,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildImage(),
|
||||
Expanded(child: _buildText(context)),
|
||||
_buildEditButton(),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildEditButton() {
|
||||
return GestureDetector(
|
||||
onTap: () {},
|
||||
child: Assets.icons.typeEdit.image(height: 24.h, width: 24.w),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSlidableAction(
|
||||
Color backgroundColor,
|
||||
Color foregroundColor,
|
||||
IconData icon,
|
||||
VoidCallback onPressed,
|
||||
) {
|
||||
return SlidableAction(
|
||||
borderRadius: BorderRadius.circular(12).r,
|
||||
onPressed: (_) => onPressed(),
|
||||
backgroundColor: backgroundColor,
|
||||
foregroundColor: foregroundColor,
|
||||
icon: icon,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildText(BuildContext context) {
|
||||
return ReadMoreText(
|
||||
_isRolled ? widget.ticket.answer : widget.ticket.question,
|
||||
trimMode: TrimMode.Line,
|
||||
trimLines: 3,
|
||||
trimCollapsedText: '\nРазвернуть',
|
||||
trimExpandedText: '\nСвернуть',
|
||||
style: Regular16px().style,
|
||||
moreStyle: Regular12px().style.copyWith(color: AppColors.primary_blue),
|
||||
lessStyle: Regular12px().style.copyWith(color: AppColors.primary_blue),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildImage() {
|
||||
final imageBytes =
|
||||
_isRolled ? widget.ticket.answerImage : widget.ticket.questionImage;
|
||||
return Wif(
|
||||
condition: imageBytes != null,
|
||||
builder:
|
||||
(context) => Padding(
|
||||
padding: const EdgeInsets.only(right: 8).r,
|
||||
child: SizedBox.square(
|
||||
dimension: 64.r,
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(8).r,
|
||||
child: Image.memory(imageBytes!, fit: BoxFit.cover),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Декорирование контейнера
|
||||
BoxDecoration getDecoration() {
|
||||
return BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12).r,
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
_isRolled ? AppColors.answer : AppColors.question,
|
||||
Colors.white,
|
||||
],
|
||||
begin: Alignment.topLeft,
|
||||
end: const Alignment(-0.6, 1),
|
||||
stops: const [0.25, 0.25],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,32 +1,113 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'dart:io';
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:remever/common/functions.dart';
|
||||
import 'package:remever/common/resources.dart';
|
||||
import 'package:remever/common/utils.dart';
|
||||
import 'package:remever/common/widgets/typography.dart';
|
||||
import 'package:remever/common/widgets/w_if.dart';
|
||||
import 'package:remever/common/widgets/wspace.dart';
|
||||
import 'package:remever/components/extensions/context.dart';
|
||||
import 'package:remever/components/extensions/state.dart';
|
||||
import 'package:remever/gen/assets.gen.dart';
|
||||
import 'package:remever/inject.dart';
|
||||
import 'package:remever/models/create_ticket_dto.dart';
|
||||
import 'package:remever/router.gr.dart';
|
||||
import 'package:remever/screens/collections/widgets/collection_progress_bar.dart';
|
||||
import 'package:remever/screens/collections/widgets/learning_card.dart';
|
||||
import 'package:remever/screens/create_card/widgets/crud_ticket.dart';
|
||||
import 'package:remever/services/tickets/tickets_interface.dart';
|
||||
import 'package:remever/widgets/debug/app_debug.dart';
|
||||
import 'package:remever/widgets/primary_button.dart';
|
||||
|
||||
@RoutePage()
|
||||
class CreateScreen extends StatelessWidget {
|
||||
class CreateScreen extends StatefulWidget {
|
||||
const CreateScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.gray_bg,
|
||||
appBar: _buildAppBar(),
|
||||
body: _buildMain(context),
|
||||
State<CreateScreen> createState() => _CreateScreenState();
|
||||
}
|
||||
|
||||
class _CreateScreenState extends State<CreateScreen> {
|
||||
CreateTicketDto _dto = CreateTicketDto();
|
||||
|
||||
// Constants for spacing and dimensions
|
||||
static const double _horizontalPadding = 16;
|
||||
static const double _verticalSpacing = 16;
|
||||
|
||||
Future<void> _pickImage(bool isQuestion) async {
|
||||
final result = await FilePicker.platform.pickFiles();
|
||||
if (result == null || result.files.isEmpty) {
|
||||
showErrorToast('Файл не выбран');
|
||||
return;
|
||||
}
|
||||
final filePath = result.files.single.path;
|
||||
if (filePath == null) {
|
||||
showErrorToast('Не удалось получить путь к файлу');
|
||||
return;
|
||||
}
|
||||
final file = File(filePath);
|
||||
final bytes = await file.readAsBytes();
|
||||
safeSetState(() {
|
||||
_dto =
|
||||
isQuestion
|
||||
? _dto.copyWith(questionImage: bytes)
|
||||
: _dto.copyWith(answerImage: bytes);
|
||||
});
|
||||
}
|
||||
|
||||
void _onCollectionTap() {
|
||||
context.pushRoute(
|
||||
CollectionSearchRoute(
|
||||
onCollectionSelect: (collection) {
|
||||
safeSetState(() => _dto = _dto.copyWith(collection: collection));
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
AppBar _buildAppBar() {
|
||||
Future<void> _onCreateTap() async {
|
||||
if (_dto.collection == null ||
|
||||
_dto.answer == null ||
|
||||
_dto.question == null) {
|
||||
showErrorToast('Необходимо заполнить все поля');
|
||||
return;
|
||||
}
|
||||
await getIt<TicketsInterface>().createTicket(_dto);
|
||||
|
||||
safeSetState(() => _dto = CreateTicketDto());
|
||||
|
||||
final tabsRouter = context.tabsRouter;
|
||||
tabsRouter.setActiveIndex(3);
|
||||
}
|
||||
|
||||
void _openFullScreenField({
|
||||
required String title,
|
||||
required String? content,
|
||||
required void Function(String?) onEditingComplete,
|
||||
}) {
|
||||
context.pushRoute(
|
||||
CrudCollectionFullscreenField(
|
||||
title: title,
|
||||
hint: '',
|
||||
height: 313,
|
||||
content: content,
|
||||
onEditingComplete: onEditingComplete,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _updateDto(String value, bool isQuestion) {
|
||||
safeSetState(() {
|
||||
_dto =
|
||||
isQuestion
|
||||
? _dto.copyWith(question: value)
|
||||
: _dto.copyWith(answer: value);
|
||||
});
|
||||
}
|
||||
|
||||
PreferredSizeWidget _buildAppBar() {
|
||||
return AppBar(
|
||||
toolbarHeight: 66.h,
|
||||
backgroundColor: AppColors.white,
|
||||
@@ -42,70 +123,167 @@ class CreateScreen extends StatelessWidget {
|
||||
|
||||
Widget _buildMain(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16).r,
|
||||
padding: EdgeInsets.symmetric(horizontal: _horizontalPadding).r,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
children: [
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
const HSpace(16),
|
||||
children: [
|
||||
const HSpace(_verticalSpacing),
|
||||
_filters(),
|
||||
const HSpace(16),
|
||||
AppTypography('Коллекция', type: Medium16px()),
|
||||
const HSpace(4),
|
||||
_buildCollection(context),
|
||||
const HSpace(16),
|
||||
AppTypography('Вопрос', type: Medium16px()),
|
||||
const HSpace(4),
|
||||
LearningCard(
|
||||
index: 0,
|
||||
type: CardType.CREATE,
|
||||
onTextTap: () {
|
||||
context.pushRoute(
|
||||
CrudCollectionFullscreenField(
|
||||
const HSpace(_verticalSpacing),
|
||||
_buildSection('Коллекция', _buildCollection(context)),
|
||||
const HSpace(_verticalSpacing),
|
||||
_buildSection(
|
||||
'Вопрос',
|
||||
CrudTicket(
|
||||
onTextTap:
|
||||
() => _openFullScreenField(
|
||||
title: 'Вопрос',
|
||||
hint: '',
|
||||
height: 313,
|
||||
onEditingComplete: (p0) {},
|
||||
content: _dto.question,
|
||||
onEditingComplete: (res) => _updateDto(res!, true),
|
||||
),
|
||||
);
|
||||
},
|
||||
onImageTap: () => _pickImage(true),
|
||||
isQuestion: true,
|
||||
dto: _dto,
|
||||
),
|
||||
const HSpace(16),
|
||||
AppTypography('Ответ', type: Medium16px()),
|
||||
LearningCard(
|
||||
index: 1,
|
||||
type: CardType.CREATE,
|
||||
onTextTap: () {
|
||||
context.pushRoute(
|
||||
CrudCollectionFullscreenField(
|
||||
),
|
||||
const HSpace(_verticalSpacing),
|
||||
_buildSection(
|
||||
'Ответ',
|
||||
CrudTicket(
|
||||
onTextTap:
|
||||
() => _openFullScreenField(
|
||||
title: 'Ответ',
|
||||
hint: '',
|
||||
height: 313,
|
||||
onEditingComplete: (p0) {},
|
||||
content: _dto.answer,
|
||||
onEditingComplete: (res) => _updateDto(res!, false),
|
||||
),
|
||||
onImageTap: () => _pickImage(false),
|
||||
isQuestion: false,
|
||||
dto: _dto,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
revertCard(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
_createBtn(),
|
||||
_buildCreateButton(),
|
||||
const HSpace(31),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSection(String title, Widget child) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
AppTypography(title, type: Medium16px()),
|
||||
const HSpace(4),
|
||||
child,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCollection(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: _onCollectionTap,
|
||||
child: Container(
|
||||
constraints: BoxConstraints(minHeight: 66.h, maxHeight: 84.h),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12).r,
|
||||
color: AppColors.white,
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8).r,
|
||||
child: Wif(
|
||||
condition: _dto.collection != null,
|
||||
builder:
|
||||
(context) => Row(
|
||||
children: [_buildAvatar(), const WSpace(5), _buildInfo()],
|
||||
),
|
||||
fallback:
|
||||
(context) => Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
AppTypography('Выберите коллекцию', type: Bold14px()),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildInfo() {
|
||||
return SizedBox(
|
||||
width: 230.w,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
_buildTitle(),
|
||||
const HSpace(4),
|
||||
Row(
|
||||
children: [
|
||||
Assets.icons.typeCards.image(
|
||||
height: 18.h,
|
||||
width: 18.w,
|
||||
color: AppColors.disabled,
|
||||
),
|
||||
const WSpace(2),
|
||||
AppTypography(
|
||||
'${_dto.collection!.likesCount.toString()} ${Utils.declOfNum(_dto.collection!.likesCount, ['карточек', 'карточки', 'карточек'])}',
|
||||
type: Regular14px(),
|
||||
color: AppColors.disabled,
|
||||
),
|
||||
],
|
||||
),
|
||||
const HSpace(6),
|
||||
const CollectionProgressBar(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTitle() {
|
||||
return AppTypography(
|
||||
_dto.collection!.title,
|
||||
type: Medium16px(),
|
||||
maxLines: 2,
|
||||
softWrap: true,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildAvatar() {
|
||||
return SizedBox.square(
|
||||
dimension: 50.r,
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(shape: BoxShape.circle, color: AppColors.bg),
|
||||
child: Wif(
|
||||
condition: _dto.collection?.image != null,
|
||||
builder:
|
||||
(context) => ClipOval(
|
||||
child: Image.memory(_dto.collection!.image!, fit: BoxFit.cover),
|
||||
),
|
||||
fallback:
|
||||
(context) => Center(
|
||||
child: AppTypography(
|
||||
_dto.collection!.title.substring(0, 1),
|
||||
type: Bold34px(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget revertCard() {
|
||||
return SizedBox(
|
||||
height: 52.h,
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
children: [
|
||||
Center(
|
||||
child: Assets.icons.typeFlip2.image(height: 20.h, width: 20.w),
|
||||
),
|
||||
@@ -124,8 +302,11 @@ class CreateScreen extends StatelessWidget {
|
||||
fit: BoxFit.contain,
|
||||
child: CupertinoSwitch(
|
||||
activeTrackColor: AppColors.primary,
|
||||
value: false,
|
||||
onChanged: (bool value) {},
|
||||
value: _dto.needRevert ?? false,
|
||||
onChanged:
|
||||
(value) => safeSetState(
|
||||
() => _dto = _dto.copyWith(needRevert: value),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -134,39 +315,42 @@ class CreateScreen extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Row _filters() {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
_buildFilterButton(AppColors.gray_bg, 'Запомнить', () {}),
|
||||
_buildFilterButton(AppColors.white, 'Держать в фокусе', () {}),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _createBtn() {
|
||||
Widget _buildCreateButton() {
|
||||
return PrimaryButton(
|
||||
color: AppColors.primary,
|
||||
onTap: _onCreateTap,
|
||||
child: AppTypography(
|
||||
'Создать карточку',
|
||||
type: Medium14px(),
|
||||
color: AppColors.white,
|
||||
),
|
||||
onTap: () {},
|
||||
);
|
||||
}
|
||||
|
||||
///
|
||||
/// Построение кнопки фильтра
|
||||
///
|
||||
Widget _buildFilterButton(Color color, String title, void Function()? onTap) {
|
||||
Widget _filters() {
|
||||
return AppDebug(
|
||||
builder: (context, isDebug) {
|
||||
if (!isDebug) return const SizedBox();
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
_buildFilterButton(AppColors.gray_bg, 'Запомнить', () {}),
|
||||
const WSpace(10),
|
||||
_buildFilterButton(AppColors.white, 'Держать в фокусе', () {}),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildFilterButton(Color color, String title, VoidCallback onTap) {
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
width: 158.h,
|
||||
height: 36.h,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(16)).r,
|
||||
borderRadius: BorderRadius.circular(16).r,
|
||||
color: color,
|
||||
),
|
||||
child: Center(child: AppTypography(title, type: SemiBold14px())),
|
||||
@@ -174,81 +358,12 @@ class CreateScreen extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCollection(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () {},
|
||||
child: Container(
|
||||
constraints: BoxConstraints(minHeight: 66.h, maxHeight: 84.h),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12).r,
|
||||
color: AppColors.white,
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8).r,
|
||||
child: Row(
|
||||
children: <Widget>[_buildAvatar(), const WSpace(5), _buildInfo()],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
///
|
||||
/// Построение основной информации
|
||||
///
|
||||
Widget _buildInfo() {
|
||||
return SizedBox(
|
||||
width: 230.w,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
_buildTitle(),
|
||||
const HSpace(4),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Assets.icons.typeCards.image(
|
||||
height: 18.h,
|
||||
width: 18.w,
|
||||
color: AppColors.disabled,
|
||||
),
|
||||
const WSpace(2),
|
||||
AppTypography(
|
||||
Random().nextInt(654).toString(),
|
||||
type: Regular14px(),
|
||||
color: AppColors.disabled,
|
||||
),
|
||||
],
|
||||
),
|
||||
const HSpace(6),
|
||||
const CollectionProgressBar(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
///
|
||||
/// Название коллекции
|
||||
///
|
||||
Widget _buildTitle() {
|
||||
return AppTypography(
|
||||
'Астрономия',
|
||||
type: Medium16px(),
|
||||
maxLines: 2,
|
||||
softWrap: true,
|
||||
);
|
||||
}
|
||||
|
||||
///
|
||||
/// Обложка коллекции
|
||||
///
|
||||
Widget _buildAvatar() {
|
||||
return SizedBox.square(
|
||||
dimension: 50.r,
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
image: DecorationImage(image: Assets.images.img.provider()),
|
||||
),
|
||||
),
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.gray_bg,
|
||||
appBar: _buildAppBar(),
|
||||
body: _buildMain(context),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
140
lib/screens/create_card/widgets/crud_ticket.dart
Normal file
@@ -0,0 +1,140 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:readmore/readmore.dart';
|
||||
import 'package:remever/common/resources.dart';
|
||||
import 'package:remever/common/typography.dart';
|
||||
import 'package:remever/common/widgets/w_if.dart';
|
||||
import 'package:remever/components/extensions/context.dart';
|
||||
import 'package:remever/gen/assets.gen.dart';
|
||||
import 'package:remever/models/create_ticket_dto.dart';
|
||||
|
||||
class CrudTicket extends StatelessWidget {
|
||||
CrudTicket({
|
||||
super.key,
|
||||
this.onTextTap,
|
||||
this.onImageTap,
|
||||
required this.isQuestion,
|
||||
required this.dto,
|
||||
});
|
||||
|
||||
void Function()? onTextTap;
|
||||
void Function()? onImageTap;
|
||||
final bool isQuestion;
|
||||
final CreateTicketDto dto;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: onTextTap,
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)).r,
|
||||
color: Colors.white,
|
||||
),
|
||||
constraints: BoxConstraints(minHeight: 50.h),
|
||||
child: Stack(
|
||||
children: <Widget>[
|
||||
SizedBox(
|
||||
height: 50.h,
|
||||
width: double.infinity,
|
||||
child: DecoratedBox(decoration: getDecoration()),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(12).r,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[_buildImage(), _buildText(context)],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildText(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: 228.w,
|
||||
child: ReadMoreText(
|
||||
isQuestion
|
||||
? dto.question ?? 'Ввести текст вопроса'
|
||||
: dto.answer ?? 'Ввести текст ответа',
|
||||
isExpandable: true,
|
||||
trimMode: TrimMode.Line,
|
||||
trimLines: 3,
|
||||
trimCollapsedText: '\nРазвернуть',
|
||||
trimExpandedText: '\nСвернуть',
|
||||
style: Regular16px().style,
|
||||
moreStyle: Regular12px().style.copyWith(color: AppColors.primary_blue),
|
||||
lessStyle: Regular12px().style.copyWith(color: AppColors.primary_blue),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Картинка
|
||||
Widget _buildImage() {
|
||||
return GestureDetector(
|
||||
onTap: onImageTap,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(right: 8).r,
|
||||
child: SizedBox.square(
|
||||
dimension: 64.r,
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(8)).r,
|
||||
gradient: const LinearGradient(
|
||||
colors: <Color>[Color(0xFFDBD7F4), Color(0xFFB6AAFE)],
|
||||
begin: Alignment.topRight,
|
||||
end: Alignment.bottomLeft,
|
||||
),
|
||||
),
|
||||
child: Wif(
|
||||
condition:
|
||||
isQuestion
|
||||
? dto.questionImage != null
|
||||
: dto.answerImage != null,
|
||||
builder: (context) {
|
||||
return ClipRRect(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(8)).r,
|
||||
child: Image.memory(
|
||||
isQuestion ? dto.questionImage! : dto.answerImage!,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
);
|
||||
},
|
||||
fallback: (context) {
|
||||
return Center(
|
||||
child: Assets.icons.typePhoto.image(
|
||||
height: 24.h,
|
||||
width: 24.w,
|
||||
color: AppColors.primary,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Декорирование контейнера
|
||||
BoxDecoration getDecoration() {
|
||||
return BoxDecoration(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)).r,
|
||||
gradient: LinearGradient(
|
||||
colors: <Color>[
|
||||
isQuestion ? AppColors.question : AppColors.answer,
|
||||
Colors.white,
|
||||
],
|
||||
begin: Alignment.topLeft,
|
||||
end: const Alignment(-0.6, 1),
|
||||
stops: const <double>[0.25, 0.25],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ import 'package:remever/components/extensions/context.dart';
|
||||
import 'package:remever/database/database.dart';
|
||||
import 'package:remever/gen/assets.gen.dart';
|
||||
import 'package:remever/inject.dart';
|
||||
import 'package:remever/models/collection_dto.dart';
|
||||
import 'package:remever/models/crud_collection_dto.dart';
|
||||
import 'package:remever/router.gr.dart';
|
||||
import 'package:remever/screens/crud_collection/widgets/crud_collection_field.dart';
|
||||
import 'package:remever/screens/dialogs/alert_dialog.dart';
|
||||
@@ -39,7 +39,7 @@ class _CrudCollectionScreenState extends State<CrudCollectionScreen> {
|
||||
/// Флаг публичности коллекции
|
||||
bool _isPublic = false;
|
||||
|
||||
CollectionDto? _collection;
|
||||
CrudCollectionDto? _collection;
|
||||
|
||||
/// Смена публичности
|
||||
void _setPublic(bool public) {
|
||||
@@ -49,7 +49,7 @@ class _CrudCollectionScreenState extends State<CrudCollectionScreen> {
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_collection = CollectionDto(
|
||||
_collection = CrudCollectionDto(
|
||||
desc: widget.editedCollection?.desc ?? '',
|
||||
title: widget.editedCollection?.title ?? '',
|
||||
isPublic: widget.editedCollection?.isPublic ?? false,
|
||||
@@ -78,15 +78,17 @@ class _CrudCollectionScreenState extends State<CrudCollectionScreen> {
|
||||
final file = File(filePath);
|
||||
final bytes = await file.readAsBytes();
|
||||
|
||||
final base64String = base64Encode(bytes);
|
||||
// final base64String = base64Encode(bytes);
|
||||
|
||||
_collection = _collection?.copyWith(avatar: base64String);
|
||||
_collection = _collection?.copyWith(avatar: bytes);
|
||||
|
||||
safeSetState(() {});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
logger.logBuild('build create screen');
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.gray_bg,
|
||||
appBar: _buildAppBar(context),
|
||||
@@ -107,7 +109,7 @@ class _CrudCollectionScreenState extends State<CrudCollectionScreen> {
|
||||
const HSpace(16),
|
||||
..._buildDescription(context),
|
||||
const HSpace(16),
|
||||
_buildPublickSwitch(),
|
||||
// _buildPublickSwitch(),
|
||||
const HSpace(16),
|
||||
AnimatedOpacity(
|
||||
// opacity: _isPublic ? 1 : 0,
|
||||
@@ -370,10 +372,7 @@ class _CrudCollectionScreenState extends State<CrudCollectionScreen> {
|
||||
condition: _collection!.avatar != null,
|
||||
builder:
|
||||
(context) => ClipOval(
|
||||
child: Image.memory(
|
||||
base64Decode(_collection!.avatar!),
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
child: Image.memory(_collection!.avatar!, fit: BoxFit.cover),
|
||||
),
|
||||
fallback:
|
||||
(context) => SizedBox.square(
|
||||
|
||||
@@ -12,14 +12,59 @@ import 'package:remever/screens/dialogs/alert_dialog.dart';
|
||||
import 'package:remever/screens/dialogs/dialog_header.dart';
|
||||
import 'package:remever/screens/dialogs/dialog_item.dart';
|
||||
import 'package:remever/services/collection/collections_interface.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
|
||||
class ActionDialog extends StatelessWidget {
|
||||
const ActionDialog({super.key, required this.collection});
|
||||
|
||||
final Collection collection;
|
||||
|
||||
void _onTrainingTap(BuildContext context) {
|
||||
showInfoToast('Все скоро будет. Рим не сразу строился');
|
||||
|
||||
Navigator.pop(context);
|
||||
}
|
||||
|
||||
void _onEditTap(BuildContext context) {
|
||||
Navigator.pop(context);
|
||||
context.pushRoute(CrudCollectionRoute(editedCollection: collection));
|
||||
}
|
||||
|
||||
void _onStatistickTap(BuildContext context) {
|
||||
showInfoToast('Ты серьезно?');
|
||||
Navigator.pop(context);
|
||||
}
|
||||
|
||||
void _onDownloadTap(BuildContext context) {
|
||||
showInfoToast('Ты серьезно?');
|
||||
Navigator.pop(context);
|
||||
}
|
||||
|
||||
void _onShareTap(BuildContext context) async {
|
||||
await Share.share('View my collection ${collection.id}');
|
||||
}
|
||||
|
||||
void _onDeleteTap(BuildContext context) async {
|
||||
final bool? res = await showCuperModalBottomSheet(
|
||||
context: context,
|
||||
height: 262.h,
|
||||
builder:
|
||||
(BuildContext context) => const AlertInfoDialog(
|
||||
title: 'Вы хотите удалить коллекцию?\nЭто действие необратимо',
|
||||
acceptTitle: 'Да, удалить',
|
||||
declineTitle: 'Нет, оставить',
|
||||
),
|
||||
);
|
||||
|
||||
if (res != null && res) {
|
||||
await getIt<CollectionsInterface>().deleteCollection(collection.id);
|
||||
|
||||
Navigator.pop(context);
|
||||
}
|
||||
}
|
||||
|
||||
void _makePublic(BuildContext context, bool public) {
|
||||
if (public) {
|
||||
if (!public) {
|
||||
showCuperModalBottomSheet(
|
||||
context: context,
|
||||
height: 282.h,
|
||||
@@ -55,65 +100,32 @@ class ActionDialog extends StatelessWidget {
|
||||
DialogItem(
|
||||
title: 'Исключена из тренировки',
|
||||
child: Assets.icons.typeHide.image(color: AppColors.primary),
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
onTap: () => _onTrainingTap(context),
|
||||
),
|
||||
DialogItem(
|
||||
title: 'Редактировать',
|
||||
child: Assets.icons.typeEdit.image(color: AppColors.primary),
|
||||
onTap: () {
|
||||
// context.back();
|
||||
context.pushRoute(
|
||||
CrudCollectionRoute(editedCollection: collection),
|
||||
);
|
||||
},
|
||||
onTap: () => _onEditTap(context),
|
||||
),
|
||||
DialogItem(
|
||||
title: 'Статистика',
|
||||
child: Assets.icons.typeStat.image(color: AppColors.primary),
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
onTap: () => _onStatistickTap(context),
|
||||
),
|
||||
DialogItem(
|
||||
title: 'Скачать',
|
||||
child: Assets.icons.typeDownload.image(color: AppColors.primary),
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
onTap: () => _onDownloadTap(context),
|
||||
),
|
||||
DialogItem(
|
||||
title: 'Поделиться',
|
||||
child: Assets.icons.typeShare.image(color: AppColors.primary),
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
onTap: () => _onShareTap(context),
|
||||
),
|
||||
DialogItem(
|
||||
title: 'Удалить',
|
||||
color: AppColors.danger,
|
||||
onTap: () async {
|
||||
final bool? res = await showCuperModalBottomSheet(
|
||||
context: context,
|
||||
height: 262.h,
|
||||
builder:
|
||||
(BuildContext context) => const AlertInfoDialog(
|
||||
title:
|
||||
'Вы хотите удалить коллекцию?\nЭто действие необратимо',
|
||||
acceptTitle: 'Да, удалить',
|
||||
declineTitle: 'Нет, оставить',
|
||||
),
|
||||
);
|
||||
|
||||
if (true) {
|
||||
await getIt<CollectionsInterface>().deleteCollection(
|
||||
collection.id,
|
||||
);
|
||||
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
onTap: () => _onDeleteTap(context),
|
||||
child: Assets.icons.typeTrash.image(color: AppColors.danger),
|
||||
),
|
||||
const BottomSafeSpace(),
|
||||
|
||||
@@ -1,17 +1,23 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:remever/common/resources.dart';
|
||||
import 'package:remever/common/widgets/typography.dart';
|
||||
import 'package:remever/common/widgets/w_if.dart';
|
||||
import 'package:remever/common/widgets/wspace.dart';
|
||||
import 'package:remever/components/extensions/context.dart';
|
||||
import 'package:remever/gen/assets.gen.dart';
|
||||
import 'package:remever/database/database.dart';
|
||||
import 'package:remever/widgets/primary_button.dart';
|
||||
|
||||
class InfoDialog extends StatelessWidget {
|
||||
const InfoDialog({super.key});
|
||||
const InfoDialog({super.key, required this.collection});
|
||||
|
||||
final Collection collection;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
return Material(
|
||||
child: Container(
|
||||
decoration: const BoxDecoration(color: Colors.white),
|
||||
constraints: BoxConstraints(
|
||||
minHeight: MediaQuery.sizeOf(context).height / 6,
|
||||
@@ -20,6 +26,7 @@ class InfoDialog extends StatelessWidget {
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 32, horizontal: 16).r,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Row(
|
||||
@@ -44,6 +51,7 @@ class InfoDialog extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -52,13 +60,7 @@ class InfoDialog extends StatelessWidget {
|
||||
child: SingleChildScrollView(
|
||||
controller: ScrollController(),
|
||||
child: AppTypography(
|
||||
'Предварительные выводы неутешительны: убеждённость некоторых оппонентов способствует подготовке и реализации форм воздействия. Как принято считать, предприниматели в сети интернет представляют собой не что иное, как квинтэссенцию победы маркетинга над разумом и должны быть описаны максимально подробно.'
|
||||
'Идейные соображения высшего порядка, а также высокотехнологичная концепция общественного уклада говорит о возможностях кластеризации усилий. Задача организации, в особенности же реализация намеченных плановых заданий выявляет срочную потребность первоочередных требований. Вот вам яркий пример современных тенденций — существующая теория обеспечивает широкому кругу (специалистов) участие в формировании поставленных обществом задач. Имеется спорная точка зрения, гласящая примерно следующее: сторонники тоталитаризма в науке освещают чрезвычайно интересные особенности картины в целом, однако конкретные выводы, разумеется, представлены в исключительно положительном свете. Лишь базовые сценарии поведения пользователей представляют собой не что иное, как квинтэссенцию победы маркетинга над разумом и должны быть функционально разнесены на независимые элементы. Каждый из нас понимает очевидную вещь: начало повседневной работы по формированию позиции способствует повышению качества распределения внутренних резервов и ресурсов.'
|
||||
'В своём стремлении улучшить пользовательский опыт мы упускаем, что тщательные исследования конкурентов набирают популярность среди определенных слоев населения, а значит, должны быть представлены в исключительно положительном свете. Противоположная точка зрения подразумевает, что непосредственные участники технического прогресса преданы социально-демократической анафеме. Вот вам яркий пример современных тенденций — выбранный нами инновационный путь не оставляет шанса для поставленных обществом задач. Задача организации, в особенности же понимание сути ресурсосберегающих технологий влечет за собой процесс внедрения и модернизации новых принципов формирования материально-технической и кадровой базы. В своём стремлении улучшить пользовательский опыт мы упускаем, что непосредственные участники технического прогресса ассоциативно распределены по отраслям. Высокий уровень вовлечения представителей целевой аудитории является четким доказательством простого факта: синтетическое тестирование влечет за собой процесс внедрения и модернизации системы массового участия!'
|
||||
'Сложно сказать, почему стремящиеся вытеснить традиционное производство, нанотехнологии призваны к ответу. Но предприниматели в сети интернет, инициированные исключительно синтетически, призваны к ответу. Следует отметить, что экономическая повестка сегодняшнего дня способствует подготовке и реализации глубокомысленных рассуждений. Равным образом, укрепление и развитие внутренней структуры в значительной степени обусловливает важность соответствующих условий активизации. Противоположная точка зрения подразумевает, что диаграммы связей будут призваны к ответу. И нет сомнений, что независимые государства формируют глобальную экономическую сеть и при этом — ограничены исключительно образом мышления.'
|
||||
'И нет сомнений, что реплицированные с зарубежных источников, современные исследования освещают чрезвычайно интересные особенности картины в целом, однако конкретные выводы, разумеется, разоблачены! Мы вынуждены отталкиваться от того, что современная методология разработки говорит о возможностях как самодостаточных, так и внешне зависимых концептуальных решений. Повседневная практика показывает, что курс на социально-ориентированный национальный проект требует от нас анализа распределения внутренних резервов и ресурсов. Задача организации, в особенности же курс на социально-ориентированный национальный проект представляет собой интересный эксперимент проверки укрепления моральных ценностей. Значимость этих проблем настолько очевидна, что социально-экономическое развитие говорит о возможностях кластеризации усилий. Не следует, однако, забывать, что дальнейшее развитие различных форм деятельности в значительной степени обусловливает важность прогресса профессионального сообщества.'
|
||||
'А ещё реплицированные с зарубежных источников, современные исследования могут быть превращены в посмешище, хотя само их существование приносит несомненную пользу обществу. Задача организации, в особенности же социально-экономическое развитие является качественно новой ступенью системы обучения кадров, соответствующей насущным потребностям. В целом, конечно, начало повседневной работы по формированию позиции требует анализа поставленных обществом задач. Но курс на социально-ориентированный национальный проект говорит о возможностях экспериментов, поражающих по своей масштабности и грандиозности. Прежде всего, существующая теория создаёт предпосылки для экономической целесообразности принимаемых решений. Внезапно, представители современных социальных резервов лишь добавляют фракционных разногласий и разоблачены.'
|
||||
'В рамках спецификации современных стандартов, действия представителей оппозиции ассоциативно распределены по отраслям. Не следует, однако, забывать, что новая модель организационной деятельности позволяет оценить значение соответствующих условий активизации. Значимость этих проблем настолько очевидна, что курс на социально-ориентированный национальный проект позволяет выполнить важные задания по разработке глубокомысленных рассуждений. Современные технологии достигли такого уровня, что начало повседневной работы по формированию позиции обеспечивает широкому кругу (специалистов) участие в формировании инновационных методов управления процессами. Имеется спорная точка зрения, гласящая примерно следующее: активно развивающиеся страны третьего мира будут объявлены нарушающими общечеловеческие нормы этики и морали. Картельные сговоры не допускают ситуации, при которой диаграммы связей будут обнародованы.',
|
||||
collection.desc,
|
||||
type: Regular14px(),
|
||||
maxLines: 9999,
|
||||
),
|
||||
@@ -71,7 +73,7 @@ class InfoDialog extends StatelessWidget {
|
||||
///
|
||||
Widget _buildCollectionTitle() {
|
||||
return AppTypography(
|
||||
'Астрономия и тайная комната Харли Хоттера',
|
||||
collection.title,
|
||||
type: Medium16px(),
|
||||
maxLines: 2,
|
||||
softWrap: true,
|
||||
@@ -86,9 +88,21 @@ class InfoDialog extends StatelessWidget {
|
||||
return SizedBox.square(
|
||||
dimension: 40.r,
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
image: DecorationImage(image: Assets.images.img.provider()),
|
||||
decoration: BoxDecoration(shape: BoxShape.circle, color: AppColors.bg),
|
||||
|
||||
child: Wif(
|
||||
condition: collection.image != null,
|
||||
builder:
|
||||
(context) => ClipOval(
|
||||
child: Image.memory(collection.image!, fit: BoxFit.cover),
|
||||
),
|
||||
fallback:
|
||||
(context) => Center(
|
||||
child: AppTypography(
|
||||
collection.title.substring(0, 1),
|
||||
type: Bold34px(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1,37 +1,125 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:remever/common/functions.dart';
|
||||
import 'package:remever/common/resources.dart';
|
||||
import 'package:remever/common/utils.dart';
|
||||
import 'package:remever/common/widgets/typography.dart';
|
||||
import 'package:remever/common/widgets/w_if.dart';
|
||||
import 'package:remever/common/widgets/wspace.dart';
|
||||
import 'package:remever/components/extensions/context.dart';
|
||||
import 'package:remever/components/extensions/state.dart';
|
||||
import 'package:remever/database/database.dart';
|
||||
import 'package:remever/gen/assets.gen.dart';
|
||||
import 'package:remever/inject.dart';
|
||||
import 'package:remever/router.gr.dart';
|
||||
import 'package:remever/screens/collections/widgets/collection_progress_bar.dart';
|
||||
import 'package:remever/screens/dialogs/dialog_header.dart';
|
||||
import 'package:remever/services/tickets/tickets_interface.dart';
|
||||
import 'package:remever/widgets/primary_button.dart';
|
||||
|
||||
class ReplaceDialog extends StatelessWidget {
|
||||
const ReplaceDialog({super.key});
|
||||
/// Диалог для переноса карточки в другую коллекцию
|
||||
class ReplaceDialog extends StatefulWidget {
|
||||
const ReplaceDialog({
|
||||
super.key,
|
||||
required this.currentCollection,
|
||||
required this.ticket,
|
||||
});
|
||||
|
||||
final Collection currentCollection;
|
||||
final Ticket ticket;
|
||||
|
||||
@override
|
||||
State<ReplaceDialog> createState() => _ReplaceDialogState();
|
||||
}
|
||||
|
||||
class _ReplaceDialogState extends State<ReplaceDialog> {
|
||||
/// Коллекция, в которую будет перенесена карточка
|
||||
Collection? _collectionToTransfer;
|
||||
|
||||
/// Обработчик выбора коллекции
|
||||
void _onCollectionTap() {
|
||||
context.pushRoute(
|
||||
CollectionSearchRoute(
|
||||
onCollectionSelect: (collection) {
|
||||
safeSetState(() => _collectionToTransfer = collection);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Обработчик кнопки "Перенести"
|
||||
Future<void> _onTransferTap() async {
|
||||
// Проверяем, что выбрана коллекция для переноса
|
||||
if (_collectionToTransfer == null) {
|
||||
showErrorToast('Необходимо выбрать место для переноса');
|
||||
return;
|
||||
}
|
||||
|
||||
// Проверяем, что выбранная коллекция не совпадает с текущей
|
||||
if (_collectionToTransfer!.id == widget.currentCollection.id) {
|
||||
showErrorToast(
|
||||
'Карточка уже находится в этой коллекции, выберите другую',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Переносим карточку через сервис
|
||||
await getIt<TicketsInterface>().transferTicket(
|
||||
widget.ticket.id,
|
||||
_collectionToTransfer!.id,
|
||||
);
|
||||
|
||||
// Закрываем диалог
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
return Material(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
children: [
|
||||
const DialogHeader(title: 'Переместить карточку'),
|
||||
const HSpace(16),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 28).r,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
children: [
|
||||
AppTypography('из коллекции', type: Medium16px()),
|
||||
const HSpace(8),
|
||||
_buildCollection(context),
|
||||
_buildCollection(widget.currentCollection),
|
||||
const HSpace(16),
|
||||
Center(
|
||||
_buildArrowIcon(),
|
||||
const HSpace(16),
|
||||
AppTypography('в коллекцию', type: Medium16px()),
|
||||
const HSpace(8),
|
||||
_buildCollection(_collectionToTransfer, isSelectable: true),
|
||||
const HSpace(16),
|
||||
_buildTransferButton(),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Кнопка "Перенести"
|
||||
Widget _buildTransferButton() {
|
||||
return PrimaryButton(
|
||||
onTap: _onTransferTap,
|
||||
child: AppTypography(
|
||||
'Перенести',
|
||||
type: Regular14px(),
|
||||
color: AppColors.white,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Иконка стрелки между коллекциями
|
||||
Widget _buildArrowIcon() {
|
||||
return Center(
|
||||
child: SizedBox.square(
|
||||
dimension: 34.r,
|
||||
child: DecoratedBox(
|
||||
@@ -40,56 +128,17 @@ class ReplaceDialog extends StatelessWidget {
|
||||
color: AppColors.gray,
|
||||
),
|
||||
child: Center(
|
||||
child: Assets.icons.typeArrowDown.image(
|
||||
height: 18.h,
|
||||
width: 18.w,
|
||||
child: Assets.icons.typeArrowDown.image(height: 18.h, width: 18.w),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const HSpace(16),
|
||||
AppTypography('в коллекцию', type: Medium16px()),
|
||||
const HSpace(8),
|
||||
_buildCollection(context),
|
||||
const HSpace(16),
|
||||
_createBtn(context),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _createBtn(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
// context.read<HomeCubit>().toCrudCollection(CrudType.CREATE);
|
||||
context.pushRoute(CrudCollectionRoute());
|
||||
},
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
SizedBox.square(
|
||||
dimension: 20.r,
|
||||
child: DecoratedBox(
|
||||
decoration: const BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: AppColors.gray,
|
||||
),
|
||||
child: Assets.icons.typePlus.image(color: Colors.black54),
|
||||
),
|
||||
),
|
||||
const WSpace(4),
|
||||
AppTypography('Создать и перенести в новую', type: Regular16px()),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCollection(BuildContext context) {
|
||||
/// Виджет отображения коллекции
|
||||
Widget _buildCollection(Collection? collection, {bool isSelectable = false}) {
|
||||
return GestureDetector(
|
||||
onTap: () {},
|
||||
onTap: isSelectable ? _onCollectionTap : null,
|
||||
child: Container(
|
||||
constraints: BoxConstraints(minHeight: 66.h, maxHeight: 84.h),
|
||||
decoration: BoxDecoration(
|
||||
@@ -97,27 +146,37 @@ class ReplaceDialog extends StatelessWidget {
|
||||
color: AppColors.white,
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8).r,
|
||||
child: Row(
|
||||
children: <Widget>[_buildAvatar(), const WSpace(5), _buildInfo()],
|
||||
child:
|
||||
collection == null
|
||||
? Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
AppTypography('Выберите коллекцию', type: Bold14px()),
|
||||
],
|
||||
)
|
||||
: Row(
|
||||
children: [
|
||||
_buildAvatar(collection),
|
||||
const WSpace(5),
|
||||
_buildInfo(collection),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
///
|
||||
/// Построение основной информации
|
||||
///
|
||||
Widget _buildInfo() {
|
||||
/// Информация о коллекции
|
||||
Widget _buildInfo(Collection collection) {
|
||||
return SizedBox(
|
||||
width: 230.w,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
_buildTitle(),
|
||||
children: [
|
||||
_buildTitle(collection),
|
||||
const HSpace(4),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
children: [
|
||||
Assets.icons.typeCards.image(
|
||||
height: 18.h,
|
||||
width: 18.w,
|
||||
@@ -125,7 +184,7 @@ class ReplaceDialog extends StatelessWidget {
|
||||
),
|
||||
const WSpace(2),
|
||||
AppTypography(
|
||||
Random().nextInt(654).toString(),
|
||||
'${collection.likesCount} ${Utils.declOfNum(collection.likesCount, ['карточек', 'карточки', 'карточек'])}',
|
||||
type: Regular14px(),
|
||||
color: AppColors.disabled,
|
||||
),
|
||||
@@ -138,28 +197,35 @@ class ReplaceDialog extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
///
|
||||
/// Название коллекции
|
||||
///
|
||||
Widget _buildTitle() {
|
||||
Widget _buildTitle(Collection collection) {
|
||||
return AppTypography(
|
||||
'Астрономия',
|
||||
collection.title,
|
||||
type: Medium16px(),
|
||||
maxLines: 2,
|
||||
softWrap: true,
|
||||
);
|
||||
}
|
||||
|
||||
///
|
||||
/// Обложка коллекции
|
||||
///
|
||||
Widget _buildAvatar() {
|
||||
Widget _buildAvatar(Collection collection) {
|
||||
return SizedBox.square(
|
||||
dimension: 50.r,
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
image: DecorationImage(image: Assets.images.img.provider()),
|
||||
decoration: BoxDecoration(shape: BoxShape.circle, color: AppColors.bg),
|
||||
child: Wif(
|
||||
condition: collection.image != null,
|
||||
builder:
|
||||
(context) => ClipOval(
|
||||
child: Image.memory(collection.image!, fit: BoxFit.cover),
|
||||
),
|
||||
fallback:
|
||||
(context) => Center(
|
||||
child: AppTypography(
|
||||
collection.title.substring(0, 1),
|
||||
type: Bold34px(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
|
||||
|
||||
// Package imports:
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:remever/common/functions.dart';
|
||||
import 'package:remever/common/resources.dart';
|
||||
import 'package:remever/common/widgets/wspace.dart';
|
||||
import 'package:remever/components/extensions/context.dart';
|
||||
@@ -18,7 +19,7 @@ class HomeScreen extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
print('build home screen');
|
||||
logger.logBuild('build home screen');
|
||||
|
||||
return SafeArea(
|
||||
top: false,
|
||||
|
||||
@@ -4,9 +4,12 @@ import 'package:drift_db_viewer/drift_db_viewer.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get_it_mixin/get_it_mixin.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:remever/common/functions.dart';
|
||||
import 'package:remever/common/widgets/typography.dart';
|
||||
import 'package:remever/common/widgets/wspace.dart';
|
||||
import 'package:remever/database/database.dart';
|
||||
import 'package:remever/widgets/primary_button.dart';
|
||||
import 'package:talker_flutter/talker_flutter.dart';
|
||||
|
||||
import '../../components/notifiers/app_settings.dart';
|
||||
import '../../components/env.dart';
|
||||
@@ -34,6 +37,17 @@ class _SandboxScreenState extends State<SandboxScreen> {
|
||||
const HSpace(8),
|
||||
_debugBox(),
|
||||
const HSpace(8),
|
||||
PrimaryButton(
|
||||
child: const Text('Логи'),
|
||||
onTap: () {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute<dynamic>(
|
||||
builder:
|
||||
(BuildContext context) => TalkerScreen(talker: talker),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -40,7 +40,9 @@ final class AuthService implements AuthInterface {
|
||||
if (response['success'] == false) return null;
|
||||
|
||||
return response['result']['authUid'];
|
||||
} catch (e) {
|
||||
} catch (e, st) {
|
||||
logger.logError('Ошибка в методе login', e, st);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -66,10 +68,8 @@ final class AuthService implements AuthInterface {
|
||||
}
|
||||
|
||||
return success;
|
||||
} catch (e) {
|
||||
if (e is DioException) {
|
||||
showErrorToast(e.response?.data['message']);
|
||||
}
|
||||
} catch (e, st) {
|
||||
logger.logError('Ошибка в методе send code', e, st);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||