반응형

📝위젯

Widget이란 앱에 있는 요소를 의미한다 (버튼, 글자, 스크롤 박스 등…)

 

📝위젯 사용 주의점

어떤 위젯이 부모의 크기를 비율로 가져가는 경우 부모의 크기가 이론상 무한대로 확장 가능할 때 제한해야한다 예를 들면 Row가 부모 위젯인 경우 Width가 이론상 무한이고 Column의 경우 Height가 이론상 무한이기 때문에 이 두개가 있을 경우 Expanded따위로 제한해야한다 참고로 Row 안에 Column 안에 있는 위젯의 경우 둘다 Expanded로 제한해줘야한다

 

📝Flutter 개발 팁

// Size 예시 [Gaps, FontSize, Color 등…]

class Gaps {
	static const v1 = SizedBox(height: 1);
	...
}

Size 표가 있으면 통일성이 생긴다

 

📝Flutter 기술

포커스 해제하기 (다른 곳 터치 시 키보드 내리기)

Widget build(BuildContext context) {
    return GestureDetector(
      onTap: _onScaffoldTap,
			...
	)
)

void _onScaffoldTap() {
	FocusManager.instance.primaryFocus?.unfocus(); // 현재 포커스 제거
}

 

다크모드 설정

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:tiktok_clone/constants/sizes.dart';
import 'package:tiktok_clone/features/authentication/sign_up_screen.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await SystemChrome.setPreferredOrientations(
    [
      DeviceOrientation.portraitUp,
    ],
  );

  runApp(const TikTokApp());
}

class TikTokApp extends StatelessWidget {
  const TikTokApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'TikTok Clone',
      themeMode: ThemeMode.system, // 시스템에 따라 다르다
      // themeMode: ThemeMode.dark,   // 다크모드 강제
      // themeMode: ThemeMode.light,  // 라이트모드 강제

      theme: ThemeData(
        // 라이트 모드일 때 사용
        brightness: Brightness.light,
        scaffoldBackgroundColor: Colors.white,
        primaryColor: const Color(0xFFE9435A),
        textSelectionTheme: const TextSelectionThemeData(
          cursorColor: Color(0xFFE9435A),
        ),
        splashColor: Colors.transparent,
        appBarTheme: const AppBarTheme(
          foregroundColor: Colors.black,
          backgroundColor: Colors.white,
          elevation: 0,
          titleTextStyle: TextStyle(
            color: Colors.black,
            fontSize: Sizes.size16 + Sizes.size2,
            fontWeight: FontWeight.w600,
          ),
        ),
				tabBarTheme: TabBarTheme( // 탭바에서 사용하는 다크 모드 내용들
          labelColor: Colors.black, // 선택 글자
          unselectedLabelColor: Colors.grey.shade500, // 노 선택 글자
          indicatorColor: Colors.black, // 아래 글자
        ),
      ),
      darkTheme: ThemeData(
        // 다크모드일 때 설정
        brightness: Brightness.dark, // 글자색상??
        scaffoldBackgroundColor: Colors.black, // scafoold 색상
        bottomAppBarTheme: BottomAppBarTheme(
          // 바텀앱바 색상
          color: Colors.grey.shade900,
        ),
        primaryColor: const Color(0xFFE9435A),
      ),
      home: const SignUpScreen(),
    );
  }
}

 

회전 막기

void main() async {
  WidgetsFlutterBinding.ensureInitialized(); // 플러터의 화면이 나오기전에 아래 설정을 사용하겠습니다라는 뜻 

  await SystemChrome.setPreferredOrientations(
    [
      DeviceOrientation.portraitUp, // 핸드폰 회전 막기
    ],
  );
  runApp(const TikTokApp());
}

 

Text Theme

