Flutter 앱 개발일지

Shazam 클론 코딩

우제혁 2023. 1. 9. 15:48

목표

기능은 제외하고 주어진 화면 만들어 보기

 

이미지

 

작동 영상

 

 

사전 준비

주어진 코드

더보기
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Shazam',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      initialIndex: 1,
      length: 3,
      child: Builder(builder: (context) {
        DefaultTabController.of(context)?.addListener(() {
          setState(() {});
        });

        return Scaffold(
          body: Stack(
            children: [
              TabBarView(
                children: [
                  FirstTab(),
                  SecondTab(),
                  ThirdTab(),
                ],
              ),
              SafeArea(
                child: Padding(
                  padding:
                      const EdgeInsets.symmetric(vertical: 20, horizontal: 16),
                  child: Column(
                    children: [
                      Container(
                        alignment: Alignment.topCenter,
                        child: TabPageSelector(
                          color: DefaultTabController.of(context)?.index == 1
                              ? Colors.blue[300]
                              : Colors.grey[400],
                          selectedColor:
                              DefaultTabController.of(context)?.index == 1
                                  ? Colors.white
                                  : Colors.blue,
                          indicatorSize: 8,
                        ),
                      ),
                    ],
                  ),
                ),
              ),
            ],
          ),
        );
      }),
    );
  }
}

// 첫번째 페이지
class FirstTab extends StatelessWidget {
  const FirstTab({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    const songs = [
      {
        'imageUrl': 'https://i.ytimg.com/vi/jAO0KXRdz_4/hqdefault.jpg',
        'title': '가을밤에 든 생각',
        'artist': '잔나비',
      },
      {
        'imageUrl': 'https://i.ytimg.com/vi/jAO0KXRdz_4/hqdefault.jpg',
        'title': '가을밤에 든 생각',
        'artist': '잔나비',
      },
      {
        'imageUrl': 'https://i.ytimg.com/vi/jAO0KXRdz_4/hqdefault.jpg',
        'title': '가을밤에 든 생각',
        'artist': '잔나비',
      },
      {
        'imageUrl': 'https://i.ytimg.com/vi/jAO0KXRdz_4/hqdefault.jpg',
        'title': '가을밤에 든 생각',
        'artist': '잔나비',
      },
      {
        'imageUrl': 'https://i.ytimg.com/vi/jAO0KXRdz_4/hqdefault.jpg',
        'title': '가을밤에 든 생각',
        'artist': '잔나비',
      },
      {
        'imageUrl': 'https://i.ytimg.com/vi/jAO0KXRdz_4/hqdefault.jpg',
        'title': '가을밤에 든 생각',
        'artist': '잔나비',
      },
    ];

    return Center(child: Text('첫번째 페이지'));
  }
}

// 두번째 페이지
class SecondTab extends StatelessWidget {
  const SecondTab({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Center(child: Text('두번째 페이지'));
  }
}

// 세번째 페이지
class ThirdTab extends StatelessWidget {
  const ThirdTab({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    const chartData = {
      'korea': [
        {
          'imageUrl': 'https://i.ibb.co/xf2HpfG/dynamite.jpg',
          'name': 'Dynamite',
          'artist': 'BTS',
        },
        {
          'imageUrl': 'https://i.ibb.co/xf2HpfG/dynamite.jpg',
          'name': 'Dynamite',
          'artist': 'BTS',
        },
        {
          'imageUrl': 'https://i.ibb.co/xf2HpfG/dynamite.jpg',
          'name': 'Dynamite',
          'artist': 'BTS',
        },
      ],
      'global': [
        {
          'imageUrl': 'https://i.ibb.co/xf2HpfG/dynamite.jpg',
          'name': 'Dynamite',
          'artist': 'BTS',
        },
        {
          'imageUrl': 'https://i.ibb.co/xf2HpfG/dynamite.jpg',
          'name': 'Dynamite',
          'artist': 'BTS',
        },
        {
          'imageUrl': 'https://i.ibb.co/xf2HpfG/dynamite.jpg',
          'name': 'Dynamite',
          'artist': 'BTS',
        },
      ],
      'newyork': [
        {
          'imageUrl': 'https://i.ibb.co/xf2HpfG/dynamite.jpg',
          'name': 'Dynamite',
          'artist': 'BTS',
        },
        {
          'imageUrl': 'https://i.ibb.co/xf2HpfG/dynamite.jpg',
          'name': 'Dynamite',
          'artist': 'BTS',
        },
        {
          'imageUrl': 'https://i.ibb.co/xf2HpfG/dynamite.jpg',
          'name': 'Dynamite',
          'artist': 'BTS',
        },
      ],
    };

    return Center(child: Text('세번째 페이지'));
  }
}

 

완성 코드

더보기
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Shazam',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      initialIndex: 1,
      length: 3,
      child: Builder(builder: (context) {
        DefaultTabController.of(context)?.addListener(() {
          setState(() {});
        });

        return Scaffold(
          body: Stack(
            children: [
              TabBarView(
                children: [
                  FirstTab(),
                  SecondTab(),
                  ThirdTab(),
                ],
              ),
              SafeArea(
                child: Padding(
                  padding:
                      const EdgeInsets.symmetric(vertical: 20, horizontal: 16),
                  child: Column(
                    children: [
                      Container(
                        alignment: Alignment.topCenter,
                        child: TabPageSelector(
                          color: DefaultTabController.of(context)?.index == 1
                              ? Colors.blue[300]
                              : Colors.grey[400],
                          selectedColor:
                              DefaultTabController.of(context)?.index == 1
                                  ? Colors.white
                                  : Colors.blue,
                          indicatorSize: 8,
                        ),
                      ),
                    ],
                  ),
                ),
              ),
            ],
          ),
        );
      }),
    );
  }
}

