상세 페이지 만들어보자
상세 페이지 만들어보자
이젠 상세페이지 차례



상세 페이지 루트 잡기
이제 사용자가 볼 상세 페이지가 필요하다. 주로 목록 -> 상세 흐름으로 앱은 이동하기 때문에 memory_detail.dart
페이지와 라우트를 추가해줬다.
import 'package:go_router/go_router.dart'; import '/screens/memory_detail.dart'; import '/screens/screen2.dart'; import '/screens/screen3.dart'; import 'main_scaffold.dart'; import 'screens/memories.dart'; final goRouter = GoRouter( initialLocation: '/memories', routes: [ ShellRoute( builder: (context, state, child) => MainScaffold(child: child), routes: [ GoRoute( path: '/memories', pageBuilder: (context, state) => const NoTransitionPage(child: MemoriesScreen()), ), GoRoute( path: '/screen2', pageBuilder: (context, state) => const NoTransitionPage(child: Screen2()), ), GoRoute( path: '/screen3', pageBuilder: (context, state) => const NoTransitionPage(child: Screen3()), ), ], ), GoRoute( path: '/memories/:id', builder: (context, state) { final id = state.pathParameters['id']!; return MemoryDetailScreen(id: id); }, ), ], );
/memories/:id
로 별도의 라우트를 만들고 위 Shell Route에 넣지 않은 이유는, MainScaffold
에 속하는 레이아웃의 화면이 아니기 때문이다.
다이나믹 루트로 id를 넘기면, 거기에 맞는 상세내역을 가져와 뿌릴거라 pathParameter
를 사용했다. 그러면 화면에서 해당 id 정보를 받아 이용할 수 있다.
class MemoryDetailScreen extends StatelessWidget { final String id; const MemoryDetailScreen({super.key, required this.id});
화면 레이아웃 잡기
아직 객체는 적용하지 않고, 대강 필요한 정보를 뿌릴 형태의 레이아웃을 잡아봤다.
보이고 싶은건
별세한 사람에 대한 짤막한 (별세일, 추모지) 정보
사진/영상을 갤러리 형태로 보이는 것
메모 형태의 텍스트 데이터 또한 미리보기로 갤러리 형태로 보이는 것
이정도로 잡았다.
import 'package:flutter/material.dart'; class MemoryDetailScreen extends StatelessWidget { final String id; const MemoryDetailScreen({super.key, required this.id}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Memories')), body: SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Card( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), elevation: 2, child: Padding( padding: const EdgeInsets.all(16), child: Row( children: [ const CircleAvatar( radius: 30, backgroundColor: Colors.grey, // backgroundImage: AssetImage('assets/profile.jpg'), ), const SizedBox(width: 16), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Dad', style: Theme.of( context, ).textTheme.titleLarge?.apply(fontWeightDelta: 2), ), SizedBox(height: 4), Text( 'Date of Passing: 02/09/2025', style: TextStyle(color: Colors.grey), ), Text( 'Memorial Site: Hambaeksan Memorial Park 9-5-182', style: TextStyle(color: Colors.grey), ), ], ), ), const Icon(Icons.arrow_forward_ios, size: 16), ], ), ), ), const SizedBox(height: 16), Text( 'Photos / Videos', style: Theme.of(context).textTheme.titleMedium, ), const SizedBox(height: 12), GridView.builder( itemCount: 9, shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 3, crossAxisSpacing: 8, mainAxisSpacing: 8, childAspectRatio: 1, ), itemBuilder: (context, i) { return Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(8), color: Colors.grey[300], // image: DecorationImage( // image: NetworkImage('https://example.com/image_$i.jpg'), // fit: BoxFit.cover, // ), ), ); }, ), Text('Texts', style: Theme.of(context).textTheme.titleMedium), const SizedBox(height: 12), GridView.builder( itemCount: 9, shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 3, crossAxisSpacing: 8, mainAxisSpacing: 8, childAspectRatio: 1, ), itemBuilder: (context, i) { return Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(8), color: Colors.grey[300], // image: DecorationImage( // image: NetworkImage('https://example.com/image_$i.jpg'), // fit: BoxFit.cover, // ), ), ); }, ), const SizedBox(height: 80), ], ), ), ); } }
여기서 하나 공유하고 싶은 부분은, 가급적 텍스트의 크기가 굵기를 별도로 매번 설정하지 않고, Theme.of(context).textTheme
클래스에서 가져와 적용하는 것을 추천한다. 유지보수나 디자인 일관성 때문이다.
또 새로운 형태의 데이터를 언제든지 추가할 수 있도록 Floating Action Button까지 추가해줬다.
floatingActionButton: FloatingActionButton( child: const Icon(Icons.add), onPressed: () {}, ),
이정도로 하면 상세 페이지가 어느정도 나온다.

조금씩 진전이 보인다.
Comments
Comments
Comments