// 방법1 - 직접 Theme 정의하기
theme: ThemeData(
  // 라이트 모드일 때 사용
  brightness: Brightness.light,
  textTheme: TextTheme(
    // <https://m2.material.io/design/typography/the-type-system.html#type-scale> 해당 사이트에서 폰트에 따른 크기를
    // css 또는 flutter전용 textTheme형식을 폰트체만 정해주면 코드를 자동 생성해준다 (Material2)
    // textTheme에서 제공하는 것들 displayLarge(이름만 제공)
    displayLarge: GoogleFonts.openSans(
        fontSize: 95, fontWeight: FontWeight.w300, letterSpacing: -1.5),
    displayMedium: GoogleFonts.openSans(
        fontSize: 59, fontWeight: FontWeight.w300, letterSpacing: -0.5),
    displaySmall:
        GoogleFonts.openSans(fontSize: 48, fontWeight: FontWeight.w400),
    headlineMedium: GoogleFonts.openSans(
        fontSize: 34, fontWeight: FontWeight.w400, letterSpacing: 0.25),
    headlineSmall:
        GoogleFonts.openSans(fontSize: 24, fontWeight: FontWeight.w400),
    titleLarge: GoogleFonts.openSans(
        fontSize: 20, fontWeight: FontWeight.w500, letterSpacing: 0.15),
    titleMedium: GoogleFonts.openSans(
        fontSize: 16, fontWeight: FontWeight.w400, letterSpacing: 0.15),
    titleSmall: GoogleFonts.openSans(
        fontSize: 14, fontWeight: FontWeight.w500, letterSpacing: 0.1),
    bodyLarge: GoogleFonts.roboto(
        fontSize: 16, fontWeight: FontWeight.w400, letterSpacing: 0.5),
    bodyMedium: GoogleFonts.roboto(
        fontSize: 14, fontWeight: FontWeight.w400, letterSpacing: 0.25),
    labelLarge: GoogleFonts.roboto(
        fontSize: 14, fontWeight: FontWeight.w500, letterSpacing: 1.25),
    bodySmall: GoogleFonts.roboto(
        fontSize: 12, fontWeight: FontWeight.w400, letterSpacing: 0.4),
    labelSmall: GoogleFonts.roboto(
        fontSize: 10, fontWeight: FontWeight.w400, letterSpacing: 1.5),
  ),
	listTileTheme: const ListTileThemeData(
    iconColor: Colors.black,
  ),
  scaffoldBackgroundColor: Colors.white,
  primaryColor: const Color(0xFFE9435A),
  textSelectionTheme: const TextSelectionThemeData(
    cursorColor: Color(0xFFE9435A),
  ),
  splashColor: Colors.transparent,
  appBarTheme: const AppBarTheme(
    foregroundColor: Colors.black,
    backgroundColor: Colors.white,
    elevation: 0,
    titleTextStyle: TextStyle(
      color: Colors.black,
      fontSize: Sizes.size16 + Sizes.size2,
      fontWeight: FontWeight.w600,
    ),
  ),
),

// 방법2 - 만들어져있는 폰트 Theme 가져오기
// GoogleFonts의 itim이라는 폰트체 내용 전체 가져오기
textTheme: GoogleFonts.itimTextTheme(
  ThemeData(brightness: Brightness.dark).textTheme, // 다크모드 테마 가져오기
),

// 상세가 먼저 적용되기 때문에 부분적으로 적용시켜도 된다
Text(
  "Sign up for TikTok",
  style: GoogleFonts.abrilFatface(
    textStyle: const TextStyle(
      fontSize: Sizes.size24,
      fontWeight: FontWeight.w700,
    ),
  ),
)

// theme 데이터 가져오기 예제
style: Theme.of(context)
  .textTheme
  .headlineSmall!
  .copyWith(color: Colors.red), // Theme에서 가져오지만 변형이 필요하면 copyWith을 사용한다
  
  
