사전캠프 4일차
4일차는 뷰 위젯과 레이아웃 위젯 강의를 들었다.
종류도 다양하고, 내용도 생각보다 방대해 차근차근 학습했다.
가로 혹은 세로로 스와이프, 즉 스크롤 할 수 있는 페이지를 만드는 위젯이다.
import 'package:flutter/material.dart'; // void main() { runApp(const MyApp()); } // class MyApp extends StatelessWidget { const MyApp({super.key}); // Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, home: Scaffold( body: PageView( children: [ Container( color: Colors.red, child: const Center( child: Text( "1", style: TextStyle(fontSize: 50, color: Colors.white), ), ), ), Container( color: Colors.blue, child: const Center( child: Text( "2", style: TextStyle(fontSize: 50, color: Colors.white), ), ), ), Container( color: Colors.yellow, child: const Center( child: Text( "3", style: TextStyle(fontSize: 50, color: Colors.white), ), ), ), ], )), ); } }
- PageView 위젯의 예시를 가져왔다.
코드를 보자면 자주 사용되는 게 몇 개 있다.
📕
children
- 페이지를 사용할 자식 위젯을 만들어 준다.Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, home: Scaffold( body: PageView( children: [ Container( color: Colors.red, child: const Center( child: Text( "1", style: TextStyle(fontSize: 50, color: Colors.white), ), ), ) . . .📙
scrollDirection
- 스크롤 방향을 결정하며, 기본값은 horizontal(가로) 방향이다.
Axis.vertical을 사용하여 세로 방향으로 전환할 수 있다.scrollDirection: Axis.vertical,📒
controller
- controller 옵션에서 PageController 클래스를 통해 원하는 스크롤 위차나 페이지로 이동한다. 여기서는 final을 사용했지만, 뒤에 나올 위젯은 다른 코드를 사용한다.
//생략 class _SampleWidgetState extends State<SampleWidget> { final _controller = PageController(); void initState() { super.initState(); _controller.addListener(() { if (_controller.position.maxScrollExtent == _controller.offset) { showDialog( context: context, builder: (context) => const CupertinoAlertDialog( content: Text('마지막에 도달했습니다.'), ), ); } }); }📗
pageSnapping
- 페이지 전환 시 Snapping 효과가 있어 일정 화면을 넘겼다면, 자동으로 다음 화면까지 보여지는데 이 옵션에 false를 주면 사용자가 이동하는 만큼, 멈추는 만큼 화면을 고정되게 만든다.
pageSnapping: false,📘
onpageChanged
- 이 옵션을 콜백 함수에 등록할 수 있는데, 이 옵션을 사용하면 사용자가 페이지를 이동했을 때 콜백함수가 호출되고, 현재 보고있는 페이지 번호(index)를 반환한다.
특정 이벤트를 수행하거나 페이지 인디케이터를 제어할 수 있는 것이다.onPageChanged: (int index) { showDialog( context: context, builder: (context) => CupertinoAlertDialog( content: Text('$index 페이지 활성화'), ), ); },
위젯 목록을 표시하고, 목록을 만드는 데 사용한다.
import 'package:flutter/material.dart'; // void main() { runApp(const MyApp()); } // class MyApp extends StatelessWidget { const MyApp({super.key}); // Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, home: Scaffold( body: ListView( scrollDirection: Axis.horizontal, children: List.generate( 10, (index) => Container( width: 100, height: 100, margin: const EdgeInsets.all(5), color: Colors.red.withAlpha((index + 1) * 25), ), ), ), ), ); } }
📔
scrollDirection
- 위에서 배운 것 처럼 vertical, horizontal 두 가지로 동일하지만, 기본값은 vertical(세로)이다.
scrollDirection: Axis.horizontal,📕
reverse
- 스크롤 방향의 반대로 정렬하는데, 기본값은 false이다.
true로 설정할 경우 가장 마지막에 배치된 것이 상단에 위치하게 된다.
예로, 채팅 화면에서 방금 입력한 대화가 가장 밑에 뜨게 하는 것이다.reverse: true,📙
controller
- controller 옵션에 ScrollController 클래스를 사용해 원하는 위치로 이동하거나, 현재 위치를 전달받는 등의 이벤트를 처리한다. ScrollController를 생성하여, controller를 등록해주면 _controller를 통해 스크롤을 제어하는 것이다.
- 아래 예시처럼
JumpTo함수를 사용하면 해당 위치로 이동하게 된다.child: ElevatedButton( onPressed: () { _controller.jumpTo(330); // 3번 }, child: const Text('3번영역으로 이동'), ),📒
physics
- 스크롤 동작을 결정하는데 사용된다고 한다.
🔸BouncingScrollPhysics
- 스크롤을 영역 밖까지 이동했을 때 바운스 되며, 콘텐츠 끝에 맞춰 이동된다.(ios와 유사)
🔸ClampingScrollPhysics
- 스크롤의 범위가 내부 콘텐츠보다 클 때 끝에서 스크롤을 클램핑한다.(Android와 유사)
🔸FixedExtentScrollPhysics
- 스크롤을 균일한 단위로 이동한다.
🔸NeverScrollableScrollPhysics
- 스크롤이 비활성화 되어 움직이지 않는 상태이다.ListView( controller: _controller, physics: const ClampingScrollPhysics(), //다른 설정의 클래스를 넣어주시면 됩니다. children: List.generate( 10, (index) => Container( width: 100, height: 100, margin: const EdgeInsets.all(5), color: Colors.red.withAlpha((index + 1) * 25), child: Center(child: Text(index.toString())), ), ), ),📗
cacheExtent
- 이를설정하면 캐시 등록을 하여, 페이지를 이동하더라도 로드 된 화면을 볼 수 있다.
단, 높은 수치를 입력할 경우 성능이 떨어질 수 있으니 적절한 값을 입력해야 한다.cacheExtent: 1000,📘
padding
- ListView 내부에 padding을 주어 간격을 설정한다.
행과 열이 있는 항목 모음 형식이 필요할 때 사용하는 위젯이다.
import 'package:flutter/material.dart'; // void main() { runApp(const MyApp()); } // class MyApp extends StatelessWidget { const MyApp({super.key}); // Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, home: Scaffold( body: GridView( gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 3, mainAxisSpacing: 2, crossAxisSpacing: 2, ), children: List.generate( 100, (index) => Center( child: Container( color: Colors.grey, child: Center(child: Text(index.toString())), ), ), ), ), ), ); } }
📕
gridDelegate
- 그리드의 행, 열, 간격 등을 정한다. GridView 위젯을 사용하려면 필수로 사용된다.
🔸SliverGridDelegateWithFixedCrossAxisCount
crossAxisCount를 사용하여, 크기에 관계없이 고정된 그리드를 만든다.body: GridView( gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 3, mainAxisSpacing: 2, crossAxisSpacing: 2, ),🔸
SliverGridDelegateWithMaxCrossAxisExtent
최대 너비가 고정된 타일로, 그리드를 만들고 그 너비를 기준으로 열 수를 계산한다.
maxCrossAxisExtent를 사용하는데, 반응형이라고 보면 될 것 같다.body: GridView( gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( maxCrossAxisExtent: 300, mainAxisSpacing: 2, crossAxisSpacing: 2, ),🔸
mainAxisSpacing
위, 아래 셀 간격을 설정한다.
🔸crossAxisSpacing
좌, 우 셀 간격을 설정한다.
📙scrollDirectionreversecontrollerpadding
- 위에서 배운 것과 동일하다.
TabBar 위젯과, TabBarView 위젯이 필요하며, 두 위젯을 연결하기 위해 TabController가 꼭! 필요하다.
다른 컨틀롤러와 다르게 controller를 생성할 때 초기 값을 설정해야한다.
class _SampleWidgetState extends State<SampleWidget> with TickerProviderStateMixin { late TabController _tabController; // void initState() { super.initState(); _tabController = TabController( length: 3, vsync: this, ); } ...//생략
initialIndex
초기 Tab 메뉴와 TabView가 어떤 페이지를 먼저 보여줄 것인지 설정하는 값으로, 보통은 첫 번째 탭이 활성화되어 사용되기 때문에 initialIndex는 설정하지 않는다.animationDuration
Tab 메뉴를 눌렀을 때 Tab 메뉴 하단의 인디케이터의 애니메이션 효과 시간을 Duration 객체를 통해 조절할 수 있다.length
메뉴가 몇 개인지를 설정하는 옵션으로, 반드시 넣어야 하는 필수 값이다.
💡TabBar의 tabs과 controller의 length에 서로 다른 값을 넣으면 오류가 발생한다.vsync
탭 컨트롤러의 렌더링을 장치 디스플레이의 수직 동기화와 동기화할지 여부를 결정한다. 이 부분의 경우 this를 넣고, 이를 매칭하기 위해서는 TickerProviderStateMixin라는 클래스를 with라는 키워드로 포함시켜야 한다.
TabBarView 위젯 예제TabBar( controller: _tabController, labelColor: Colors.blue, unselectedLabelColor: Colors.grey, labelPadding: const EdgeInsets.symmetric(vertical: 20), tabs: const [ Text('메뉴1'), Text('메뉴2'), Text('메뉴3'), ], ),
labelColor- 메뉴 색상unselectedLabelColor- 비활성화 메뉴 색상labelPadding- 메뉴 간격indicatorWeight- 활성화 메뉴의 인디게이터의 두께labelStyle- 활성화 메뉴 텍스트 스타일unselectedLabelStyle- 비활성화 텍스트 스타일 설정
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(body: const SampleWidget()),
);
}
}
class SampleWidget extends StatefulWidget {
const SampleWidget({super.key});
State<SampleWidget> createState() => _SampleWidgetState();
}
class _SampleWidgetState extends State<SampleWidget>
with TickerProviderStateMixin {
late TabController _tabController;
void initState() {
super.initState();
_tabController = TabController(
length: 3,
vsync: this,
);
}
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
TabBar(
controller: _tabController,
labelColor: Colors.blue,
unselectedLabelColor: Colors.grey,
labelPadding: const EdgeInsets.symmetric(vertical: 20),
tabs: const [
Text('메뉴1'),
Text('메뉴2'),
Text('메뉴3'),
],
),
Expanded(
child: TabBarView(
controller: _tabController,
children: [
Container(
color: Colors.blue,
child: Center(child: Text('메뉴1 페이지 ')),
),
Container(
color: Colors.blue,
child: Center(child: Text('메뉴2 페이지 ')),
),
Container(
color: Colors.blue,
child: Center(child: Text('메뉴3 페이지 ')),
),
],
),
),
],
),
);
}
}
테두리, 배경색, 패딩 등을 꾸밀 수 있으며, 다른 위젯을 래핑하고 모양을 제어하는 방법을 제공하는 데에도 사용된다.
Container 옵션 예제Container( padding: const EdgeInsets.only( left: 20, right: 20, ), width: 200, height: 50, color: Colors.red, child: Center(child: Text('Container')), ),
padding- 내부 간격width- 너비height- 높이color- 내부 뒷배경 색상child- 모든 위젯에는 child 존재, 자식 Widget 지정 가능decoration- 그라데이션, 그림자, 모서리 설정 등
Container(
padding: const EdgeInsets.only(
left: 20,
right: 20,
),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Color.fromARGB(255, 255, 59, 98).withOpacity(0.7),
Color.fromARGB(255, 255, 59, 98)
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: Color.fromARGB(255, 255, 59, 98).withOpacity(0.5),
spreadRadius: 5,
blurRadius: 7,
offset: Offset(0, 3), // changes position of shadow
),
],
),
width: 200,
height: 150,
child: Center(
child: Text(
'Container',
style: TextStyle(color: Colors.white),
)),
),
Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( color: Colors.red, width: 100, height: 40, ), const SizedBox(height: 10), Container( color: Colors.blue, width: 100, height: 40, ), ], ), //SizedBox 사용 위/아래 간격 사용 예제
- 고정 크기박스를 만드는 데 사용하며, 위젯 사이 패딩을 넣어 빈 공간을 만든다.
Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( color: Colors.red, width: 100, height: 40, ), const SizedBox(width: 10), Container( color: Colors.blue, width: 100, height: 40, ), ], ), //SizedBox 사용 좌/우 간격 사용 예제
- Row에서 사용할 경우 height가 아닌 width로 지정했다.
Row 위젯
Row( mainAxisAlignment: MainAxisAlignment.center, children: List.generate( 5, (index) => Container( width: 40, height: 40, color: Colors.red, margin: const EdgeInsets.all(5), ), ), ),
- 가로 정렬이 필요할 때 사용한다.
위에서 배운 Container 와 옵션을 활용해 꾸몄고,List.generate(를 사용하여, 5번 반복할 수 있다.
📕
mainAxisAlignment
이 옵션은 가로 정렬시 어떤 기준으로 보여줄지를 나타내는 옵션이다.
기본적으로 정렬의 경우start,center,end으로, 각각 좌측, 중앙, 우측 정렬이다.
spaceBetween은 양끝을 정렬시키고 가운데의 경우 동일한 간격을 준다.
spaceEvenly는 양끝을 포함한 전체를 동일한 간격으로 정렬한다.
spaceAround는 각 Container의 좌우에 동일한 여백을 주어, 그 여백이 겹치지 않게 한다.
📙crossAxisAlignment
정렬은 가로 기준이며, cross의 경우 세로 기준으로 설정된다.
동일하게start,center,end를 사용하고,stretch의 경우 세로로 끝까지 늘린다.
또한, 값을 줄 때 변화가 없다면body: center가 되어있는지 확인하고,SizedBox를 만들면 해결된다.body: center ( child : SizedBox (💡 부모의 높이만큼 어떻게 세로 정렬을 할지 결정되기 때문에 기준이 되는 부모의 높이가 있어야 한다.
Column 위젯
Column( children: List.generate( 5, (index) => Container( width: 40, height: 40, color: Colors.red, margin: const EdgeInsets.all(5), ), ), ),
- 세로 정렬이 필요할 때 사용한다.
📒
mainAxisAlignment
반대로 세로 정렬 시 어떤 기준으로 보여줄지를 나타내는 옵션이다.
spaceBetween은 각 상하단을 정렬시키고 가운데의 경우 동일한 간격을 준다.
spaceEvenly는 각 상하단을 포함한 전체를 동일한 간격으로 정렬한다.
spaceAround는 각 Container의 상하에 동일한 여백을 주어, 그 여백이 겹치지 않게 한다.
📗crossAxisAlignment
정렬은 세로 기준이며, cross의 경우 가로 기준이 된다.
동일하게start,center,end를 사용하고,stretch의 경우 가로로 끝까지 늘린다.
또한, 값을 줄 때 변화가 없다면body: center가 되어있는지 확인하고,SizedBox를 만들면 해결된다.body: center ( child : SizedBox (
Widget build(BuildContext context) { return const MaterialApp(
보통의 경우 여기서 const를 지우면 오류가 해결되는 것 같다.
body: center ( child : SizedBox (
또한, 값을 줄 때 변화가 없다면 body: center가 되어있는지 확인하고, SizedBox를 만들면 해결된다.
Layout 위젯 강의 중 2-4 Expanded 위젯, 2-5 Stack 위젯 & Positioned 위젯이 남았다.
생각보다 이해하면서 그리고 코드도 외우고, 기억하려고 하니 예상한 진도보다 늦었다.
그리고 복습하는 느낌으로 TIL을 작성중에 있는데, 이게 효율적인지 비효율적인지는 아직 잘 모르겠다.
나중에 막히거나, 생각이 나지 않을 때 내가 기억하려고 써둔 팁을 보러 오면 좋지 않을까! 싶긴 한데... 좀 더 고민해봐야겠다.
그래도 이해하고 넘어가는 게 뭔가 개운해서 좋은 것 같다.
그냥 넘어가면 봤던 건데 기억이 안나서 답답하거나, 어디서 봤는지 찾아야 되니 열심히 공부해보자💙