하위 트리는 상위 트리 내부에 존재 해야한다.
전체는 MaterialApp
내부에 존재 해야한다.
import 'package:flutter/material.dart';
import 'package:flutter_receipe/pages/recipe_page.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false, // 화면에 디버그 제거
theme: ThemeData(
fontFamily: "PatuaOne"
),
home: RecipePage(), // 클래스 분리
);
}
}
MaterialApp
하위에 Scaffold
를 생성 ( = 페이지 )
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class RecipePage extends StatelessWidget { // StatelessWidget - 상태없음(고정된 뷰)
const RecipePage({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return Scaffold(
appBar: _appBar(), // 위젯 분리 / dart 에서 '_'는 private을 뜻함
body: Padding( // 패딩을 주기 위해 위젯으로 감싼다.
padding: const EdgeInsets.symmetric(horizontal: 16.0), // symmetric 대칭
child: ListView( // ListView - 스크롤 가능
children: [
_title('Recipes'),
_menus(), // 위젯 분리 ( ctrl + alt + m 키 사용)
_listItem(imageName: "burger", title: "Made coffee", des: "커피 설명중입니다 아이스 아메리카노"),
_listItem(imageName: "coffee", title: "Made Burger", des: "햄버거 만드는 설명입니다. 불고기 버거"),
_listItem(imageName: "pizza", title: "Made Pizza", des: "피자 만드는 설명입니다. 파인애플 피자"),
],
),
),
);
}
원래 있는 위젯(ListView
)을 Padding
으로 감싸게 되면 child로 자식 위젯이 된다.
Scaffold
는 AppBar
, Drawer
, BottomNavigationBar
, FloatingActionButton
같은 하위 위젯을 가진다.
AppBar _appBar() {
return AppBar(
backgroundColor: Colors.white, // 이 방식 보다 테마로 작성하는것이 좋다
actions: [
IconButton(
onPressed: (){
print("클릭됨");
},
icon: Icon(CupertinoIcons.search),
color: Colors.black),
SizedBox(width: 15), // 컴포넌트 사이 여백을 줄때 간단히 사용
Icon(CupertinoIcons.heart, color: Colors.redAccent),
SizedBox(width: 15,)
],
);
}
}
위 코드로 만든 AppBar
Widget _title(String text, {double tp = 20.0}) // 디폴트 값 20, 값을 바꿀 수 있다.
=> Padding( // 람다 표현식으로 타이틀 리턴
padding: EdgeInsets.only(top: tp ), // 원래 있던 const를 제거 동적으로 패딩 변환
child: Text(text,style: TextStyle(fontSize: 30),),
);
Widget _menus() {
return Padding(
padding: const EdgeInsets.only(top: 20.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, // 가로 방향
children: [
_menu(icon: Icons.food_bank, des: "All"),
_menu(icon: Icons.emoji_food_beverage, des: "Burger"),
_menu(icon: Icons.fastfood, des: "Coffee"),
_menu(icon: Icons.local_pizza, des: "Pizza"),
],
),
);
}
Row
나 Column
은 자식 요소들을 가진다 => children
Widget _menu({required var icon, required String des}) {
// 컨테이너를 위젯타입으로 바꿔 놓으면 padding 같은 옵션 줄때 안 터짐
return Container(
width: 60,
height: 80,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(25),
border: Border.all(color: Colors.black87),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center, // 세로 방향
children: [
Icon(icon, color: Colors.redAccent, size: 30),
Text(des)
],
),
);
}
Container
는 크기를 정하지 않으면 최대한 크게 잡힌다.
decoration: BoxDecoration()
옵션으로 컨테이너의 모양을 잡을 수 있다.
여기 까지의 코드로 만들면
Widget _listItem({required String imageName, required String title, required String des}) {
return Padding(
padding: const EdgeInsets.only(top: 30.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ClipRRect(
borderRadius: BorderRadius.circular(15),
child: AspectRatio(
aspectRatio: 9/5 ,
child: Image.asset("assets/images/${imageName}.jpeg",
fit: BoxFit.cover)
),
),
Text(title, style: TextStyle(fontSize: 30),),
Text(des),
],
),
);
}
required
는 다트문법인데 파라미터를 반드시 필요로 할 경우에 넣는다.
ClipRRect
ClipRect
이름이 비슷해서 헷갈렸는데 차이는
ClipRRect
는 사각형모양의 UI를 둥글게 깍을때 사용한다.
ClipRect
는 사각형내부를 잘라내는데 사용한다. - 특정영역 제거
AspectRatio
는 지정한 비율로 이미지를 표현할때 주로 사용한다.
위 코드로 만들면