// 방법3 - Typography 사용 (Flutter 자체 내제된 Font Style)
theme: ThemeData(
        textTheme: Typography.blackMountainView,
			...

darkTheme: ThemeData(
        textTheme: Typography.whiteMountainView, // ?
			...

// 일부 항목 커스텀 가능
static const TextTheme blackMountainView = TextTheme(
    displayLarge: TextStyle(debugLabel: 'blackMountainView displayLarge', fontFamily: 'Roboto', color: Colors.black54, decoration: TextDecoration.none),
    displayMedium: TextStyle(debugLabel: 'blackMountainView displayMedium', fontFamily: 'Roboto', color: Colors.black54, decoration: TextDecoration.none),
    displaySmall: TextStyle(debugLabel: 'blackMountainView displaySmall', fontFamily: 'Roboto', color: Colors.black54, decoration: TextDecoration.none),
		...
) // 예시

 

📝자주쓰는 위젯 코드

// 색상지정 및 투명도
Colors.white.withOpacity(0.5)
Colors.white
Color(0xFF181818)

// 폰트 두께
FontWeight.w800

// 보더형태
BorderRadius.circular(25)

// 위젯 경계 넘어갈시 자름
clipBehavior: Clip.hardEdge

// 좌우 패딩
EdgeInsets.symmetric(horizontal: 20,)

// Column에서 가로 정렬, Row에서 세로 정렬
crossAxisAlignment: CrossAxisAlignment.start
crossAxisAlignment: CrossAxisAlignment.end
crossAxisAlignment: CrossAxisAlignment.spaceBetween

// Column에서 수직 정렬, Row에서 가로 정렬
mainAxisAlignment: MainAxisAlignment.start
mainAxisAlignment: MainAxisAlignment.end
mainAxisAlignment: MainAxisAlignment.spaceBetween

 

 

📝 레이아웃 & UI 위젯

Widget 명 역할 키 : 설명 → 값 사용 예) 활용 예제

Widget명 역할 키 : 설명 → 값 사용 예) 활용 예제
MaterialApp 구글 기본앱같은 디자인에 쓰인다 → 지원 위젯이 많다 [커스텀디자인을 많이 만들기 때문에 해당 위젯 사용] home : 위젯 넣을 곳 (일반적 Scaffold 많이 사용) → Widget MaterialApp(home: Scaffold(..))  
Cupertino 아이폰 기본앱같은 디자인에 쓰인다 → 하위 지원 위젯이 별로 없어서 개발하기 힘듬      
Scaffold 앱은 대부분 상 / 중 / 하로 나누어져있는데 쉽게 구성하기 위한 위젯 appBar : 상단에 넣을 위젯 → PreferredSizeWidget
body
: 중단에 넣을 위젯 -> Widget
bottomNavigationBar : 하단에 넣을 위젯 → Widget
backgroundColor: 배경색 → Color
Scaffold(appBar: Padding(..))  
Padding 패딩을 주는 위젯 padding : 패딩옵션 → EdgeInsetschild : 패딩 안에 들어갈 Widget → Widget    
Color 색을 반환해주는 위젯 색 값 → int Color(0xFF181818)  
EdgeInsets 패딩 옵션 위젯 symmetric → 좌우패딩, 상하패딩 all → 상하좌우 패딩 vertical : 상하 패딩 → doublehorizental : 좌우 패딩 → double
Column Children 안에 항목을 세로로 배치한다 (이론상 무한정 배치 가능하지만 화면 벗어난다고 에러 발생) [width 제한 : 상위 위젯크기 (없으면 화면 크기), height 제한 : 무한 (화면 벗어날시 에러)]- 화면 세로줄 하나를 다 차지한다 (넓이만 없어) children : 세로 박스 안에 들어갈 위젯 리스트 → Widget
crossAxisAlignment : 수직 정렬 옵션 →CrossAxisAlignment
children: [const SizedBox(..)]  
SizedBox 빈 박스 생성 (Not Div) [마진 주는 용도로 자주 쓰인다] height : 높이 → int SizedBox ( height : 80 )  
Row Children 안에 항목을 가로로 배치한다 (이론상 무한정 배치 가능하지만 화면 벗어난다고 에러 발생) [width 제한 : 무한 (화면 벗어날시 에러), height 제한 : 상위 위젯크기 (없으면 화면 크기), ]- 화면 가로줄 하나를 다 차지한다 (높이만 없어) mainAxisAlignment : 수평 정렬 → MainAxisAlignment
children : 가로 박스 안에 들어갈 위젯 리스트 → Widget
Row(children : [ …])  
Text 텍스트 위젯 data[생략 가능(필수)] : 텍스트 → String
style : 텍스트 스타일 → TextStyle
Text( 'Hey, Selena', style: TextStyle( ... ),  
TextStyle 텍스트 스타일 위젯 color : 색상 → Color
fontSize : 글자크기 → int
fontWeight : 폰트 두께 -> FontWeight
TextStyle( color: Colors.white, fontSize: 28, fontWeight: FontWeight.w800, ),  
AppBar 상단 레이아웃 (헤더) 위젯 title : 헤더에 적을 내용 → String    
Center 수평 및 수직 가운데 정렬된 상태로 정중앙에 위치한다 child : Center 박스 안에 들어갈 Widget → Widget Center( child: Text(”hello word”))  
SingleChildScrollView 스크롤 바 위젯 child : 스크롤바 안에 들어갈 Widget → Widget    
Container child에 있는 위젯 크기만큼 크기가 정해진다 clipBehavior : 내용물 크기가 커서 넘치는 경우 처리 방식 → Clip
decoration : 컨테이너에 설정할 색상 등 옵션 → Decoration
Container(clipBehavior: Clip.hardEdge, …)  
BoxDecoration (This is not Widget) 색상과 둥근 정도 등을 조정한다 color : 박스 색상 → Color
borderRadius : 박스 테두리 둥근 정도 → BorderRadiusGeometry
BoxDecoration(borderRadius: BorderRadius.circular(25))  
Icon 아이콘 위젯 icon[생략 가능] : 사용할 아이콘 → IconData
color : 아이콘 색상 → Color
size : 아이콘 크기 → int
Icon( icon, color: isInverted ? _blackColor : Colors.white, size: 88, ),  
Transform.scale Child에 적은 위젯의 크기 및 색상 등을 조정한다 scale : 크기 → double
child : Scale 조절할 위젯 → Widget
Transform.scale( scale: 2.2, ... )  
Transform.translate Child에 적은 위젯의 위치를 이동시킨다(scale과 같이 적용시키는 경우 Transform.scale( … child : Transform.translate( .. child : 적용시킬 위젯) 순으로 적용시킨다 offset : 위치 → Offset
child : 회전 시킬 위젯 → Widget
Transform.translate( offset: const Offset(..))  
Offset 현재 위치 x, y축 이동 위젯 dx[생략 가능] : 현재 위치에서 x축 이동 → doubledy[생략 가능] : 현재 위치에서 y축 이동 → double Offset(-5,12)  
IconButton 버튼 이벤트가 있는 아이콘 위젯 icon : 아이콘 종류 → Icon
onPressed : 눌렀을 때 동작 시키기 → Function:void
iconSize : 아이콘 사이즈 → int
onPressed: onClicked  
ThemeData 공통 스타일을 적용시킬 Data를 설정한다 (이름은 이미 정해져있다 textTheme, focusColor 등..) → 결국 그냥 이름일 뿐이고 반환값만 신경쓰고 문맥에 따른 것만 잘 생각해서 정하면 된다 textTheme : 텍스트 테마 → TextTheme textTheme: const TextTheme(...)  
TextTheme 텍스트 스타일 적용시킬 Theme을 정한다 이름은 이미 정해져있다 titleLarge 등..) → 결국 그냥 이름일 뿐이고 반환값만 신경쓰고 문맥에 따른 것만 잘 생각해서 정하면 된다 titleLarge : → TextStyle textTheme: const TextTheme( titleLarge: TextStyle( color: Colors.red, ),  
Flexible Flex박스를 만든다 → 각각의 박스를 비율로 줄 수 있어서 어떤 핸드폰이든 %이기 때문에 깨지지 않는다 flex : 비율 → int
child : FlexBox에 넣을 위젯 → Widget
Flexible( flex: 1,…  
Expanded 사용 가능한 크기만큼 가져갑니다 (최대 화면의 크기) child : 확장된 Row안에 들어갈에 넣을 위젯 → Widget
flex : 나눌 비율 크기 → int
Expanded( child: Container(  
ListView 리스트의 형태로 데이터를 보여준다 → 많은 데이터 보여줄 때 사용 하지만… 모든 데이터를 가져오기 때문에 메모리 누수 현상 발생 가능성 존재      
ListView.builder 리스트 형태로 데이터를 보여주고 화면에 보이는 부분만 렌더링 하기 때문에 메모리 누수 걱정이 없다 scrollDirection : 리스트뷰 스크롤 방향 → ScrollWidget
itemCount : 전체 아이템 수 → int
padding : 패딩 옵션 → EdgeInsets
itemBuilder : 새로운 인덱스 감지시 itemBuilder함수를 호출해 렌더링할 위젯을 설정한다 추후 스크롤 시 안 보이는 부분은 사라지고 나중에 다시 보일 때 재활용하게 된다 → 일반적으로 itemBuilder : (context, index) 이런형식으로 이용된다
   
ListView.seperator ListView.builder와 동일하며 리스트 사이에 위젯을 넣을 수 있다 위와 동일seperatorBuilder : 리스트 사이에 들어갈 위젯 → 일반적으로 speratorBuilder : (BuildContext context, int index) ⇒ 위젯..    
Image.network 이미지를 가져온다      
CircularProgressIndicator 로딩바 X CircularProgressIndicator()  
SafeArea 와이파이, 배터리 등 위에 있는 부분을 제외한 위젯 박스 child : 모든 위젯 [일반적으로 Column, Container 등..] → Widget SafeArea(child: Column...)  
FractionallySizedBox 부모 위젯의 전체 크기를 비율로 가져가는 위젯 박스 widthFactor : 0.5 → double
heightFactor : 0.5 → double
child : 비율로 가져가는 하위 위젯 → Widget
FractionallySizedBox(widthFactor:0.5, heightFactor:0.5, child: Column…)  
Positioned.fill 화면을 꽉채울 수 있는 위젯 child : 화면에 꽉 채울 위젯 → Widget Positioned.fill(child:…)  
Stack 화면 위에 쌓을 수 있는 위젯 (absolute처럼 위젯 위에 올릴 수 있다) children : 쌓을 위젯들 → Widget[] Stack(children: […]) 박스 안에 [아이콘
Positioned 위젯을 상하좌우 값을 줘서 위치시킬 수 있다 left : left 위젯 위치 값 → int
right : right 위젯 위치 값 → int
top : top 위젯 위치 값 → int
bottom : bottom 위젯 위치 값 → int
Positioned(left:20,top:40,child:...)  
Wrap Wrap은 가로로 Children Widget들을 채우는데 더 이상 못 채우면 알아서 줄바꿈이 된다 runSpacing : 줄 간격 → int
spacing : 요소들 사이 가로 간격 → int
children : 적용시킬 위젯들 → Widget[]
Wrap(runSpacing: 15, spacing: 15, children: ...)  
TextField 인풋박스를 제공해준다. 유효성 검사 등 다양한 기능 제공     https://api.flutter.dev/flutter/material/TextField-class.html
TabBar 앱 하단에 3개나 5개 아이콘 등에 쓰이는 Tab들을 의미한다.     https://api.flutter.dev/flutter/material/TabBar-class.html
TabPageSelector 슬라이드해서 페이지를 넘어가는 탭들의 경우 현재 어떤 탭 위치에 있는지 알려주는 역할을 한다.     https://api.flutter.dev/flutter/material/TabPageSelector-class.html
InputDecoration TextField 또는 TextFormField와 같은 입력 필드를 꾸미는데 사용되는 위젯입니다.     https://api.flutter.dev/flutter/material/InputDecoration-class.html
UnderlineInputBorder TextField 또는 TextFormField의 밑줄 스타일로 설정할 때 사용됩니다.     https://api.flutter.dev/flutter/material/UnderlineInputBorder-class.html
BorderSide 경계선(border)의 색상, 두께 및 스타일을 정의하는 위젯입니다.     https://api.flutter.dev/flutter/painting/BorderSide-class.html
TabBarView 여러 페이지를 탭 전환을 통해 스와이프할 수 있는 뷰를 제공.     https://api.flutter.dev/flutter/material/TabBarView-class.html
AspectRatio 자식 위젯의 너비와 높이 비율을 일정하게 유지시켜주는 위젯입니다     https://api.flutter.dev/flutter/widgets/AspectRatio-class.html
TextFormField TextField와 비슷하지만 Form 위젯과 함께 사용됩니다.     https://api.flutter.dev/flutter/material/TextFormField-class.html
NavigationDestination 네비게이션에 보여줄 항목을 정의해주는 위젯입니다.     https://api.flutter.dev/flutter/material/NavigationDestination-class.html
PageView 수평 또는 수직으로 페이지를 스와이프할 수 있게 해주는 위젯입니다.     https://api.flutter.dev/flutter/widgets/PageView-class.html
FadeInImage.assetNetwork 이미지가 로드되는 동안 보여줄 Placeholder 이미지입니다. (로딩개념)     https://api.flutter.dev/flutter/widgets/FadeInImage/FadeInImage.assetNetwork.html
NavigationBar 3.0에서 새롭게 도입된 신개념으로 BottomNavigationBar보다 선호된다. TabBar의 경우 하단이 아니여도 붙일 수 있지만 NavigationBar의 경우는 하단에 붙는다.     https://api.flutter.dev/flutter/material/NavigationBar-class.html
ListTile List형식인데 알림으로 오는 형식처럼 앞뒤에 붙여서 리스트를 쉽게 만들수 있게 도와준다.     https://api.flutter.dev/flutter/material/ListTile-class.html
Dismissible List형식인데 왼쪽이나 오른쪽 슬라이드로 삭제하거나 할 수 있는 기능이다.     https://api.flutter.dev/flutter/widgets/Dismissible-class.html
GoRoute 라우팅, 중첩라우팅기능, 애니메이션전환 등을 제공한다.     https://pub.dev/packages/go_router
VerticalDivider 위젯들 사이에 수직선을 넣어 구분할 수 있는 위젯     https://api.flutter.dev/flutter/material/VerticalDivider-class.html
RichText 위젯을 배열로 가지기 때문에 TextSpan위젯을 사용시 첫글자는 다른 스타일 중간 글자는 다른 스타일을 넣기가 편하다     https://api.flutter.dev/flutter/widgets/RichText-class.html
DefaultTextStyle 텍스트 위젯을 여러 개 써야할 때 최상단 스타일 고정하고 여러 항목을 받을 수 있다.     https://api.flutter.dev/flutter/widgets/DefaultTextStyle-class.html
NestedScrollView 상단의 헤더 영역과 본문 영역이 서로 독립적인 스크롤을 처리하면서도, 상호작용하는 방식으로 동작하는 게 특징입니다. 예를 들어, 상단의 헤더가 먼저 스크롤되고, 그 이후에 본문 영역이 스크롤되는 방식입니다.     https://api.flutter.dev/flutter/widgets/NestedScrollView-class.html

 

 

모달창

// 모달창 (Yes / No) iOS
onTap: () {
  showCupertinoDialog(
    context: context,
    builder: (context) => CupertinoAlertDialog(
      // iOS 모달창
      title: const Text("Are you sure?"),
      content: const Text("Plx dont go"),
      actions: [
        CupertinoDialogAction(
          // iOS 모달 No 버튼
          onPressed: () => Navigator.of(context).pop(),
          child: const Text("No"),
        ),
        CupertinoDialogAction(
          // iOS 모달 Yes 버튼
          onPressed: () => Navigator.of(context).pop(),
          isDestructiveAction: true,
          child: const Text("Yes"),
        ),
      ],
    ),
  );
},

// 모달창 (Yes / No) AOS
onTap: () {
  showDialog(
    context: context,
    builder: (context) => AlertDialog(
      // AOS 모달창
      icon: const FaIcon(FontAwesomeIcons.skull),
      title: const Text("Are you sure?"), // 제목 내용
      content: const Text("Plx dont go"), // 제목 안에 내용
      actions: [
        IconButton(
          onPressed: () => Navigator.of(context).pop(),
          icon: const FaIcon(FontAwesomeIcons.car),
        ), // 왼쪽
        TextButton(
          onPressed: () => Navigator.of(context).pop(),
          child: const Text("Yes"),
        ), // 오른쪽
      ],
    ),
  );
}

// 모달창 하단형 (iOS)
onTap: () {
	showCupertinoModalPopup(
	  // 모달창 하단형
	  context: context,
	  builder: (context) => CupertinoActionSheet(
	    // 모달창 하단형에 들어갈 내용
	    title: const Text("Are you sure?"),
	    message: const Text("Please dooooont gooooo"),
	    actions: [
	      CupertinoActionSheetAction(
	        isDefaultAction: true, // OK (파란글씨)
	        onPressed: () => Navigator.of(context).pop(),
	        child: const Text("Not log out"),
	      ),
	      CupertinoActionSheetAction(
	        isDestructiveAction: true, // 취소 (빨간 글씨)
	        onPressed: () => Navigator.of(context).pop(),
	        child: const Text("Yes plz."), // 들어갈 문구
	      )
	    ],
	  ),
	);
},

 

스크롤 (sliver)

import 'package:flutter/material.dart';

class UserProfileScreen extends StatefulWidget {
  const UserProfileScreen({super.key});

  @override
  State createState() => _UserProfileScreenState();
}

class _UserProfileScreenState extends State {
  @override
  Widget build(BuildContext context) {
    return CustomScrollView(
      slivers: [ // 앱의 스크롤에따라 모양이 변하는 것들을 말한다
	      // <https://api.flutter.dev/flutter/material/SliverAppBar-class.html> 샌드박스 화면으로 보면 이해가 쉽다.
        SliverAppBar(
					snap:
              true, // floating가 ture일 때 동작하며 SliverFixedExtentList에 리스트들을 살짝 위로 스크롤하면 서서히 나오는게 아니라 한번에 짠 하고 나온다
          floating:
              true, // SliverFixedExtentList에 있는 리스트를  위로 스크롤할 때 올릴 때 해당 앱바가 출현한다(끝까지 올리지 않는 이상 안 나옴 일반적)
          stretch: true, // 해당 앱바를 아래로 당길 수 있냐 (그래서 늘어나냐 안 늘어나냐)
          pinned: true, // 내렸을 때 앱바를 고정시킬지 여부
          backgroundColor: Colors.teal, // 위로 당겼을 때 보여줄 것
          collapsedHeight: 80, // 높이 (언제부터 위에 bacground color teal을 실행시킬거냐에 대한)
          expandedHeight: 200
          flexibleSpace: FlexibleSpaceBar(
            stretchModes: const [
              StretchMode.blurBackground, // -> 아래로 당길시 뿌얘짐
              StretchMode.zoomBackground, // 밑에 선언한 백그라운드를 중심으로 당겨진다
              StretchMode.fadeTitle, // Title이 뿌얘진다
            ],
            background: Image.asset(
              "assets/images/placeholder.jpg",
              fit: BoxFit.cover,
            ),
            title: const Text("Hello!"),
          ),
        ),
        // 스크롤이 가능한 위젯 생성
				const SliverToBoxAdapter( 
          child: Column(
            children: [
              CircleAvatar(
                backgroundColor: Colors.red,
                radius: 20,
              ),
            ],
          ),
        ),
        // 스크롤이 가능한 리스트 위젯 생성
				SliverFixedExtentList(
          // Body값 (List)
          delegate: SliverChildBuilderDelegate(
            childCount: 50,
            (context, index) => Container(
              color: Colors.amber[100 * (index % 9)],
              child: Align(
                alignment: Alignment.center,
                child: Text("Item $index"),
              ),
            ),
          ),
          itemExtent: 100, // 높이 차이
        ),
				// SliverAppBar에 비해 사용자 정의가 더 자유롭다.
				SliverPersistentHeader(
          delegate: CustomDelegate(),
          pinned: true,
          floating: true,
        ),
        // 스크롤이 가능한 그리드 위젯 생성
				SliverGrid( 
          delegate: SliverChildBuilderDelegate(
            childCount: 50,
            (context, index) => Container(
              color: Colors.blue[100 * (index % 9)],
              child: Align(
                alignment: Alignment.center,
                child: Text("Item $index"),
              ),
            ),
          ),
          gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
            maxCrossAxisExtent: 50, // 정사각형 크기
            mainAxisSpacing: Sizes.size20, // 세로 마진
            crossAxisSpacing: Sizes.size20, // 가로 마진
            childAspectRatio: 1, // 정사각형들 비율
          ),
        )
      ],
    );
  }
}

// SliverPersistentHeader에 사용하기 위해 따로 정의 필요
class CustomDelegate extends SliverPersistentHeaderDelegate {
  @override
  Widget build(
    BuildContext context,
    double shrinkOffset,
    bool overlapsContent,
  ) {
    return Container(
      color: Colors.indigo,
      child: const FractionallySizedBox(
        heightFactor: 1, // 차지하는 높이 퍼센티지
        child: Center(
          child: Text(
            'Title!!!!!',
            style: TextStyle(
              color: Colors.white,
            ),
          ),
        ),
      ),
    );
  }

  @override
  double get maxExtent => 150; // 높이

  @override
  double get minExtent => 120; // pin사용시 유지되는 높이 영역

  @override
  bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) {
    return false; // 필요 시 다시 빌드 여부
  }
}

Sliver는 Scroll안에 일반적으로 사용되며 스크롤함에 따라 보여지는 게 다른 걸 의미한다.

 

에러 위젯

void showFirebaseErrorSnack(
  // 하단에 에러 메세지 알림 처럼 튀어나옴
  BuildContext context,
  Object? error,
) {
  ScaffoldMessenger.of(context).showSnackBar(
    SnackBar(
      showCloseIcon: true, // 닫기 버튼
      content: Text( // 보여줄 메시지
        (error as FirebaseException).message ?? "Something wen't wrong.",
      ),
    ),
  );
}

 

 

📝그 외 위젯

Widget 명 역할 키 : 설명 → 값 사용 예) 활용 예제

Widget 명 역할 키 : 설명 → 값 사용 예) 활용 예제
FutureBuilder Http통신 후에 데이터 정보를 담는 위젯 future : Future타입의 데이터 → Future<T>
builder : ****future에서 받은 데이터 정보 → AsyncWidgetBuilder -> 일반적으로 (context, snapshot) 사용
   
GestureDetector 동작 이벤트를 제공해준다 onTap : 탭 눌렀을 때 → funciton
onPanUpdate : 드래깅 감지 → function(DragUpdateDetails details)
onPanEnd : 드래깅 끝냈을 때 → function(DragEndDetails details)
- onPageUpdatedetails.delta.dx > 0 → 오른쪽에서 왼쪽으로 스왑  
Navigator.push 데이터를 다른 위젯으로 보내고 Router로 해당 위젯을 띄운다 (뒤로가기가 쌓인다) 일반적으로 **(context, Router)**받고 위젯을 Router 객체로 변환하기 위해서MaterialPageRoute를 사용한다 Navigator.of(context).push(MaterialPageRoute(builder: (context) => const LoginScreen(),),);  
Navigator.pop 뒤로가기   Navigator.*of*(context).pop()  
MaterialPageRoute 위젯을 Router형태로 바꾸는 역할 builder : 어떤 context를 읽을 지 → buildContext
fullscreenDialog : 페이지 이동 효과 → boolean
MaterialPageRoute( builder: (context) => DetailScreen( title: title, thumb: thumb, id: id, ) → [DetailScreen 위젯을 Router로]  

 

 

📝애니메이션 위젯

Widget 명 역할 키 : 설명 → 값 사용 예) 활용 예제

Widget 명 역할 키 : 설명 → 값 사용 예) 활용 예제
Hero 페이지 Route 애니메이션 효과 (히어로 처럼 슝 나옴) 필요하면 알아서 사용하세요   https://docs.flutter.dev/ui/animations/hero-animations
AnimatedOpacity 불투명 애니메이션 위젯 opacity : 불투명도 → double (0~1)
duration : 불투명도 애니메이션 적용 시간 → Duration
child : 적용시킬 위젯 → Widget
AnimatedOpacity(opacity: 1,duration: const Duration(milliseconds: 300), child: …)  
AnimatedCrossFade FadeIn FadeOut 전환 애니메이션 위젯 firstChild : 위젯 → Widget
secondChild : 위젯 → Widget
duration : 애니메이션 적용 시간 → Duration
crossFadeState : 보여줄 위젯 → CrossFadeState.showFirst, CrossFadeState.showSecond
AnimatedCrossFade(fistChild:…, secondChild:…, duration:…, crossFadeState: …)  
AnimatedBuilder 상태 변화를 감지해 화면을 업데이트할 수 있습니다. (컨트롤러로 로직처리를 하며 Builder를 통해 그려줍니다.     https://api.flutter.dev/flutter/widgets/AnimatedBuilder-class.html
AnimatedDefaultTextStyle 텍스트에 애니메이션을 줄수 있는 위젯입니다.     https://api.flutter.dev/flutter/widgets/AnimatedDefaultTextStyle-class.html
SizeTransition 위젯의 크기(가로 또는 세로 방향)를 애니메이션을 통해 변환하는 위젯입니다.     https://api.flutter.dev/flutter/widgets/SizeTransition-class.html
SlideTransition 슬라이드하는 애니메이션을 적용한 위젯입니다.     https://api.flutter.dev/flutter/widgets/SlideTransition-class.html
AnimatedModalBarrier 모달로서 화면을 가리는 반투명한 배경(Barrier) 위젯을 만듭니다.     https://api.flutter.dev/flutter/widgets/AnimatedModalBarrier-class.html
AnimatedList 리스트의 항목들이 동적으로 추가되거나 제거될 때 애니메이션을 적용할 수 있는 위젯     https://api.flutter.dev/flutter/widgets/AnimatedList-class.html
ColorTween 애니메이션 전환시 색상의 변화되는 과정을 애니메이션으로 보여줍니다.     https://api.flutter.dev/flutter/animation/ColorTween-class.html
Tween 시작과 종료값을 줄 수 있는 애니메이션기능을 줍니다.     https://api.flutter.dev/flutter/animation/Tween-class.html

 

📝Flutter 개발 팁

 

 

 

 

반응형