Files
Remever/lib/screens/crud_collection/crud_collection.dart

455 lines
14 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'dart:convert';
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/widgets/bottom_safe_space.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/inject.dart';
import 'package:remever/models/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';
import 'package:remever/screens/dialogs/tags_dialog.dart';
import 'package:remever/services/collection/collections_interface.dart';
import 'package:remever/widgets/primary_button.dart';
import '../../../components/extensions/state.dart';
enum CrudType { CREATE, EDIT }
@RoutePage()
class CrudCollectionScreen extends StatefulWidget {
const CrudCollectionScreen({super.key, this.crudType = CrudType.CREATE});
final CrudType crudType;
@override
State<CrudCollectionScreen> createState() => _CrudCollectionScreenState();
}
class _CrudCollectionScreenState extends State<CrudCollectionScreen> {
/// Флаг публичности коллекции
bool _isPublic = false;
CollectionDto? _collection;
/// Смена публичности
void _setPublic(bool public) {
_collection = _collection?.copyWith(isPublic: public);
safeSetState(() => _isPublic = public);
}
@override
void initState() {
_collection = CollectionDto(desc: '', title: '', isPublic: false);
super.initState();
}
void _pickImage() 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();
final base64String = base64Encode(bytes);
_collection = _collection?.copyWith(avatar: base64String);
safeSetState(() {});
print('Base64 строка: $base64String');
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.gray_bg,
appBar: _buildAppBar(context),
body: _buildMainBody(context),
);
}
/// Основное тело экрана
Widget _buildMainBody(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16).r,
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
const HSpace(16),
_buildPhotoAndTitle(context),
const HSpace(16),
..._buildDescription(context),
const HSpace(16),
_buildPublickSwitch(),
const HSpace(16),
AnimatedOpacity(
opacity: _isPublic ? 1 : 0,
duration: const Duration(seconds: 1),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
..._buildTagButton(),
const HSpace(16),
_buildTagsList(),
const HSpace(47),
],
),
),
_buildCreateBtn(context),
const BottomSafeSpace(),
],
),
),
);
}
///Кнопка создания
Widget _buildCreateBtn(BuildContext context) {
return PrimaryButton(
height: 52,
onTap: () async {
if (_collection!.desc.isEmpty && _collection!.title.isEmpty) {
showErrorToast(
'Для создания публичной коллекции добавьте описание и тэги',
);
return;
}
getIt<CollectionsInterface>().createCollection(_collection!);
context.back();
},
color: AppColors.primary,
child: AppTypography(
widget.crudType == CrudType.CREATE
? 'Создать коллекцию'
: 'Сохранить изменения',
type: Regular14px(),
color: Colors.white,
),
);
}
/// Построение списка тегов
Widget _buildTagsList() {
return SizedBox(
height: 68.h,
child: Row(
children: <Widget>[
Expanded(
child: Wrap(
runSpacing: 8.r,
spacing: 8.r,
children: List<Widget>.generate(6, (int index) {
return GestureDetector(
onTap: () {},
child: Container(
height: 30,
decoration: BoxDecoration(
borderRadius:
const BorderRadius.all(Radius.circular(6)).r,
color: const Color(0xFFFFE4E6),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
AppTypography(
'tag $index',
type: Regular14px(),
height: 0.95,
color: AppColors.danger,
),
const WSpace(8),
Center(
child: Icon(
Icons.close,
size: 14.r,
color: AppColors.danger,
),
),
],
),
),
),
);
}),
),
),
const WSpace(9),
GestureDetector(
onTap: () {
showCuperModalBottomSheet(
context: context,
height: 270.h,
builder: (BuildContext context) => const TagsDialog(),
);
},
child: AppTypography(
'+13',
type: Medium16px(),
color: AppColors.primary,
),
),
],
),
);
}
/// Построение кнопки добавления тега
List<Widget> _buildTagButton() {
return <Widget>[
AppTypography('Тэги', type: SemiBold14px()),
const HSpace(4),
CrudCollectionField(
height: 42,
width: 348,
hint: 'Добавить тэг',
// onTap: () => context.pushRoute(const AddTagsRoute()),
),
];
}
/// Построение свитчера на публичность коллекции
Widget _buildPublickSwitch() {
return GestureDetector(
onTap: () => _setPublic(!_isPublic),
child: Row(
children: <Widget>[
SizedBox.square(
dimension: 20.r,
child: Assets.icons.typePublic.image(color: AppColors.primary),
),
const WSpace(2),
Flexible(
fit: FlexFit.tight,
child: AppTypography(
'Публичная коллекция',
type: Medium16px(),
color: AppColors.primary,
),
),
const WSpace(2),
SizedBox(
height: 20.h,
width: 36.w,
child: FittedBox(
fit: BoxFit.contain,
child: CupertinoSwitch(
activeTrackColor: AppColors.primary,
value: _isPublic,
onChanged: _setPublic,
),
),
),
],
),
);
}
/// Построение блока с описанием
List<Widget> _buildDescription(BuildContext context) {
return <Widget>[
AppTypography('Описание', type: SemiBold14px()),
const HSpace(4),
CrudCollectionField(
height: 110,
width: 348,
hint: 'Добавить описание',
content: _collection?.desc,
onTap: () {
context.pushRoute(
CrudCollectionFullscreenField(
title: 'Описание',
height: 333,
content: _collection?.desc,
onEditingComplete: (res) {
safeSetState(
() => _collection = _collection?.copyWith(desc: res ?? ''),
);
},
),
);
},
),
];
}
/// Построение блока фото и заголовка
Widget _buildPhotoAndTitle(BuildContext context) {
return Row(
children: <Widget>[_buildPhoto(), const WSpace(8), _buildTitle(context)],
);
}
/// Построение поля для ввода заголовка
Widget _buildTitle(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
AppTypography('Название', type: SemiBold14px()),
const HSpace(4),
CrudCollectionField(
height: 91,
width: 225,
hint: 'Добавить название',
content: _collection?.title,
onTap: () {
context.pushRoute(
CrudCollectionFullscreenField(
title: 'Название',
hint: 'Максимальное количество символов - 250',
content: _collection?.title,
onEditingComplete: (res) {
safeSetState(
() => _collection = _collection?.copyWith(title: res ?? ''),
);
},
),
);
},
),
],
);
}
/// Построение обложки
Widget _buildPhoto() {
return GestureDetector(
onTap: () => _pickImage(),
child: SizedBox.square(
dimension: 115.r,
child: DecoratedBox(
decoration: const BoxDecoration(
shape: BoxShape.circle,
gradient: LinearGradient(
colors: <Color>[Color(0xFFB6AAFE), Color(0xFFDBD7F4)],
begin: Alignment.bottomLeft,
end: Alignment.topRight,
),
),
child: Wif(
condition: _collection!.avatar != null,
builder:
(context) => ClipOval(
child: Image.memory(
base64Decode(_collection!.avatar!),
fit: BoxFit.cover,
),
),
fallback:
(context) => SizedBox.square(
dimension: 32.r,
child: Center(
child: Assets.icons.typePhoto.image(
height: 32.h,
width: 32.w,
color: AppColors.primary,
),
),
),
),
),
),
);
}
/// Построение шапки
AppBar _buildAppBar(BuildContext context) {
return AppBar(
toolbarHeight: 56.h,
backgroundColor: AppColors.white,
shadowColor: Colors.transparent,
leading: IconButton(
onPressed: () async {
if (widget.crudType == CrudType.EDIT) {
final bool? res = await showCuperModalBottomSheet(
context: context,
height: 262.h,
builder:
(BuildContext context) => const AlertInfoDialog(
title: 'Вы хотите сбросить все внесенные изменения?',
acceptTitle: 'Да, сбросить',
declineTitle: 'Нет, оставить',
),
);
if (res != null && res) context.back();
} else {
context.back();
}
},
icon: const Icon(CupertinoIcons.left_chevron, color: Colors.black),
),
centerTitle: true,
title: GestureDetector(
onLongPress: () => context.pushRoute(const SandboxRoute()),
child: AppTypography(
widget.crudType == CrudType.CREATE
? 'Создать коллекцию'
: 'Редактировать',
type: SemiBold20px(),
color: AppColors.body_text,
),
),
actions: <Widget>[
Wif(
condition: widget.crudType == CrudType.EDIT,
builder: (BuildContext context) {
return Padding(
padding: const EdgeInsets.only(right: 16).r,
child: GestureDetector(
onTap: () {
showCuperModalBottomSheet(
context: context,
height: 262.h,
builder:
(BuildContext context) => const AlertInfoDialog(
title: 'Вы хотите сбросить все внесенные изменения?',
acceptTitle: 'Да, сбросить',
declineTitle: 'Нет, оставить',
),
);
},
child: Assets.icons.typeTrash.image(
height: 24.h,
width: 24.w,
color: AppColors.primary,
),
),
);
},
),
],
);
}
}