프로젝트 하면서 새롭게 배운거나 알고는 있었지만 제대로는 알지 못했던거 정리
처음에는 공간에 높이가 없다가
이렇게 내용이 있을때 높이를 따로 줘야하는데 이거를 구현할때 처음해보는거라
고민을 많이했다.
Container(
width: 340.w,
height: addDiseaseType.isNotEmpty ? 36.h : 0,
margin: const EdgeInsets.fromLTRB(12, 0, 12, 0),
child: addDiseaseType.isNotEmpty
? ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: addDiseaseType.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.fromLTRB(0, 0, 8, 0),
child: Chip(
labelStyle: Black(12.sp, FontWeight.w500),
side: const BorderSide(color: Colors.transparent),
backgroundColor: Colors.white,
label: Text(addDiseaseType[index]),
onDeleted: () {
_removeDisease(index);
},
deleteIcon:
Image.asset('assets/image_asset/edit/close.png'),
),
);
},
)
: const SizedBox.shrink(),
),
그러던 중 SizedBox.shrink()를 알게 됐는데 이것은 빈 위젯이다
처음에는 빈 위젯을 보여주고 나중에 높이를 36이 나오도록 했다.
class CircleContainer extends StatelessWidget {
final double width;
final double height;
const CircleContainer(
{super.key, this.width = double.infinity, this.height = double.infinity});
@override
Widget build(BuildContext context) {
return GlassContainer(
width: width,
height: height,
color: Colors.white.withOpacity(0.8),
gradient: LinearGradient(
colors: [
Colors.white.withOpacity(0.8),
Colors.white,
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderGradient: LinearGradient(
colors: [
Colors.white.withOpacity(0.5),
Colors.white.withOpacity(0.2),
Colors.white.withOpacity(0.2),
Colors.white.withOpacity(0.2),
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
stops: [0.10, 0.30, 0.40, 0.0],
),
blur: 20,
borderRadius: BorderRadius.circular(100),
borderWidth: 1.0,
elevation: 10.0,
boxShadow: [
BoxShadow(
color: Color(0x26000000),
blurRadius: 4,
offset: Offset(0, 4),
spreadRadius: 0,
)
],
);
}
}
final double width ,final double height
이런식으로 해주면 높이와 넓이만 바꿀 수 있는 공통 위젯으로 만들 수 있다.
class CustomTabbar extends StatefulWidget {
final TabController tabController;
final List<Widget> tabView;
final String? bannerImage;
const CustomTabbar({
super.key,
required this.tabController,
required this.tabView,
this.bannerImage,
});
@override
State<CustomTabbar> createState() => _CustomTabbarState();
}
class _CustomTabbarState extends State<CustomTabbar> {
@override
Widget build(BuildContext context) {
return Column(
children: [
TabBar(
dividerHeight: 0,
unselectedLabelStyle: whiteOpacity(14.sp, FontWeight.w600),
indicatorColor: Colors.white,
indicatorSize: TabBarIndicatorSize.values.first,
indicatorPadding: EdgeInsets.symmetric(horizontal: 8),
indicatorWeight: 2,
labelStyle: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Colors.white,
fontFamily: 'Pretendard'),
labelColor: Colors.white,
controller: widget.tabController,
tabs: [
Container(
height: 48.h,
alignment: Alignment.center,
child: Text(
'정보',
),
),
Container(
height: 48.h,
alignment: Alignment.center,
child: Text(
'거래',
),
),
Container(
height: 48.h,
alignment: Alignment.center,
child: Text(
'모임',
),
),
],
),
if (widget.bannerImage != null)
Image.asset(
widget.bannerImage!,
),
Expanded(
child: TabBarView(
controller: widget.tabController, children: widget.tabView))
],
);
}
}
Future<List<Map<String, dynamic>>> getMemos() async {
try {
QuerySnapshot snapshot = await firestore
.collection('Memo')
.orderBy('timestamp', descending: true)
.get();
List<Map<String, dynamic>> memos = snapshot.docs.map((doc) {
Map<String, dynamic> data = doc.data() as Map<String, dynamic>;
print('Fetched memo: $data');
return {
'id': doc.id,
'content': data['content'],
'timestamp': (data['timestamp'] as Timestamp).toDate(),
};
}).toList();
print('Fetched memos: $memos');
return memos;
} catch (e) {
// print('Error fetching memos: $e');
return [];
}
}
데이터 형식을 불러와서 데이터 내용과 타임스탬프를 가져와서
데이터의 내용이 작성한 순서대로 업데이트 되도록 orderBy를 사용했다.
Future<void> deletedMemo(String docId) async {
if (docId.isEmpty) {
Get.snackbar('삭제 실패', '문서 ID가 비어 있습니다.');
return;
}
try {
await firestore.collection('Memo').doc(docId).delete();
Get.back();
Get.snackbar('삭제 성공', '메모가 삭제되었습니다.');
} catch (e) {
print('Error deleting memo: $e');
Get.snackbar('삭제 실패', '메모를 삭제하는 중 오류가 발생했습니다.');
}
}
파이어스토어에 문서를 삭제하려면 고유id값이 필요한데 자동으로 생기는 고유id값 이것을 입력해주면 삭제가 된다.
하지만 어플을 사용하면서 이것을 사용자가 할수 없으니 방법을 찾아야한다.
File? _image; //선택한 이미지 파일 저장
final ImagePicker picker = ImagePicker();//이미지 선택 처리
String _PetProfileImag =
'assets/image_asset/pet_upload/animal_profile (2).png'; //프로필의 기본이미지
Future<void> _pickImage(ImageSource source) async {
final pickedFile = await picker.pickImage(source: source);
if (pickedFile != null) {
setState(() {
_image = File(pickedFile.path);
});
//스토리지에 먼저 사진 업로드 하는 부분
final storageRef = FirebaseStorage.instance
.ref() //시작점
.child('pet_images/${DateTime.now().toIso8601String()}');
final uploadTask = storageRef.putFile(_image!);
uploadTask.whenComplete(() async {
try {
final imageUrl = await storageRef.getDownloadURL();
// 이미지 저장!
await FirebaseFirestore.instance.collection('pet_profiles').add({
'image_url': imageUrl,
});
widget.onChanged(imageUrl); // 새로운 URL로 호출
} catch (e) {
print('Error uploading image: $e');
}
});
}
}
_pickImage는 지정된 카메라나 갤러이에서 이미지를 선택해준다.
final storageRef = FirebaseStorage.instance
.ref() //시작점
.child('pet_images/${DateTime.now().toIso8601String()}');
final uploadTask = storageRef.putFile(_image!);
이미지 URL을 파이어베이스 파이어스토어에 저장을 해준다.
GestureDetector(
onTap: () {
setState(() {
_PetProfileImag =
'assets/image_asset/pet_upload/animal_profile (4).png';
widget.onChanged(_PetProfileImag);
});
},
child: ProfileCard(
petName: '물고기',
imgUrl:
'assets/image_asset/pet_upload/animal_select (6).png',
),
),
새 이미지경로로 _PetProfileImag 호출되게 함