일단은 지금 제작중인 소셜차트라는 앱이 소셜네트워킹 앱이어서 사용자들이 글을 쓸 수 있도록 해야 해. 페이스북이나 트위터처럼 말이지.
이거 말고도 구현해야 할 것들이 굉장히 많지만 글을 쓸 수 있는 환경부터 만드는 게 더미 데이터를 넣으면서 테스트하기 좋을 거 같아서 글쓰는 페이지부터 만들고 있어.
1차적으로 해쉬태그와 url을 자동으로 인식해서 link preview를 보여주는 정도로만 구현중이야.
먼저 textfield에 hashtag와 url을 꾸며주는 건 detactable_text_field라는 flutter package를 사용했고👇
https://pub.dev/packages/detectable_text_field
link preview와 metatag 빼내는 건 any_link_preview라는 flutter package를 사용했어.
https://pub.dev/packages/any_link_preview
View는 아래 정도로 간단히 구현 가능하고
body: SingleChildScrollView(
child: Container(
padding: const EdgeInsets.only(
left: 10,
right: 10,
bottom: 50,
),
child: Column(
children: [
DetectableTextField(
keyboardType: TextInputType.multiline,
detectionRegExp: hashTagUrlRegExp,
decoration: const InputDecoration(
hintText: "자신의 인사이트를 공유해보세요!",
border: InputBorder.none,
),
autofocus: true,
maxLines: null,
minLines: null,
autocorrect: false,
controller: controller.textController,
onChanged: (value) {
controller.userText = value;
},
),
Obx(
() => controller.userLink != null
? Stack(
children: [
AnyLinkPreview(
displayDirection: UIDirection.uiDirectionHorizontal,
link: controller.userLink!,
bodyMaxLines: 3,
placeholderWidget: const Center(child: Text("로딩중")),
errorImage: "Error loding image",
),
Positioned(
top: 0,
right: 0,
child: CupertinoButton(
minSize: 10,
padding: EdgeInsets.all(0),
onPressed: () => controller.userLink = null,
child: Icon(Icons.cancel, color: Colors.black45),
),
)
],
)
: const SizedBox(),
),
],
),
),
),
Controller에서는 아래와 같이 구현할 수 있어.
class ScreenWriteController extends GetxController
with GetSingleTickerProviderStateMixin {
static final _urlRegExp = RegExp(
"(?!\\n)(?:^|\\s)$urlRegexContent" + "( |\n)",
multiLine: true,
);
var key = GlobalKey<FormState>();
var textController = TextEditingController();
final _userText = Rx<String>("");
final _previewLink = Rxn<String>();
final _linkData = Rxn<Metadata>();
final _deletedLink = Rx<List<String>>([]);
String get userText => _userText.value;
set userText(String value) => _userText.value = value;
String? get previewLink => _previewLink.value;
set previewLink(String? value) => _previewLink.value = value;
set deletedLink(String value) => _deletedLink.value.add(value);
@override
void onInit() {
// TODO: implement onInit
super.onInit();
ever(
_userText,
(userText) async {
Iterable<String> extractedLink =
extractDetections(userText, _urlRegExp).isEmpty
? []
: extractDetections(userText, _urlRegExp)
.where((element) => !_deletedLink.value.contains(element));
if (extractedLink.isEmpty) return;
AnyLinkPreview.getMetadata(link: extractedLink.first).then((value) {
if (value != null) {
_linkData.value = value;
previewLink = extractedLink.first;
} else {
//do nothing!
}
});
},
);
}
@override
void onReady() {
// TODO: implement onReady
super.onReady();
//hide main navigation bar
Get.find<MainNavigatorController>().isBottomTabVisible = false;
}
@override
void onClose() {
// TODO: implement onClose
super.onClose();
//show the main navigation bar again
Get.find<MainNavigatorController>().isBottomTabVisible = true;
}
}
이제 내용들을 저장해볼건데, 작성자, 작성일 이런 기본적인 것들을 포함해서 link preview 데이터 정도만 추가해주면 될 것 같아. 나머지는 차차 추가해보자. 난 유저가 작성한 글을 insightCard라고 부를거야.
난 firestore를 사용하고 있어. 그냥 단숨히 document에 string, int... 등을 쓰는 건 잘 되는데.. 또 다른 Map을 넣는 건 어떻게 하는지 모르겠네? ㅋㅋ 뭐 별거 있겠어? 그냥 해보는 거지 ㅋㅋㅋ 일단 model 고고 👇👇
class InsightCardModel {
InsightCardModel({
required this.createdAt,
required this.chartId,
required this.cardType,
required this.author,
required this.userText,
this.linkPreviewData,
this.replyCount = 0,
this.pinCount = 0,
});
final Timestamp createdAt; //creation time
final String chartId;
final String cardType;
final DocumentReference author; //userReference
final String userText;
final LinkPreviewData? linkPreviewData;
final int replyCount;
final int pinCount;
InsightCardModel.fromJson(Map<String, Object?> json)
: this(
createdAt: json['createdAt']! as Timestamp,
chartId: json['chartId']! as String,
cardType: json['cardType'] as String,
author: json['author'] as DocumentReference,
userText: json['userText'] as String,
linkPreviewData: json['linkPreviewData'] as LinkPreviewData?,
replyCount: json['replyCount'] as int,
pinCount: json['pinCount'] as int,
);
Map<String, Object?> toJson() {
return {
'createdAt': createdAt,
'chartId': chartId,
'cardType': cardType,
'author': author,
'userText': userText,
'linkPreviewData': linkPreviewData,
'replyCount': replyCount,
'pinCount': pinCount,
};
}
}
class LinkPreviewData {
LinkPreviewData({
required this.url,
this.image,
this.title,
this.description,
});
final String url;
final String? image;
final String? title;
final String? description;
}
이렇게 모델을 작성하고 firestore에 collection에 add로 문서 업로드를 시도해보니 ㅋㅋ 역시나 에러... Invalid ardument ㅎㅎ 뭐 당연한가? 못알아 먹던 json을 dynamic 속에 넣었다고 알아먹을리가 없으니까 ㅎㅎ
중첩되어 사용될 클래스에도 fromJson과 toJson을 추가해주고 그걸 메인 모델 클래스에서 사용하도록 하자구.
class InsightCardModel {
InsightCardModel({
required this.createdAt,
required this.chartId,
required this.cardType,
required this.author,
required this.userText,
this.linkPreviewData,
this.replyCount = 0,
this.pinCount = 0,
});
final Timestamp createdAt; //creation time
final String chartId;
final String cardType;
final DocumentReference author; //userReference
final String userText;
final LinkPreviewData? linkPreviewData;
final int replyCount;
final int pinCount;
InsightCardModel.fromJson(Map<String, dynamic?> json)
: this(
createdAt: json['createdAt']! as Timestamp,
chartId: json['chartId']! as String,
cardType: json['cardType'] as String,
author: json['author'] as DocumentReference,
userText: json['userText'] as String,
//여기!!!!!!
linkPreviewData: LinkPreviewData?.fromJson(json['linkPreviewData']),
replyCount: json['replyCount'] as int,
pinCount: json['pinCount'] as int,
);
Map<String, dynamic?> toJson() {
return {
'createdAt': createdAt,
'chartId': chartId,
'cardType': cardType,
'author': author,
'userText': userText,
//여기!!!!!!
'linkPreviewData': linkPreviewData?.toJson(),
'replyCount': replyCount,
'pinCount': pinCount,
};
}
}
class LinkPreviewData {
LinkPreviewData({
this.url,
this.image,
this.title,
this.description,
});
final String? url;
final String? image;
final String? title;
final String? description;
//추가!!!!!!
LinkPreviewData.fromJson(Map<String, dynamic> json)
: this(
description: json['description'],
image: json['image'],
title: json['title'],
url: json['url']);
//추가!!!!!!
Map<String, dynamic> toJson() => {
"url": url,
"image": image,
"title": title,
"description": description,
};
}
그리고 나서 테스트를 해보면 👇
이제 더미 데이터를 몇십개 만들고 listview로 만들어 볼 예정. ㅎㅎ 더디긴 해도 진도가 나가긴 하네 ㅎㅎㅎ 셀프 화이팅이다 ㅋㅋㅋㅋㅋㅋ 안녕!
'Flutter > Flutter Project' 카테고리의 다른 글
[소셜차트] 앱 제작기 #10. Tap to scroll top (0) | 2022.11.08 |
---|---|
[소셜차트] 앱 제작기 #9. 무한 스크롤 (0) | 2022.11.07 |
[소셜차트] 앱 제작기 #6. Profile & Notice Screen Layout (1) | 2022.10.26 |
[소셜차트] 앱 제작기 #5. Model and Firestore 1% (0) | 2022.10.24 |
[소셜차트] 앱 제작기 #4. 로그인 시스템 구성 (0) | 2022.10.22 |