// 첫번째 페이지
class FirstTab extends StatelessWidget {
  const FirstTab({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    const songs = [
      {
        'imageUrl': 'https://i.ytimg.com/vi/jAO0KXRdz_4/hqdefault.jpg',
        'title': '가을밤에 든 생각',
        'artist': '잔나비',
      },
      {
        'imageUrl': 'https://i.ytimg.com/vi/jAO0KXRdz_4/hqdefault.jpg',
        'title': '가을밤에 든 생각',
        'artist': '잔나비',
      },
      {
        'imageUrl': 'https://i.ytimg.com/vi/jAO0KXRdz_4/hqdefault.jpg',
        'title': '가을밤에 든 생각',
        'artist': '잔나비',
      },
      {
        'imageUrl': 'https://i.ytimg.com/vi/jAO0KXRdz_4/hqdefault.jpg',
        'title': '가을밤에 든 생각',
        'artist': '잔나비',
      },
      {
        'imageUrl': 'https://i.ytimg.com/vi/jAO0KXRdz_4/hqdefault.jpg',
        'title': '가을밤에 든 생각',
        'artist': '잔나비',
      },
      {
        'imageUrl': 'https://i.ytimg.com/vi/jAO0KXRdz_4/hqdefault.jpg',
        'title': '가을밤에 든 생각',
        'artist': '잔나비',
      },
    ];

    return SafeArea(
        child: Padding(
      padding: const EdgeInsets.symmetric(horizontal: 16),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Row(
            crossAxisAlignment: CrossAxisAlignment.start,
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              Padding(
                padding: const EdgeInsets.only(top: 8.0),
                child: Icon(Icons.settings),
              ),
              Text(
                "라이브러리",
                style: TextStyle(
                  fontSize: 18,
                  fontWeight: FontWeight.bold,
                ),
              ),
              Icon(null),
            ],
          ),
          SizedBox(
            height: 8,
          ),
          //Shazam
          Padding(
            padding: const EdgeInsets.symmetric(vertical: 8.0),
            child: Row(
              children: [
                ImageIcon(
                  NetworkImage("https://i.ibb.co/hxNbZ8p/shazam.png"),
                  size: 18,
                ),
                SizedBox(
                  width: 12,
                ),
                Text(
                  "Shazam",
                  style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600),
                ),
              ],
            ),
          ),
          Divider(),
          //아티스트
          Padding(
            padding: const EdgeInsets.symmetric(vertical: 8.0),
            child: Row(
              children: [
                Icon(Icons.person_rounded),
                SizedBox(
                  width: 12,
                ),
                Text(
                  "아티스트",
                  style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600),
                ),
              ],
            ),
          ),
          Divider(),
          //회원님을 위한 재생 목록
          Padding(
            padding: const EdgeInsets.symmetric(vertical: 8.0),
            child: Row(
              children: [
                Icon(Icons.music_note),
                SizedBox(
                  width: 12,
                ),
                Text(
                  "회원님을 위한 재생 목록",
                  style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600),
                ),
              ],
            ),
          ),
          Divider(),
          SizedBox(height: 16),
          Text("최근 Shazam",
              style: TextStyle(
                fontSize: 18,
                fontWeight: FontWeight.w600,
              )),
          SizedBox(height: 16),
          Expanded(
            child: GridView.builder(
              gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: 2,
                childAspectRatio: 3 / 5,
              ),
              itemCount: songs.length,
              itemBuilder: (context, index) {
                var song = songs[index];
                String title = song['title']!;
                String artist = song['artist']!;
                String imageUrl = song['imageUrl']!;

                return Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: Container(
                    margin: EdgeInsets.all(4),
                    decoration: BoxDecoration(
                        color: Colors.white,
                        borderRadius: BorderRadius.all(
                          Radius.circular(8),
                        ),
                        boxShadow: [
                          BoxShadow(
                            color: Colors.grey.withOpacity(0.5),
                            blurRadius: 1,
                            spreadRadius: 1,
                          )
                        ]),
                    child: Column(children: [
                      ClipRRect(
                        borderRadius: BorderRadius.only(
                          topLeft: Radius.circular(8),
                          topRight: Radius.circular(8),
                        ),
                        child: Image.network(
                          imageUrl,
                          fit: BoxFit.cover,
                          height: MediaQuery.of(context).size.width *
                              0.5 *
                              (5 / 3) *
                              0.55,
                        ),
                      ),
                      Expanded(
                        child: Padding(
                          padding: const EdgeInsets.all(8.0),
                          child: Container(
                            width: double.infinity,
                            child: Column(
                              crossAxisAlignment: CrossAxisAlignment.start,
                              children: [
                                Text(title,
                                    style: TextStyle(
                                      fontSize: 18,
                                      fontWeight: FontWeight.bold,
                                    )),
                                Text(artist,
                                    style: TextStyle(
                                      fontSize: 14,
                                      fontWeight: FontWeight.bold,
                                      color: Colors.grey,
                                    )),
                                Spacer(),
                                Image.network(
                                  "https://i.ibb.co/KG9m5QS/applemusic.png",
                                  width: 60,
                                )
                              ],
                            ),
                          ),
                        ),
                      ),
                    ]),
                  ),
                );
              },
            ),
          )
        ],
      ),
    ));
  }
}

