
What's the point?
- 위젯은 UI를 빌드할 때 사용하는 클래스다.
- 위젯은 레이아웃과 UI 요소에서 동시에 쓰인다.
- 복잡한 위젯은 간단한 위젯들의 조합으로 만들어진다.
Note
debugPaintSizeEnabled를 true로 설정하면 레이아웃을 가시적으로 볼 수 있다.
1, 2, 3은 생략한다.
대부분의 위젯은 build 메서드를 가진다.
위젯을 인스턴스로 만들어 리턴시키는 함수다.
Material 앱에서는 Scaffold 위젯을 사용할 수 있다.
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
const String appTitle = 'Flutter layout demo';
return MaterialApp(
title: appTitle,
home: Scaffold(
appBar: AppBar(title: const Text(appTitle)),
body: const Center(
child: Text('Hello World'),
),
),
);
}
}
Note
Material library는Material Design 규칙을 위젯에 적용한다.
UI를 만들 때,표준 widget library를 사용해도 되고,Material library를 사용해서 만들어도 된다.
cupertino 앱을 사용하려면 CupertinoPageScaffold를 사용하면 된다.
Material과 달리 default banner나 background color를 제공하지 않는다.
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return const CupertinoApp(
title: 'Flutter layout demo',
theme: CupertinoThemeData(
brightness: Brightness.light,
primaryColor: CupertinoColors.systemBlue,
),
home: CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
backgroundColor: CupertinoColors.systemGrey,
middle: Text('Flutter layout demo'),
),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[Text('Hello World')],
),
),
),
);
}
}
theme 속성에 CupertinoThemeData를 설정하면 된다.CupertinoNavigationBar를 navigationBar 속성에 지정하면 된다.CupertinoColors를 사용하면 된다.Note
Cupertino library는 Apple의Human Interface Guideline을 위젯에 적용한다.
UI를 만들 때,표준 widget library를 사용해도 되고,Cupertino library를 사용해서 만들어도 된다.
build 메서드 안에 Container를 넣으면 된다.
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return Container(
decoration: const BoxDecoration(color: Colors.white),
child: const Center(
child: Text(
'Hello World',
textDirection: TextDirection.ltr,
style: TextStyle(fontSize: 32, color: Colors.black87),
),
),
);
}
}
non-Material 앱은 default로 AppBar, title 또는 background color를 포함하지 않는다.
넣고 싶으면 직접 만들어서 넣어야 한다.
가장 많이 사용하는 레이아웃 패턴은 위젯을 horizontal 또는 vertical하게 배치시키는 거다.
Note
Row와Column이 가장 흔히 쓰이는 기본적인 위젯이다.
이런 로우 레벨 위젯은 커스터마이즈를 극대화시킬 수 있다.
로우 레벨 위젯으로 만족되지 않으면 하이 레벨 위젯을 사용하면 된다.
Row대신ListTile을 사용하면leading과trailing속성에 위젯을 넣을 수 있다.
Column대신ListView를 사용하면 자동 스크롤이 가능하다.
레이아웃이 너무 넓어 디바이스에 맞지 않는다면 Expanded 위젯을 사용하자.
Row나 column 안에서 사용하면 딱 맞게 조절된다.
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(child: Image.asset('images/pic1.jpg')),
Expanded(child: Image.asset('images/pic2.jpg')),
Expanded(child: Image.asset('images/pic3.jpg')),
],
);
default로 Row나 Column은 최대한 많은 공간을 차지하려고 한다.
하지만 children을 가깝게 모으고 싶으면 MainAxisSize.min을 하면 된다.
Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.star, color: Colors.green[500]),
Icon(Icons.star, color: Colors.green[500]),
Icon(Icons.star, color: Colors.green[500]),
const Icon(Icons.star, color: Colors.black),
const Icon(Icons.star, color: Colors.black),
],
)
너무 nesting되는 상황이 발생하면 변수나 함수로 나눠서 관리하는 것이 좋다.
final stars = Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.star, color: Colors.green[500]),
Icon(Icons.star, color: Colors.green[500]),
Icon(Icons.star, color: Colors.green[500]),
const Icon(Icons.star, color: Colors.black),
const Icon(Icons.star, color: Colors.black),
],
);
final ratings = Container(
padding: const EdgeInsets.all(20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
stars,
const Text(
'170 Reviews',
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.w800,
fontFamily: 'Roboto',
letterSpacing: 0.5,
fontSize: 20,
),
),
],
),
);
const descTextStyle = TextStyle(
color: Colors.black,
fontWeight: FontWeight.w800,
fontFamily: 'Roboto',
letterSpacing: 0.5,
fontSize: 18,
height: 2,
);
// DefaultTextStyle은 하위 Text 위젯들에게 동일한 스타일을 적용한다.
// 여기선 하위 Text 위젯들이 전부 descTextStyle로 적용된다.
// DefaultTextStyle.merge는 기존 스타일에 다른 스타일을 덮어씌우는 함수다. (copyWith 느낌)
final iconList = DefaultTextStyle.merge(
style: descTextStyle,
child: Container(
padding: const EdgeInsets.all(20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Column(
children: [
Icon(Icons.kitchen, color: Colors.green[500]),
const Text('PREP:'),
const Text('25 min'),
],
),
Column(
children: [
Icon(Icons.timer, color: Colors.green[500]),
const Text('COOK:'),
const Text('1 hr'),
],
),
Column(
children: [
Icon(Icons.restaurant, color: Colors.green[500]),
const Text('FEEDS:'),
const Text('4-6'),
],
),
],
),
),
);
final leftColumn = Container(
padding: const EdgeInsets.fromLTRB(20, 30, 20, 20),
child: Column(children: [titleText, subTitle, ratings, iconList]),
);
ContainerGridViewListViewStackScaffoldAppBarCardListTileCupertinoPageScaffoldCupertinoNavigationBarCupertinoSegmentedControlCupertinoTabBar, CupertinoTabScaffoldGridView.count
column의 개수를 정할 수 있다.
GridView.extent
max pixel에 맞춰 유동적으로 개수를 채워넣는다.
Widget _buildGrid() => GridView.extent(
maxCrossAxisExtent: 150,
padding: const EdgeInsets.all(4),
mainAxisSpacing: 4,
crossAxisSpacing: 4,
children: _buildGridTileList(30),
);
// The images are saved with names pic0.jpg, pic1.jpg...pic29.jpg.
// The List.generate() constructor allows an easy way to create
// a list when objects have a predictable naming pattern.
List<Widget> _buildGridTileList(int count) =>
List.generate(count, (i) => Image.asset('images/pic$i.jpg'));
Note
Table위젯은 grid를 만들되, scroll이 안되도록 만들 때 사용하면 좋다.
대부분의 위젯과 함께 사용될 수 있지만, 주로 ListTile과 함께 쓰인다.
Card는 default로 size가 0이므로 SizedBox로 size를 제약줄 수 있다.
Card는 약간의 rounded corner와 drop shadow를 가지고 있어, 3D 효과를 준다.
elevation 속성을 바꾸면 drop shadow 효과를 바꿀 수 있다.
24로 설정하면 시각적으로 표면에 더 떠있는 것처럼 보이고, 그림자가 더 분산된 것처럼 보인다.
Widget _buildCard() {
return SizedBox(
height: 210,
child: Card(
child: Column(
children: [
ListTile(
title: const Text(
'1625 Main Street',
style: TextStyle(fontWeight: FontWeight.w500),
),
subtitle: const Text('My City, CA 99984'),
leading: Icon(Icons.restaurant_menu, color: Colors.blue[500]),
),
const Divider(),
ListTile(
title: const Text(
'(408) 555-1212',
style: TextStyle(fontWeight: FontWeight.w500),
),
leading: Icon(Icons.contact_phone, color: Colors.blue[500]),
),
ListTile(
title: const Text('costa@example.com'),
leading: Icon(Icons.contact_mail, color: Colors.blue[500]),
),
],
),
),
);
}