화면결과

폴더구조
Riverpod(상태관리)로 전역적으로 상태를 선언하고
read해야되는곳과 watch해야되는곳을 부분적으로 상태관리

코드
looking_screen.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:fronttodayhome/components/image_container.dart';
import 'package:fronttodayhome/models/Feed.dart';
import 'package:fronttodayhome/screens/looking/components/looking_body.dart';
import 'package:fronttodayhome/screens/looking/looking_vm.dart';
class LookingScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final model = ref.watch(LookingScreenProvider);
if (model == null || model.list == null) {
return Center(child: CircularProgressIndicator()); // 로딩 중일 때 표시
}
return Scaffold(
backgroundColor: Colors.grey[100],
appBar: AppBar(
title: Text("둘러보기"),
actions: [
IconButton(onPressed: () {}, icon: Icon(CupertinoIcons.search)),
IconButton(
onPressed: () {},
icon: Icon(CupertinoIcons.plus_rectangle_on_rectangle)),
],
bottom: PreferredSize(
preferredSize: Size.fromHeight(1.0),
child: Container(
color: Colors.grey,
height: 1.0,
),
)),
body: ListView(
children: [
look_header(),
...List.generate(
model.list!.length,
(index) => Padding(
padding: const EdgeInsets.only(bottom: 16.0),
child: LookingBody(feed: model.list![index]),
),
),
],
),
);
}
}
class look_header extends StatelessWidget {
const look_header({
super.key,
});
@override
Widget build(BuildContext context) {
return Card(
margin: EdgeInsets.only(bottom: 12.0),
elevation: 0.5,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0.0)),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
children: [
ImageContainer(
borderRadius: 6.0,
imageUrl: 'https://picsum.photos/id/780/200/100',
width: 45.0,
height: 45.0,
),
const SizedBox(width: 16.0),
Expanded(
child: Text(
lifeTitle,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
)
],
),
),
);
}
}
looking_body.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:fronttodayhome/components/image_container.dart';
import 'package:fronttodayhome/screens/looking/looking_vm.dart';
import 'package:fronttodayhome/theme.dart';
class LookingBody extends StatelessWidget {
final Looking feed; // Feed 대신 Looking으로 변경
const LookingBody({Key? key, required this.feed}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: Colors.white,
border: Border(
bottom: BorderSide(width: 1, color: Color(0xFFD4D5DD)),
),
),
child: Column(
children: [
_buildTop(),
_buildWriter(),
_buildWriting(),
_buildImage(),
Divider(
height: 1,
thickness: 1,
color: Colors.grey[300],
),
_buildTail(),
],
),
);
}
// 기존의 _buildTop 위젯을 다시 통합
Widget _buildTop() {
return Padding(
padding: const EdgeInsets.symmetric(
vertical: 16,
horizontal: 16,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
padding: EdgeInsets.all(4),
decoration: BoxDecoration(
shape: BoxShape.rectangle,
borderRadius: BorderRadius.all(Radius.circular(4)),
color: Color.fromRGBO(247, 247, 247, 1)),
child: Text(
feed.category,
style: TextTheme().bodyMedium,
),
),
Text(
feed.date,
style: textTheme().bodyMedium, //작동안함
//스타일해야될듯
),
],
),
);
}
// 기존의 _buildWriter 위젯을 다시 통합
Widget _buildWriter() {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
children: [
ImageContainer(
width: 30,
height: 30,
borderRadius: 15,
imageUrl: feed.profileImgUri,
),
const SizedBox(width: 8.0),
Text.rich(
TextSpan(children: [
TextSpan(
text: '${feed.userName}',
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.bold,
color: Colors.black87,
),
),
]),
),
],
),
);
}
// 기존의 _buildWriting 위젯을 다시 통합
Widget _buildWriting() {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
child: Align(
alignment: Alignment.centerLeft,
child: Text(
feed.content,
style: textTheme().bodyLarge,
maxLines: 3,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.start,
),
),
);
}
// 기존의 _buildImage 위젯을 다시 통합
Widget _buildImage() {
return Visibility(
visible: feed.contentImgUris.isNotEmpty,
child: Padding(
padding: EdgeInsets.only(left: 16, right: 16, bottom: 16),
child: Image.network(
feed.contentImgUris.isNotEmpty ? feed.contentImgUris[0].url : '',
height: 200,
width: double.infinity,
fit: BoxFit.cover,
),
),
);
}
// 기존의 _buildTail 위젯을 다시 통합
Widget _buildTail() {
if (feed.contentImgUris.isEmpty) {
return SizedBox.shrink(); // 아무것도 렌더링하지 않음
}
return Padding(
padding: const EdgeInsets.all(16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
for (var imageUrl in feed.contentImgUris.take(3)) // 최대 3개의 이미지만 표시
_buildImageItem(imageUrl.url),
],
),
Row(
children:[
Text(
'상품 더보기',
style: textTheme().displayMedium,
),
const SizedBox(width: 8.0),
Icon(
CupertinoIcons.right_chevron,
size: 18.0,
color: Colors.black87,
),
]
),
],
),
);
}
}
Widget _buildImageItem(String imageUrl) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 4.0),
child: ClipRRect(
borderRadius: BorderRadius.circular(8.0), // 이미지에 border radius 적용
child: Image.network(
imageUrl,
width: 50, // 이미지 너비
height: 50, // 이미지 높이
fit: BoxFit.cover, // 이미지 잘 맞게 표시
),
),
);
}
looking_vm.dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:fronttodayhome/data/repository/feed_repository.dart';
import 'package:logger/logger.dart';
class LookingScreenVm extends StateNotifier<LookingScreenModel?> {
LookingScreenVm(super.state);
Future<void> notifyInit() async {
// 1. 통신을 해서 응답 받기
List<dynamic> list = await FeedRepository().findAll();
Logger().d("이건 응답받은 데이터 $list");
List<Looking>? feeds = list.map((e) => Looking.fromMap(e)).toList();
Logger().d(feeds);
// 2. 상태 갱신
state = LookingScreenModel (list: feeds);
}
void updateIndex(int newIndex) { // 다시해야함
state = LookingScreenModel( list: state!.list);
}
}
class LookingScreenModel {
List<Looking>? list;
LookingScreenModel({this.list});
}
// Feed 창고 데이터
class Looking {
final String category;
final String profileImgUri;
final String userName;
final int postId;
final String content;
final String date;
final List<ContentImgUris> contentImgUris; // 대문자로 수정
Looking({
required this.category,
required this.profileImgUri,
required this.userName,
required this.postId,
required this.content,
required this.date,
this.contentImgUris = const [],
});
// JSON 데이터를 Feed 객체로 변환하는 메서드
factory Looking.fromMap(Map<String, dynamic> map) {
return Looking(
category: map['category'],
content: map['content'],
postId: map['postId'],
date: map['date'],
userName: map['userName'],
profileImgUri: map['profileImgUrl'],
// userFeedPhotos를 ContentImgUris 객체로 변환
contentImgUris: (map['userFeedPhotos'] as List).map((photo) {
return ContentImgUris(
id: photo['id'],
type: photo['type'],
url: photo['url'],
);
}).toList(),
);
}
}
class ContentImgUris{
final int id;
final String type;
final String url;
ContentImgUris({required this.id, required this.type, required this.url});
}
final LookingScreenProvider =
StateNotifierProvider<LookingScreenVm, LookingScreenModel?>((ref) {
return LookingScreenVm(null)..notifyInit();
});
Share article