// 두번째 페이지
class SecondTab extends StatelessWidget {
  const SecondTab({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(
        gradient: LinearGradient(
          begin: Alignment.topCenter,
          end: Alignment.bottomCenter,
          colors: [Colors.blue[300]!, Colors.blue[900]!],
        ),
      ),
      child: SafeArea(
        child: Column(
          children: [
            Padding(
              padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
              child: Row(
                children: [
                  GestureDetector(
                    onTap: () {
                      DefaultTabController.of(context)!.animateTo(0);
                    },
                    child: Column(
                      children: [
                        Icon(
                          Icons.person,
                          color: Colors.white,
                        ),
                        Text(
                          "라이브러리",
                          style: TextStyle(color: Colors.white),
                        )
                      ],
                    ),
                  ),
                  Spacer(),
                  GestureDetector(
                    onTap: () {
                      DefaultTabController.of(context)!.animateTo(2);
                    },
                    child: Column(
                      children: [
                        Icon(
                          Icons.show_chart,
                          color: Colors.white,
                        ),
                        Text(
                          "차트",
                          style: TextStyle(color: Colors.white),
                        )
                      ],
                    ),
                  ),
                ],
              ),
            ),
            Padding(
              padding: const EdgeInsets.only(top: 60),
              child: Text(
                "Shazam하려면 탭하세요",
                style: TextStyle(
                  color: Colors.white,
                  fontSize: 28,
                  fontWeight: FontWeight.bold,
                ),
              ),
            ),
            Padding(
              padding: const EdgeInsets.only(top: 80),
              child: Container(
                alignment: Alignment.center,
                width: 200,
                height: 200,
                decoration: BoxDecoration(
                  color: Colors.blue[300],
                  shape: BoxShape.circle,
                ),
                child: Image.network(
                  "https://i.ibb.co/hxNbZ8p/shazam.png",
                  color: Colors.white,
                  width: 150,
                  height: 150,
                ),
              ),
            ),
            Padding(
              padding: const EdgeInsets.only(top: 90),
              child: Container(
                alignment: Alignment.center,
                width: 40,
                height: 40,
                decoration: BoxDecoration(
                  color: Colors.blue[400],
                  shape: BoxShape.circle,
                ),
                child: Icon(
                  Icons.search,
                  color: Colors.white,
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

// 세번째 페이지
class ThirdTab extends StatelessWidget {
  const ThirdTab({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    const chartData = {
      'korea': [
        {
          'imageUrl': 'https://i.ibb.co/xf2HpfG/dynamite.jpg',
          'name': 'Dynamite',
          'artist': 'BTS',
        },
        {
          'imageUrl': 'https://i.ibb.co/xf2HpfG/dynamite.jpg',
          'name': 'Dynamite',
          'artist': 'BTS',
        },
        {
          'imageUrl': 'https://i.ibb.co/xf2HpfG/dynamite.jpg',
          'name': 'Dynamite',
          'artist': 'BTS',
        },
      ],
      'global': [
        {
          'imageUrl': 'https://i.ibb.co/xf2HpfG/dynamite.jpg',
          'name': 'Dynamite',
          'artist': 'BTS',
        },
        {
          'imageUrl': 'https://i.ibb.co/xf2HpfG/dynamite.jpg',
          'name': 'Dynamite',
          'artist': 'BTS',
        },
        {
          'imageUrl': 'https://i.ibb.co/xf2HpfG/dynamite.jpg',
          'name': 'Dynamite',
          'artist': 'BTS',
        },
      ],
      'newyork': [
        {
          'imageUrl': 'https://i.ibb.co/xf2HpfG/dynamite.jpg',
          'name': 'Dynamite',
          'artist': 'BTS',
        },
        {
          'imageUrl': 'https://i.ibb.co/xf2HpfG/dynamite.jpg',
          'name': 'Dynamite',
          'artist': 'BTS',
        },
        {
          'imageUrl': 'https://i.ibb.co/xf2HpfG/dynamite.jpg',
          'name': 'Dynamite',
          'artist': 'BTS',
        },
      ],
    };

    return SafeArea(
        child: Column(
      children: [
        Text(
          "차트",
          style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
        ),
        SizedBox(height: 18),
        Expanded(
          child: ListView(
            children: [
              Stack(
                alignment: Alignment.center,
                children: [
                  Container(
                    color: Colors.purple[900],
                    width: double.infinity,
                    height: 180,
                  ),
                  Column(
                    children: [
                      Container(
                        width: MediaQuery.of(context).size.width * 0.8,
                        child: ElevatedButton(
                          onPressed: () {},
                          style: ButtonStyle(
                              backgroundColor:
                                  MaterialStateProperty.all(Colors.white)),
                          child: Text(
                            "국가 및 도시별 차트",
                            style: TextStyle(
                                color: Colors.purple[900],
                                fontWeight: FontWeight.bold),
                          ),
                        ),
                      ),
                      SizedBox(
                        height: 8,
                      ),
                      Text(
                        "전 세계",
                        style: TextStyle(
                            color: Colors.white, fontWeight: FontWeight.bold),
                      )
                    ],
                  ),
                ],
              ),
              //대한민국
              Container(
                width: double.infinity,
                height: 8,
                color: Colors.grey,
              ),
              Padding(
                padding: const EdgeInsets.all(8.0),
                child: Row(
                  children: [
                    Text("대한민국 차트"),
                    Spacer(),
                    Text(
                      "모두 보기",
                      style: TextStyle(color: Colors.blue),
                    ),
                  ],
                ),
              ),
              Row(
                children: chartData['korea']!.map((element) {
                  String imageUrl = element['imageUrl']!;
                  String name = element['name']!;
                  String artist = element['artist']!;
                  return Padding(
                    padding: const EdgeInsets.all(8.0),
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        Image.network(
                          imageUrl,
                          width: MediaQuery.of(context).size.width * 0.29,
                        ),
                        Text(name),
                        Text(artist),
                      ],
                    ),
                  );
                }).toList(),
              ),
              //글로벌
              Container(
                width: double.infinity,
                height: 8,
                color: Colors.grey,
              ),
              Padding(
                padding: const EdgeInsets.all(8.0),
                child: Row(
                  children: [
                    Text("글로벌 차트"),
                    Spacer(),
                    Text(
                      "모두 보기",
                      style: TextStyle(color: Colors.blue),
                    ),
                  ],
                ),
              ),
              Row(
                children: chartData['global']!.map((element) {
                  String imageUrl = element['imageUrl']!;
                  String name = element['name']!;
                  String artist = element['artist']!;
                  return Padding(
                    padding: const EdgeInsets.all(8.0),
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        Image.network(
                          imageUrl,
                          width: MediaQuery.of(context).size.width * 0.29,
                        ),
                        Text(name),
                        Text(artist),
                      ],
                    ),
                  );
                }).toList(),
              ),
              // 뉴욕 차트
              Container(
                width: double.infinity,
                height: 8,
                color: Colors.grey,
              ),
              Padding(
                padding: const EdgeInsets.all(8.0),
                child: Row(
                  children: [
                    Text("뉴욕 차트"),
                    Spacer(),
                    Text(
                      "모두 보기",
                      style: TextStyle(color: Colors.blue),
                    ),
                  ],
                ),
              ),
              Row(
                children: chartData['newyork']!.map((element) {
                  String imageUrl = element['imageUrl']!;
                  String name = element['name']!;
                  String artist = element['artist']!;
                  return Padding(
                    padding: const EdgeInsets.all(8.0),
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        Image.network(
                          imageUrl,
                          width: MediaQuery.of(context).size.width * 0.29,
                        ),
                        Text(name),
                        Text(artist),
                      ],
                    ),
                  );
                }).toList(),
              )
            ],
          ),
        )
      ],
    ));
  }
}

 

2주차

  • Row
    가로로 요소를 삽입할때 쓰임
  • leadingWidth: 100
    오버플로우 나지않게 해주는 역할을 해줌
  • 화면 구성 방법
    column과 row와 stack을 이용하여 화면 구성
  • overflow: TextOverflow.ellipsis
    오버플로우를 막아줘야하지만 최종적으로 Expanded로 감싸줘야한다 이는 Refactor -> widget으로 만든뒤 이름을 변경하자
  • Alignment 방법
    Flutter 가로 세로 정렬
  • SizedBox(width: 12)
    패딩처럼 공간 띄우기
  • Spacer()
    공간 최대한 차지하고 나머지 위젯 밀어내는 역할
  • GestureDetector
    위젯을 버튼처럼
  • floatingActionButton: FloatingActionButton
    눌리는 아이콘 만듥;

 

  • 간단한 조건문 만들기
isFavorite
                              ? CupertinoIcons.heart_fill
                              : CupertinoIcons.heart,
                          color: isFavorite ? Colors.pink : Colors.black,

?뒤는 참일때

:뒤는 거짓일때

 

 

  • padding 방법
    padding: const EdgeInsets.symmetric(horizontal: 16),
    symmetric 대칭형으로
    horizontal 가로방향만
  • statefullwidget
    사용할때는 2개의 클래스를 사용하는데 위의 클래스 변수를 아래 클래스에서 사용하고 싶을떄는 widget.imageUrl 처럼 widget을 붙이면 된다.

사진의 required this.imageUrl은 imageUrl인자를 꼭 받겠다는 문장이다.

 

  • final의 의미
    final도 const와 마찬가지로 처음 선언한 값을 변경할 수 없다.

    하지만 다른점은 const의경우 선언부에서 무조건 값을 부여해주어야하지만 final은 선언 시 값을 부여하지 않고 이후 최초 1번 값을 부여해줄 수 있다.

    그래서 선언부의 코드만으로는 컴파일러가 해당 변수의 값을 알 수 없기 때문에 const처럼 앱 실행 시 메모리에 넣어 관리할 수 없다.

 

 

 


  • Scaffold 사용
    Scaffold는 appbar 와 body를 사용할떄 사용하는것이므로 appbar를 안쓸거면 scaffold를 사용안해도 된다

 

  • 그라데이션 넣는 방법
decoration: BoxDecoration(
        gradient: LinearGradient(
          begin: Alignment.topCenter,
          end: Alignment.bottomCenter,
          colors: [Colors.blue[300]!, Colors.blue[900]!],
        ),
      ),

blue[300]! 뒤에 !오는 이유는 널값이 들어갈수도있어서 오류 방지를 위해 넣는다.

 

 

  • mainAxisAlignment: MainAxisAlignment.spaceBetween

아이콘을 양쪽으로 붙게 하는 기능

→ 두 아이콘 사이에 Spacer(), 함수를 넣어도 가능

 

 

  • SafeArea

너무 처음부터 붙지않게 약간의 공간을 만들어 준다.(아이폰의위와 아래 부분에 가리지 않게 해줌)

 

 

  • IconButton사용시 아이콘하고 text의 공간이 생기는데 이를 해결하는 방법

 

GestureDetector(
                  onTap: () {
                    DefaultTabController.of(context)!.animateTo(0);
                  },

IconButton 대신 GestureDetector로 column을 묶어줘서 클릭 이벤트로 만들면 된다.

 

 

  • GestureDetector의 사용법

InkWell과 매우 비슷합니다. 둘다 클릭시 동작하게 만들어줌

InkWell과의 차이점은 사용자의 동작을 감지 시 별도의 애니메이션 효과가 없습니다.

 

  • 패딩 세부사항 조절

padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16)

padding: const EdgeInsets.only(top: 100),

 

 

  • 공간 여백을 퍼센트로 주는 방법

SizedBox(height: MediaQuery.of(context).size.height * 0.1),

10퍼센트만큼 차지하게 만들기.

 

  • 그림 뒤에 여백 주기

stack사용 뿐만이 라니라 container 자체에 색을 넣어주는것도 가능

Container(
              alignment: Alignment.center,
              width: 200,
              height: 200,
              decoration: BoxDecoration(
                color: Colors.blue[300],
                shape: BoxShape.circle,
              ),
              child: Image.network(
                "<https://i.ibb.co/hxNbZ8p/shazam.png>",
                color: Colors.white,
                width: 150,
                height: 150,
              ),

 

+참고로

alignment: Alignment.center, 정렬을 해줘야 크기가 적용됨 그이유는 어떤 걸 기준으로 크기를 조절해야할지 모르기 때문에 정렬을 해줘야 적용이 되는것

 

비포 에프터

 

  • ListView 오류

크기를 정해주지 않아서 오류가 나는데 이는 ListView를 Expanded로 refacture 해주면 된다 감싸주면 된다.

 

  • color: Colors.purple[900],

색 뒤에 [900] 해줌으로 색을 조절할수있다 높을수록 찐하게

 

  • 크기 설정

width: double.infinity,

끝까지 채우는 것

 

  • 버튼의 색을 바꾸는 방법

+파란 줄 오류가 뜨는 이유는 child는 맨 뒤에 있어야한다고 해서 style 뒤로 옮기면 해결 된다.

 

 

  • 버튼의 가로 넓이 조절
Container(
                        width: MediaQuery.of(context).size.width * 0.8,
                        child: ElevatedButton(

버튼을 container로 묶어준뒤 넓이 설정

 

 

  • list요소 나타내기

위처럼

요소들을 나타내고 싶을때

row로 묶은뒤 chartData['korea']로 접근을 하는데

이를 .map으로 나타낼수있다.

chartData['korea'].map((element) {
                    return Text("he");
                  })

이때 return하는것 자체를 list로 만들어야하는데 이는 .list를 붙여서

chartData['korea'].map((element) {
                    return Text("he");
                  }).toList(),

이렇게 만들어야하는데

row에는 children: []로 이미 list가 있으므로

Row(
                children: chartData['korea'].map((element) {
                  return Text("he");
                }).toList(),
              )

이런식으로 []를 벗겨준다

이때

이처럼 map에 오류가 뜨는데 이는 chartData['korea']에 null값이 올수 있으니까 오류가 뜨는건데

children: chartData['korea']!.map((element) {
                  return Text("he");
                }).toList(),

이런식으로 null값이 아니라는

chartData['korea']!.map((element) !를 붙여주면 해결된다.

+추가 활용

Row(
                children: chartData['newyork']!.map((element) {
                  String imageUrl = element['imageUrl']!;
                  String name = element['name']!;
                  String artist = element['artist']!;
                  return Padding(
                    padding: const EdgeInsets.all(8.0),
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        Image.network(
                          imageUrl,
                          width: MediaQuery.of(context).size.width * 0.29,
                        ),
                        Text(name),
                        Text(artist),
                      ],
                    ),
                  );
                }).toList(),
              )

요소를 변수로 지정해주고 지정해줄때는 !와 ; 를 잘 넣어야한다

 

 

 

  • 3가지 요소 및 2가지 요소 정렬

Icon(null)값과 mainAxisAlignment: MainAxisAlignment.spaceBetween으로 정렬을 하는 방법도 있음

Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            Icon(Icons.settings),
            Text(
              "라이브러리",
              style: TextStyle(
                fontSize: 18,
                fontWeight: FontWeight.bold,
              ),
            ),
            Icon(null),
          ],
        )

여기서 아이콘만 살짝 밑으로 내리려 할때 주로 padding을 주는데 이때 라이브러리란 텍스트도 같이 내려오게 된다

이러한 이유는 기본 정렬이 center이므로 같이 정렬이 되는건데 이를

crossAxisAlignment: CrossAxisAlignment.start, 로 맞춰주면 해결이 된다.

Row(
          crossAxisAlignment: CrossAxisAlignment.start,
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            Padding(
              padding: const EdgeInsets.only(top: 8.0),
              child: Icon(Icons.settings),
            ),
            Text(
              "라이브러리",
              style: TextStyle(
                fontSize: 18,
                fontWeight: FontWeight.bold,
              ),
            ),
            Icon(null),
          ],
        )

 

 

  • 이미지 아이콘화
ImageIcon(
              NetworkImage("<https://i.ibb.co/hxNbZ8p/shazam.png>"),
              size: 18,
            ),
  • 폰트 bold 세세한 설정

fontWeight: FontWeight.w600

 

  • GridView.builder 사용

사용할때 공간지정오류를 위해 expanded로 묶어줘야한다.

Expanded(
            child: GridView.builder(
              gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: 2,
                childAspectRatio: 3 / 5,
              ),
              itemCount: songs.length,
              itemBuilder: (context, index) {
                return Text("D");
              },
            ),
          )

기본적인 요소들이며 분석해보면

gridDelegate: SliverGridDelegateWithFixedCrossAxisCount는 스타일을 정하는것이며

 

crossAxisCount는 가로로 몇개를 표시할지

 

childAspectRatio: 3 / 5,는 세로로 길이를 조절

itemCount: songs.length,
              itemBuilder: (context, index) {
                return Text("D");
              },

밑은 요소를 나타내는데 사용된다.

return Container(
                  decoration: BoxDecoration(
                      color: Colors.blue,
                      borderRadius: BorderRadius.all(
                        Radius.circular(8),
                      )),
                );

여기서

borderRadius: BorderRadius.all(
                        Radius.circular(8),
                      )

이부분은 테두리를 둥글게 만드는 옵션이다.

 

+이미지를 넣을때 이미지도 같이 둥글게 만들려면

ClipRRect(
                          borderRadius: BorderRadius.only(
                            topLeft: Radius.circular(8),
                            topRight: Radius.circular(8),
                          ),
                          child: Image.network(imageUrl),
                        ),

cliprrect을 사용해서 한번더 해준다.

 

그림자

Boxdecoration안에

boxShadow: [
                        BoxShadow(
                          color: Colors.grey.withOpacity(0.5),
                          blurRadius: 1,
                          spreadRadius: 1,
                        )
                      ]

 

이렇게 넣어주는데

이렇게 그림자로 정해준 회색이 밑에도 차지해버리므로 컨테이너 자체 색을 흰색으로 정해줘야한다.