말랑한 하루
[Flutter] (Project) MapleApp: 14. Equipment Detail Page 제작 본문
이제 아이템을 누르면 팝업으로 아이템에 대한 상세 정보를 보여주는 페이지를 제작하면서, 겪었던 일들에 대해 얘기해보려 한다.
🐇 static config
일단 색상을 사용하는 것이 많기 때문에, 기존 static config파일을 좀 더 세분화 했다. 기존의 static config는 TabList와, InfoList 들이 담겨있었기 때문에 static list config로 명명하고, 주로 색상을 선택하여 반환하는 static switch config를 생성했다.
🐇 router
팝업을 띄우기 위해선 GoRouter의 push를 사용하면 팝업을 띄울 수 있다. 팝업은 페이지 내 상세 정보이기 때문에, Router에서 관리할 때 중첩 라우트를 사용해 가독성을 높이고자 했다.
🥕 nested router
충접 라우터 구조는 단순하다. 이전 버전까지는 children 속성을 활용했으나, GoRouter 버전이 업그레이드 되면서 routers 속성으로 변경되었다. 상세 구현은 다음 create와 같다.
🍒 create
GoRoute(
path: '/Page',
name: 'Page',
pageBuilder: (context, state)
=> const NoTransitionPage(child: Page()),
routes: [
GoRoute(
path: 'Child',
name: 'Child',
pageBuilder: (context, state) => NoTransitionPage(child: Child()),
),
],
),
🥕 router with data
하지만, 상세 팝업을 띄우면서 상세 정보 데이터를 함께 보내야 했기 때문에 GoRouter에서 데이터를 실어 보내는 것을 학습해야 했다. 방법은 다음과 같다.
🍒 params
GoRouter이전 버전에서는 params 속성을 사용했다. 하지만, 버전이 업그레이드 되면서 extra 속성으로 전속 변경되었기 때문에 extra 속성을 알아보도록 하자.
🍒 extra
※ reference : https://docs.page/csells/go_router/parameters
기존 params를 사용할 땐 Object 형태만 사용할 수 있었다. 하지만 extra는 더 넓은 범위를 허용할 수 있게 되었다. 내가 원하는 객체/클래스를 go/push의 extra에 실어 놓으면, GoRoute의 builder가 진행될 때 state에 담긴 extra를 가져와 사용할 수 있다.
이때 extra가 문자열이 아니라면, Type 명시(as Type)을 꼭 진행해주어야 한다. 사용 방법에 대한 예시는 다음과 같다.
🍇 useage
context.push('/Page/Child', extra: data);
GoRoute(
path: 'Child',
name: 'Child',
pageBuilder: (context, state) => NoTransitionPage(
child: Child(item: state.extra as Data),
),
),
⚠️ GoRouterState state, Type: GoRouterState, Invalid constant value.
만약 GoRouter에서 extra를 사용할 때 다음 오류가 생긴다면, builder 반환을 const Widget으로 하기 때문에 생기는 오류로 Widget의 const 선언을 제거해주면 말끔히 해결된다.
이후 상세 화면을 어떻게 구상해야 할까 고민했다. 상세 화면의 정보는 짧을 수도, 길 수도 있다. 하지만 전체적인 내용이 대게 긴 요소를 가지기 있기 때문에 ScrollView를 사용해보려 했다.
🐇 SingleChildScrollView
※ reference : https://api.flutter.dev/flutter/widgets/SingleChildScrollView-class.html
SingleChildScrollView는 단일 위젯을 스크롤 할 수 있는 Widget입니다. 상세 페이지를 구현할 때, 길이가 늘어날 수 있는 페이지에 대해서 컨트롤하기 최적이라고 생각했습니다.
🥕 Create
SingleChildScrollView는 LayoutBuilder와 ConstraintBox, BoxConstraints Class를 활용해서 만드는 것이 기본이다.
하지만 우리는 보통 Column Widget을 활용해서 요소를 쌓아나가려 합니다. 그때 SingleChildScrollView는 자식에게 무한한 공간을 제공하기 위한 노력을 하고, 일반적으로 가능한 한 크게 커지려고 하는 Column과 충돌하게 됩니다. 이 충돌을 막기 위해, 아래 기술된 내용에 대해 허용 가능한 일반적인 경우에만 사용하는 것이 좋습니다. 그런 경우엔 ListView 또는 CustomScrollView를 사용해보세요
LayoutBuilder는 Viewport의 크기를 얻는 데 사용됩니다. SingleChildScrollView가 보는 제약 조건을 통해 암시적으로, Viewport가 일반적으로 최대 높이 제약 조건에 맞게 커지므로 스크롤 보기 내에서 ConstraintedBox를 사용하여 Column의 최소 높이를 설정해줍니다.
Column에는 Expanded 자식이 없을 때, BoxConstraints.maxHeight에서 무한한 높이를 취하는 대신, 자식에 맞게 자동으로 축소하려고 시도합니다. 하지만 minHeight 속성 보다 작을 순 없으므로 ConstraintedBox에서 제공하는 최소 높이와 자식 높이의 합보다 커집니다.
하위 항목의 최소 크기에 맞지 않을 경우 Column은 mainAxisAlignment 인수에 지정된 대로 할당할 남을 공간을 가지게 됩니다.
위 이야기처럼 하위 Column에 대한 최소 크기를 제공하는 것 이외에도 IntrinsicHeight Widget을 사용하여 열이 내용과 정확히 일치하도록 강제합니다. 이 위젯은 ConstraintedBox 제약 조건과 결합하여 열이 Viewport만큼 커지거나 콘텐츠만큼 커지는 것 중 가장 큰 것이 되도록 보장합니다.
IntrinsicHeight만 지정된 경우 하위 항목이 전체 화면보다 작을 때 열이 전체 뷰포트에 맞게 커지지 않습니다. 뷰포트의 크기만 사용된 경우 하위 항목이 뷰포트보다 크면 열이 오버플로됩니다 .
이제 제가 사용했던 SingleChildScrollView의 구조에 대해서 말해드리겠습니다.
🥕 Implement
Column(
children: [
Container(),
Expanded(
child: LayoutBuilder(
builder:
(BuildContext context, BoxConstraints viewportConstraints) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: viewportConstraints.maxHeight,
),
child: TodoMain(),
),
);
},
),
),
],
),
위 모습과 같이 Column으로 내가 원하는 항목을 쌓을 때, 나머지 부분을 Expanded로 감싸줍니다. 이후, Container가 차지하고 남은 공간을 최대로 확장하여 height크기를 지니고, 그 자식으로 LayoutBuilder를 활용하여 viewport의 크기를 가져옵니다. 가져온 viewport의 높이는 BoxConstraints 속성의 최소크기로 지정되고, 최대 크기는 List내 요소 크기만큼 최대로 확장되도록 지정하지 않습니다.
이 뒤로는 상세 내용을 구현하면서 알게 된 것들에 대해 정리해가려 한다. 대부분 오류에 관한 이야기며 제가 겪었던 경험을 다른 사람은 손쉽게 해결할 수 있도록 작성해가겠다.
'개발 > Flutter' 카테고리의 다른 글
[Flutter] (Project) MapleApp: 16. Equipment Detail Page 제작-3 (0) | 2024.01.11 |
---|---|
[Flutter] (Project) MapleApp: 15. Equipment Detail Page 제작-2 (0) | 2024.01.10 |
[Flutter] (Proejct) MapleApp: 13. Equipment Page 제작 (0) | 2024.01.08 |
[Flutter] (Proejct) MapleApp: 12. Singleton Pattern 적용 (0) | 2024.01.07 |
[Flutter] (Project) MapleApp: 11. Character Page 제작 (0) | 2024.01.07 |