[Flutter] List, ListView, ListView.Builder 차이
2023. 3. 11. 19:38ㆍDevelopers 공간 [Shorts]/Frontend
728x90
반응형
<분류>
A. 수단
- OS/Platform/Tool : Linux, Kubernetes(k8s), Docker, AWS
- Package Manager : node.js, yarn, brew,
- Compiler/Transpillar : React, Nvcc, gcc/g++, Babel, Flutter
- Module Bundler : React, Webpack, Parcel
B. 언어
- C/C++, python, Javacsript, Typescript, Go-Lang, CUDA, Dart, HTML/CSS
C. 라이브러리 및 프레임워크 및 SDK
- OpenCV, OpenCL, FastAPI, PyTorch, Tensorflow, Nsight
1. What? (현상)
List와 관련된 다양한 언어가 있어. 정확히 구분해 설명해놓으려고 합니다.
+ 그리고 추가로 ListView와 같은 "리스트 렌더링" 안에 "리스트 렌더링"을 추가로 넣는 경우 아래와 같은 에러가 나는데, 왜그런지와 어떻게 해결할 수 있을지 살펴봅니다.
"RenderBox was not laid out: RenderViewport#123ab NEEDS-LAYOUT NEEDS-PAINT"
2. Why? (원인)
- + 추가 에러의 경우 리스트 안의 리스트의 크기가 무한하기 때문에 일어나는 에러라고 볼 수 있습니다. 즉, 밖의 사이즈는 안의 리스트를 기준으로 만들어야하는데, 밖에서는 안의 개수를 알 수 없고 안의 리스트는 밖의 사이즈를 알수 없기 때문에 나는 에러입니다.
3. How? (해결책)
- List<>() : 자료구조로, 단순히 리스트를 의미합니다. 아직 위젯이 아니므로, 아래와 같이 위젯리스트로 만들고 직접 렌더링 해주어야합니다.
- ** Tuple List : 'package:tuple/tuple.dart' 패키지를 활용하면 아래와 같이 구현 가능합니다.
List<Tuple2<String,Widget>> tabLists = [Tuple2('tab1', Template()), Tuple2('tab2', Template2())];
--> tabLists[i].item1
- ** Tuple List : 'package:tuple/tuple.dart' 패키지를 활용하면 아래와 같이 구현 가능합니다.
// Decaration1
List<Widget> widgets = [
Text('Toyota', style: kStyle,),
Text('VolksWagen', style: kStyle,),
Text('Nissan', style: kStyle,),
Text('Renault', style: kStyle,),
Text('Mercedes', style: kStyle,),
Text('BMW', style: kStyle,)
];
// Decaration2
List<Widget> _buildUserGroups(BuildContext context) {
var userGroup = List<Widget>();
userGroup.add(Text("Users"));
for (var i = 0; i < 5; i++) {
userGroup.add(Text("User " + i.toString()));
}
return userGroup;
}
// Widget Rendering
Column(
children: _buildUserGroups(context).map((Widget item)=> item),
)
- ListView() : ListView는 위젯으로, Rendering하는데 사용합니다. 가장 기본적인 Default Constructor로, 직접 children:[] 에 Widget들을 넣어 scroll 가능한 위젯을 만들어줍니다.
- + 추가 에러의 경우 ListView>ListView 혹은 Column >ListView 대신 아래와 같은 옵션을 사용할 수 있습니다.
- Column > ListView(shrinkWrap:true)
- Column > [Expanded/Flexible]
- Column > [SizedBox]
- CustomScrollView>SliverList
- + 추가 에러의 경우 아래 파라미터에 대한 간단한 소개를 알면 조금 더 이해할 수 있습니다.(아래 더보기 클릭)
- + 추가 에러의 경우 ListView>ListView 혹은 Column >ListView 대신 아래와 같은 옵션을 사용할 수 있습니다.
더보기
-----------------------------------------------------------------------------------------------------
<ListView 파라미터 간단히 소개>
- scrollDirection : Axis.vertical 혹은 Axis.horizontal을 사용가능합니다.
- children : 직접 만들어 넣어주어도되지만, ListTile()을 사용하면 width를 정해주지않아도 Expanded()처럼 알아서 화면을 최대한 활용하기 때문에 좋습니다.(vertical 일 때)
** horizontal 의 경우 정해진 width로 감싸주는 것이 좋습니다. - reverse : 역순도 가능합니다.
- controller : 보통은 member 변수로 있지만, 따로 scrollController를 선언해 넣어주면 나중에 scrollController.animateTo()를 활용해 ListView의 위치를 이동시키는데 사용할 수도 있습니다.
ScrollController _scrollController = _scrollController = new ScrollController();
...
child : ListView(
scrollDirection: Axis.vertical,
shrinkWrap: true,
controller: _scrollController,
children: _items.map((Item item) {
return _singleItemDisplay(item);
}).toList());
viewModel.scrollController.animateTo(viewModel.scrollController.position.extentInside, duration: Duration(seconds: 2), curve: Curves.ease);
- shrinkWrap : ListView안에 또 ListView를 넣는 경우, child의 "크기만큼만 할당"하게 하기 위해서 child ListView에 true를 해줍니다. 즉, 사용자에게 보이는 외부 높이가 아닌, 내부적인 높이(자식의 위젯 수에 따라 무한히 늘어나는)를 활용해 렌더링 하는 것이다.(굉장히 cost-expensive합니다)
** slivers : 애니메이션 효과로, 스크롤시 위로 올라가 사라지는 등 헤더의 하단부를 보여주는 효과를 의미합니다. 위의 경우, ListView>ListView 대신 "CustomScrollView>SliverList"를 활용하면 문제를 해결할 수 있습니다. - physics : 위 shrinkWrap==true 상황에, child의 physics에 NeverScrollableScrollPhysics()를 넣어주어 child의 scroll을 금지시켜줄 수 있습니다. 이외에 BouncingScrollPhysics() 등 스크롤 시의 효과를 선택해줄 수 있습니다.
-----------------------------------------------------------------------------------------------------
ListView(
padding: const EdgeInsets.all(8),
children: <Widget>[
Container(
height: 50,
color: Colors.amber[600],
child: const Center(child: Text('Entry A')),
),
Container(
height: 50,
color: Colors.amber[500],
child: const Center(child: Text('Entry B')),
),
Container(
height: 50,
color: Colors.amber[100],
child: const Center(child: Text('Entry C')),
),
],
)
- ListView().builder() : ListView의 named constructor로, on demand로 각각의 위젯들이 만들어질 것이므로, itemCount: 와 itemBuilder: 를 활용해 렌더링할 위젯을 만들어냅니다. 리스트가 너무 길거나, pagenation을 구현할 때 사용합니다.
** Pagenation(페이칭 처리) : 매번 전부 가져오면 너무 느려지므로, 데이터를 조금씩 나눠 가져오고 user 의 request에 의해 가져오게 하는 방식입니다. 이는 보통 Front와 Back 모두의 구현이 필요합니다.
final List<String> entries = <String>['A', 'B', 'C'];
final List<int> colorCodes = <int>[600, 500, 100];
Widget build(BuildContext context) {
return ListView.builder(
padding: const EdgeInsets.all(8),
itemCount: entries.length,
itemBuilder: (BuildContext context, int index) {
return Container(
height: 50,
color: Colors.amber[colorCodes[index]],
child: Center(child: Text('Entry ${entries[index]}')),
);
}
);
}
- ListView().separate() : ListView의 naed constructor로, separator로 구분되는 ListView를 만들어 냅니다. 위와 같이 itemCount: 와 itemBuilder: 를 활용해 렌더링할 위젯을 만들어내지만, separatorBuilder로 구분선까지 만들어주는 것이 특이합니다.
final List<String> entries = <String>['A', 'B', 'C'];
final List<int> colorCodes = <int>[600, 500, 100];
ListView.separated(
padding: const EdgeInsets.all(8),
itemCount: entries.length,
itemBuilder: (BuildContext context, int index) {
return Container(
height: 50,
color: Colors.amber[colorCodes[index]],
child: Center(child: Text('Entry ${entries[index]}')),
);
},
separatorBuilder: (BuildContext context, int index) => const Divider(),
);
이외에도 GridView, PageView 등도 위와 같으니, api 페이지를 참고해 위와 같이 다양한 constructor를 활용해 구현하면 좋습니다.
https://terry1213.github.io/flutter/flutter-decoding-flutter-shrinkwrap-vs-slivers/
https://api.flutter.dev/flutter/widgets/ListView-class.html
728x90
반응형
'Developers 공간 [Shorts] > Frontend' 카테고리의 다른 글
[Flutter] Custom Scrollable Tabbar 구현하기 (0) | 2023.03.12 |
---|---|
[Flutter] 카카오 API 활용하기 위한 셋팅 (0) | 2023.03.12 |
[Flutter] A GlobalKey was used multiple times inside one widget's child list (0) | 2023.03.06 |
[Flutter] Conditonal 하게 Rendering하는 방법 (0) | 2023.03.05 |
[Flutter] Stacked 패키지에서 initState가 동작을 안한다? (State에 대해서....) (0) | 2023.03.05 |