diff --git a/assets/images/bagMoa.png b/assets/images/bagMoa.png new file mode 100644 index 0000000..2b0ecb4 Binary files /dev/null and b/assets/images/bagMoa.png differ diff --git a/assets/images/book.png b/assets/images/book.png new file mode 100644 index 0000000..4382263 Binary files /dev/null and b/assets/images/book.png differ diff --git a/assets/images/brush.png b/assets/images/brush.png new file mode 100644 index 0000000..b4ca51d Binary files /dev/null and b/assets/images/brush.png differ diff --git a/assets/images/heartMoa.png b/assets/images/heartMoa.png new file mode 100644 index 0000000..ad43f38 Binary files /dev/null and b/assets/images/heartMoa.png differ diff --git a/assets/images/hearts.png b/assets/images/hearts.png new file mode 100644 index 0000000..5d9e4a3 Binary files /dev/null and b/assets/images/hearts.png differ diff --git a/assets/images/location.png b/assets/images/location.png new file mode 100644 index 0000000..372da3f Binary files /dev/null and b/assets/images/location.png differ diff --git a/assets/images/movie.png b/assets/images/movie.png new file mode 100644 index 0000000..b49887b Binary files /dev/null and b/assets/images/movie.png differ diff --git a/assets/images/movie2.png b/assets/images/movie2.png new file mode 100644 index 0000000..7233e98 Binary files /dev/null and b/assets/images/movie2.png differ diff --git a/assets/images/newsMoa.png b/assets/images/newsMoa.png new file mode 100644 index 0000000..164689d Binary files /dev/null and b/assets/images/newsMoa.png differ diff --git a/assets/images/recipe.png b/assets/images/recipe.png new file mode 100644 index 0000000..6f205fc Binary files /dev/null and b/assets/images/recipe.png differ diff --git a/assets/images/style.png b/assets/images/style.png new file mode 100644 index 0000000..71362d5 Binary files /dev/null and b/assets/images/style.png differ diff --git a/lib/constants/file_constants.dart b/lib/constants/file_constants.dart index 0a3572c..e7356cb 100644 --- a/lib/constants/file_constants.dart +++ b/lib/constants/file_constants.dart @@ -44,6 +44,18 @@ class Assets { static AssetImage profileMoa = const AssetImage('$_imgPath/profileMoa.png'); static AssetImage link = const AssetImage('$_imgPath/link.png'); static AssetImage image = const AssetImage('$_imgPath/image.png'); + + static AssetImage bagMoa = const AssetImage('$_imgPath/bagMoa.png'); + static AssetImage book = const AssetImage('$_imgPath/book.png'); + static AssetImage brush = const AssetImage('$_imgPath/brush.png'); + static AssetImage heartMoa = const AssetImage('$_imgPath/heartMoa.png'); + static AssetImage hearts = const AssetImage('$_imgPath/hearts.png'); + static AssetImage location = const AssetImage('$_imgPath/location.png'); + static AssetImage movie = const AssetImage('$_imgPath/movie.png'); + static AssetImage movie2 = const AssetImage('$_imgPath/movie2.png'); + static AssetImage newsMoa = const AssetImage('$_imgPath/newsMoa.png'); + static AssetImage recipe = const AssetImage('$_imgPath/recipe.png'); + static AssetImage style = const AssetImage('$_imgPath/style.png'); } class Svgs { diff --git a/lib/screens/add_content/add_image_content.dart b/lib/screens/add_content/add_image_content.dart index b683208..d159a2d 100644 --- a/lib/screens/add_content/add_image_content.dart +++ b/lib/screens/add_content/add_image_content.dart @@ -78,6 +78,7 @@ class AddImageContent extends HookConsumerWidget { } loading.value = true; + String base64Image = await xFileToBase64(imageFile.value!); var selectTag = []; diff --git a/lib/screens/add_content/add_link_content.dart b/lib/screens/add_content/add_link_content.dart index eadd152..3b37819 100644 --- a/lib/screens/add_content/add_link_content.dart +++ b/lib/screens/add_content/add_link_content.dart @@ -1,7 +1,9 @@ +import 'dart:convert'; import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; @@ -34,15 +36,30 @@ class AddLinkContent extends HookConsumerWidget { var loading = useState(false); var picker = ImagePicker(); var imageFile = useState(null); - var receiveImage = useState(null); - // var defaultImageList = useState>([]); - + var defaultImage = useState(null); + var defaultImageList = useState>([ + null, + Assets.bagMoa, + Assets.book, + Assets.brush, + Assets.heartMoa, + Assets.hearts, + Assets.location, + Assets.movie, + Assets.movie2, + Assets.newsMoa, + Assets.recipe, + Assets.style + ]); + + var selectedIndex = useState(-1); var title = useState(''); var link = useState(''); var memo = useState(''); var hashtag = useState(''); var hashtagController = useTextEditingController(); var selectedTagList = useState>([]); + var lifeCycle = useAppLifecycleState(); var imageError = useState(''); var titleError = useState(''); @@ -60,11 +77,7 @@ class AddLinkContent extends HookConsumerWidget { return; } - // todo 기본제공 이미지 10종 리스트에서 추가 - // imageFile.value = defaultImageList.value[index - 1]; - // todo 대표 이미지 미지정시 하트들고있는 모아 이미지로 대체 - if (imageFile.value != null) { imageError.value = ''; } @@ -89,8 +102,12 @@ class AddLinkContent extends HookConsumerWidget { return; } loading.value = true; - - String base64Image = await xFileToBase64(imageFile.value!); + late String base64Image; + if (defaultImage.value == null) { + base64Image = await xFileToBase64(imageFile.value!); + } else { + base64Image = defaultImage.value!; + } var selectTag = []; selectedTagList.value.map((element) { @@ -181,12 +198,16 @@ class AddLinkContent extends HookConsumerWidget { var crawledTitle = document.head ?.querySelector("meta[property='og:title']") ?.attributes['content']; + + if (crawledTitle != null && crawledTitle.length > 30) { + crawledTitle = '${crawledTitle.substring(0, 30)}...'; + } var crawledDescription = document.head ?.querySelector("meta[property='og:description']") ?.attributes['content']; - var crawledImage = document.head - ?.querySelector("meta[property='og:image']") - ?.attributes['content']; + // var crawledImage = document.head + // ?.querySelector("meta[property='og:image']") + // ?.attributes['content']; link.value = url; linkController.text = url; @@ -194,18 +215,16 @@ class AddLinkContent extends HookConsumerWidget { titleController.text = crawledTitle ?? ''; memo.value = crawledDescription ?? ''; memoController.text = crawledDescription ?? ''; - receiveImage.value = crawledImage ?? ''; - // imageFile.value = XFile(crawledImage ?? ''); }); } useEffect(() { - if (receiveUrl != null) { + if (receiveUrl != null && lifeCycle == AppLifecycleState.resumed) { // todo 유효한 url인지 체크필요 getCrawlUrl(receiveUrl!); } return null; - }, []); + }, [lifeCycle]); return Scaffold( appBar: const AppBarBack( @@ -215,109 +234,145 @@ class AddLinkContent extends HookConsumerWidget { body: Stack( fit: StackFit.expand, children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 15), - child: SingleChildScrollView( - physics: const ClampingScrollPhysics(), - padding: const EdgeInsets.only(bottom: 100), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - '링크', - style: H4TextStyle(), + SingleChildScrollView( + physics: const ClampingScrollPhysics(), + padding: const EdgeInsets.only(bottom: 100), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 15), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + '링크', + style: H4TextStyle(), + ), + const SizedBox(height: 5), + EditText( + controller: linkController, + onChanged: onChangedLink, + hintText: '링크를 입력하세요.', + ), + ErrorText( + errorText: linkError.value, + errorValidate: linkError.value.isNotEmpty, + ), + const SizedBox(height: 25), + const Text( + '대표 이미지', + style: H4TextStyle(), + ), + ], ), - const SizedBox(height: 5), - EditText( - controller: linkController, - onChanged: onChangedLink, - hintText: '링크를 입력하세요.', - ), - ErrorText( - errorText: linkError.value, - errorValidate: linkError.value.isNotEmpty, - ), - const SizedBox(height: 25), - const Text( - '대표 이미지', - style: H4TextStyle(), - ), - const SizedBox(height: 5), - SizedBox( - width: double.infinity, - height: 85, - child: ListView.builder( - itemCount: 11, - scrollDirection: Axis.horizontal, - itemBuilder: (context, index) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 7), - child: Ink( - width: 85, - height: 85, - decoration: BoxDecoration( - borderRadius: const BorderRadius.all( - Radius.circular(15), - ), - border: Border.all( - color: AppColors.grayBackground, - width: 0.5, - ), - color: AppColors.textInputBackground, + ), + const SizedBox(height: 5), + SizedBox( + width: double.infinity, + height: 85, + child: ListView.builder( + padding: const EdgeInsets.symmetric(horizontal: 15), + itemCount: defaultImageList.value.length, + scrollDirection: Axis.horizontal, + itemBuilder: (context, index) { + var image = defaultImageList.value[index]; + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 7), + child: Ink( + width: 85, + height: 85, + decoration: BoxDecoration( + borderRadius: const BorderRadius.all( + Radius.circular(15), + ), + border: Border.all( + color: selectedIndex.value == index + ? AppColors.primaryColor + : AppColors.grayBackground, + width: 0.5, ), - child: InkWell( - onTap: () => pickImage( - source: ImageSource.gallery, index: index), - borderRadius: const BorderRadius.all( - Radius.circular(15), - ), - child: index == 0 - ? imageFile.value != null - ? Container( - decoration: BoxDecoration( - borderRadius: - BorderRadius.circular(15), - border: Border.all( - color: AppColors.grayBackground, - width: 0.5, - ), - image: DecorationImage( - fit: BoxFit.cover, - image: FileImage( - File(imageFile.value!.path)), - ), + color: selectedIndex.value == index + ? AppColors.moaSecondary + : AppColors.textInputBackground, + ), + child: InkWell( + onTap: () async { + if (index == 0) { + pickImage( + source: ImageSource.gallery, index: index); + selectedIndex.value = -1; + return; + } + + ByteData bytes = await rootBundle + .load(image!.assetName.toString()); + var buffer = bytes.buffer; + var base64Images = + base64.encode(Uint8List.view(buffer)); + defaultImage.value = base64Images; + imageFile.value = null; + selectedIndex.value = index; + }, + borderRadius: const BorderRadius.all( + Radius.circular(15), + ), + child: index == 0 + ? imageFile.value != null + ? Container( + decoration: BoxDecoration( + borderRadius: + BorderRadius.circular(15), + border: Border.all( + color: AppColors.grayBackground, + width: 0.5, ), - ) - : Center( - child: Image( - width: 15, - height: 15, - image: Assets.circlePlus, + image: DecorationImage( + fit: BoxFit.cover, + image: FileImage( + File(imageFile.value!.path)), ), - ) - : const SizedBox(), - ), + ), + ) + : Center( + child: Image( + width: 15, + height: 15, + image: Assets.circlePlus, + ), + ) + : Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10, + vertical: 20, + ), + child: Center( + child: Image( + image: image!, + ), + ), + ), ), - ); - }, - ), + ), + ); + }, ), - AddContentBottom( - onChangedTitle: onChangedTitle, - titleController: titleController, - memoController: memoController, - addHashtag: addHashtag, - hashtagController: hashtagController, - onChangedHashtag: onChangedHashtag, - onChangedMemo: onChangedMemo, - memo: memo, - tagError: tagError, - title: title, - titleError: titleError, - selectedTagList: selectedTagList, - ) - ], - ), + ), + AddContentBottom( + onChangedTitle: onChangedTitle, + titleController: titleController, + memoController: memoController, + addHashtag: addHashtag, + hashtagController: hashtagController, + onChangedHashtag: onChangedHashtag, + onChangedMemo: onChangedMemo, + memo: memo, + tagError: tagError, + title: title, + titleError: titleError, + selectedTagList: selectedTagList, + ) + ], ), ), Positioned( diff --git a/lib/screens/add_content/widgets/add_content_bottom.dart b/lib/screens/add_content/widgets/add_content_bottom.dart index a70da95..59e845c 100644 --- a/lib/screens/add_content/widgets/add_content_bottom.dart +++ b/lib/screens/add_content/widgets/add_content_bottom.dart @@ -39,158 +39,161 @@ class AddContentBottom extends HookWidget { @override Widget build(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox(height: 25), - const Text( - '제목', - style: H4TextStyle(), - ), - const SizedBox(height: 5), - EditText( - controller: titleController, - maxLength: 30, - onChanged: onChangedTitle, - hintText: '1~30자로 입력할 수 있어요.', - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - ErrorText( - errorText: titleError.value, - errorValidate: titleError.value.isNotEmpty, - ), - Padding( - padding: const EdgeInsets.only(top: 8), - child: Text( - '${title.value.length}/30', - style: TextStyle( - color: title.value.length >= 30 - ? AppColors.danger - : AppColors.blackColor.withOpacity(0.3), - fontSize: 12, - fontFamily: FontConstants.pretendard), + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 15), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 25), + const Text( + '제목', + style: H4TextStyle(), + ), + const SizedBox(height: 5), + EditText( + controller: titleController, + maxLength: 30, + onChanged: onChangedTitle, + hintText: '1~30자로 입력할 수 있어요.', + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + ErrorText( + errorText: titleError.value, + errorValidate: titleError.value.isNotEmpty, ), - ), - ], - ), - const SizedBox(height: 25), - Row( - children: [ - const Text( - '태그', - style: H4TextStyle(), - ), - const SizedBox(width: 8), - Text( - '태그를 선택해주세요.', - style: const Body1TextStyle() - .merge(const TextStyle(color: AppColors.subTitle)), - ) - ], - ), - const SizedBox(height: 10), - Wrap( - spacing: 10, - runSpacing: 10, - children: [ - ...selectedTagList.value.map((tag) { - return InkWell( - borderRadius: BorderRadius.circular(50), - onTap: () { - selectedTagList.value = selectedTagList.value.map((e) { - if (e.name == tag.name) { - return SelectedTagModel( - name: e.name, isSelected: !e.isSelected); - } - return e; - }).toList(); - tagError.value = ''; - }, - child: HashtagBox( - isSelected: tag.isSelected, - hashtag: tag.name, + Padding( + padding: const EdgeInsets.only(top: 8), + child: Text( + '${title.value.length}/30', + style: TextStyle( + color: title.value.length >= 30 + ? AppColors.danger + : AppColors.blackColor.withOpacity(0.3), + fontSize: 12, + fontFamily: FontConstants.pretendard), ), - ); - }).toList(), - ], - ), - const SizedBox(height: 10), - EditText( - maxLength: 7, - controller: hashtagController, - hintText: '태그를 입력하세요.', - onChanged: onChangedHashtag, - suffixIcon: Container( - margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 7), - width: 35, - height: 35, - child: CircleIconButton( - backgroundColor: AppColors.primaryColor, - onPressed: addHashtag, - icon: const Icon( - Icons.add, ), - ), + ], ), - ), - Row( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - ErrorText( - errorText: tagError.value, - errorValidate: tagError.value.isNotEmpty, - ), - Padding( - padding: const EdgeInsets.only(top: 8), - child: Text( - '${hashtagController.text.length}/7', - style: TextStyle( - color: hashtagController.text.length == 7 - ? AppColors.danger - : AppColors.blackColor.withOpacity(0.3), - fontSize: 12, - fontFamily: FontConstants.pretendard), + const SizedBox(height: 25), + Row( + children: [ + const Text( + '태그', + style: H4TextStyle(), ), - ), - ], - ), - const SizedBox(height: 25), - const Text( - '메모', - style: H4TextStyle(), - ), - const SizedBox(height: 5), - Stack( - children: [ - EditText( - controller: memoController, - maxLines: 4, - maxLength: 100, - height: 135, - hintText: '메모를 입력하세요.', - borderRadius: BorderRadius.circular(15), - onChanged: onChangedMemo, - backgroundColor: AppColors.textInputBackground, - ), - Positioned( - right: 20, - bottom: 20, - child: Text( - '${memo.value.length}/100', - style: TextStyle( - color: memo.value.length == 100 - ? AppColors.danger - : AppColors.blackColor.withOpacity(0.3), - fontSize: 12, - fontFamily: FontConstants.pretendard), + const SizedBox(width: 8), + Text( + '태그를 선택해주세요.', + style: const Body1TextStyle() + .merge(const TextStyle(color: AppColors.subTitle)), + ) + ], + ), + const SizedBox(height: 10), + Wrap( + spacing: 10, + runSpacing: 10, + children: [ + ...selectedTagList.value.map((tag) { + return InkWell( + borderRadius: BorderRadius.circular(50), + onTap: () { + selectedTagList.value = selectedTagList.value.map((e) { + if (e.name == tag.name) { + return SelectedTagModel( + name: e.name, isSelected: !e.isSelected); + } + return e; + }).toList(); + tagError.value = ''; + }, + child: HashtagBox( + isSelected: tag.isSelected, + hashtag: tag.name, + ), + ); + }).toList(), + ], + ), + const SizedBox(height: 10), + EditText( + maxLength: 7, + controller: hashtagController, + hintText: '태그를 입력하세요.', + onChanged: onChangedHashtag, + suffixIcon: Container( + margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 7), + width: 35, + height: 35, + child: CircleIconButton( + backgroundColor: AppColors.primaryColor, + onPressed: addHashtag, + icon: const Icon( + Icons.add, + ), ), ), - ], - ), - ], + ), + Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + ErrorText( + errorText: tagError.value, + errorValidate: tagError.value.isNotEmpty, + ), + Padding( + padding: const EdgeInsets.only(top: 8), + child: Text( + '${hashtagController.text.length}/7', + style: TextStyle( + color: hashtagController.text.length == 7 + ? AppColors.danger + : AppColors.blackColor.withOpacity(0.3), + fontSize: 12, + fontFamily: FontConstants.pretendard), + ), + ), + ], + ), + const SizedBox(height: 25), + const Text( + '메모', + style: H4TextStyle(), + ), + const SizedBox(height: 5), + Stack( + children: [ + EditText( + controller: memoController, + maxLines: 4, + maxLength: 100, + height: 135, + hintText: '메모를 입력하세요.', + borderRadius: BorderRadius.circular(15), + onChanged: onChangedMemo, + backgroundColor: AppColors.textInputBackground, + ), + Positioned( + right: 20, + bottom: 20, + child: Text( + '${memo.value.length}/100', + style: TextStyle( + color: memo.value.length == 100 + ? AppColors.danger + : AppColors.blackColor.withOpacity(0.3), + fontSize: 12, + fontFamily: FontConstants.pretendard), + ), + ), + ], + ), + ], + ), ); } } diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index 18f2481..1ad1ef5 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -1,9 +1,9 @@ import 'dart:async'; import 'dart:convert'; -import 'dart:typed_data'; import 'dart:ui' as ui; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:image_picker/image_picker.dart'; Future xFileToBase64(XFile xFile) async { diff --git a/lib/widgets/moa_widgets/dynamic_grid_list.dart b/lib/widgets/moa_widgets/dynamic_grid_list.dart index 1bd3a57..6b9371a 100644 --- a/lib/widgets/moa_widgets/dynamic_grid_list.dart +++ b/lib/widgets/moa_widgets/dynamic_grid_list.dart @@ -77,6 +77,7 @@ class DynamicGridList extends HookWidget { : AspectRatio( aspectRatio: rate!, child: ImageOnNetwork( + // fit: BoxFit.contain, width: double.infinity, border: Border.all( color: AppColors.moaOpacity30, diff --git a/pubspec.lock b/pubspec.lock index 7d84691..ae16d50 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1008,7 +1008,7 @@ packages: source: hosted version: "1.8.3" path_provider: - dependency: transitive + dependency: "direct main" description: name: path_provider sha256: "3087813781ab814e4157b172f1a11c46be20179fcc9bea043e0fba36bc0acaa2" diff --git a/pubspec.yaml b/pubspec.yaml index 99fb104..dba6dce 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -74,6 +74,7 @@ dependencies: sqflite_common_ffi_web: ^0.3.6 http_parser: ^4.0.2 share_plus: ^7.0.2 + path_provider: ^2.0.15 conditional_dependencies: sqflite: