seong

Flutter 간단한 프로필 앱 , 아이콘을 버튼화, 모서리 둥글게 본문

Flutter/Flutter

Flutter 간단한 프로필 앱 , 아이콘을 버튼화, 모서리 둥글게

hyeonseong 2022. 8. 12. 17:50

사진, 아이콘의 모서리를 강제로 둥글게 만들기 - ClipRRect

원하는 위젯을 ClipRRect로 감싸주고 borderRadius속성 주면 된다. 

Widget _buildHeaderPic() {
    return Padding(
      padding: const EdgeInsets.all(16.0),
      child: ClipRRect(
        borderRadius: BorderRadius.circular(5),
        child: AspectRatio(
          aspectRatio: 5 / 3,
          child: Image.asset(
            selectePic[selectedId],
            fit: BoxFit.cover,
          ),
        ),
      ),
    );
  }

아이콘을 버튼화 시키는 방법

Inkwell로 감싸고, onTap로 행위를 주면 된다.

사용될 컴포넌트들 모두 생성 및 정리 

main.dart(실행파일)

import 'package:flutter/material.dart';
import 'package:flutter_profile/pages/profile_page.dart';
import 'package:flutter_profile/theme.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: theme(),
      home: ProfilePage(),
    );
  }
}

theme.dart 앱의 기본적인 테마들을 작성하는 파일 

MaterialColor을 따로 작성해준 이유는 primary컬러로 white,black등 작성이 되지 않는다.

그럴때는 MaterialColor로 작성해주면 된다.

import 'package:flutter/material.dart';

const MaterialColor primaryWhite = MaterialColor(0xFFFFFFFF, {
  50: Color(0xFFFFFFFF),
  100: Color(0xFFFFFFFF),
  200: Color(0xFFFFFFFF),
  300: Color(0xFFFFFFFF),
  400: Color(0xFFFFFFFF),
  500: Color(0xFFFFFFFF),
  600: Color(0xFFFFFFFF),
  700: Color(0xFFFFFFFF),
  800: Color(0xFFFFFFFF),
  900: Color(0xFFFFFFFF),
}); // 앱 실행시 한번만 읽으면 되는 부분을 const로 작성

ThemeData theme() {
  return ThemeData(
    primarySwatch: primaryWhite, // 기본 배경 색상
    appBarTheme: AppBarTheme(
      iconTheme: IconThemeData(color: Colors.blue),
    ),
  );
}


profile_page

여러 페이지가 있을때 페이지 이동 하면 뒤로 가기버튼 자동 생성해주는 속성 leading : Icon(Icons.arrow_back_ios)

 

import 'package:flutter/material.dart';
import 'package:flutter_profile/components/profile_buttons.dart';
import 'package:flutter_profile/components/profile_count_info.dart';
import 'package:flutter_profile/components/profile_drawer.dart';
import 'package:flutter_profile/components/profile_header.dart';
import 'package:flutter_profile/components/profile_tab.dart';

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: _buildProfileAppBar(),
      endDrawer: ProfileDrawer(),
      body: Column(
        children: [
          SizedBox(height: 20),
          ProfileHeader(),
          SizedBox(height: 20),
          ProfileCountInfo(),
          SizedBox(height: 20),
          ProfileButtons(),
          // 남아 있는 세로 공간 모두 차지 하기 위해 Expanded
          Expanded(child: ProfileTab()),
        ],
      ),
    );
  }

  AppBar _buildProfileAppBar() {
    // 앱바의 수정이 많아졌기 때문에 메소드화
    return AppBar(
      leading: Icon(Icons.arrow_back_ios),
      title: Text(
        "Profile",
        style: TextStyle(color: Colors.black),
      ),
      centerTitle: true,
    );
  }
}

profile_count_info.dart 파일(Likes,Post,Share를 다룸 )

Row 나 Column을 mainAxis를 사용할때 주의할 점은 각각 기준이 다르기 때문에 주의 해야한다.

import 'package:flutter/material.dart';

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

  @override
  Widget build(BuildContext context) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.spaceAround,
      children: [
        _buildInfo("50", "Posts"),
        _buildLine(),
        _buildInfo("10", "Likes"),
        _buildLine(),
        _buildInfo("3", "Share"),
      ],
    );
  }

  Widget _buildInfo(String count, String title) {
    return Column(
      children: [
        Text(
          count,
          style: TextStyle(
            fontSize: 15,
          ),
        ),
        SizedBox(
          height: 2,
        ),
        Text(
          title,
          style: TextStyle(
            fontSize: 15,
          ),
        ),
      ],
    );
  }

  Widget _buildLine() {
    return Container(
      width: 2,
      height: 60,
      color: Colors.blue,
    );
  }
}


profile_header.dart부분 

CircleAvatar는 아이콘을 동그랗게 만들어주는 속성이다.(이것 외에 ClipRRECT 속성이 있다)

import 'package:flutter/material.dart';

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

  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        SizedBox(width: 20),
        _buildHeaderAvatar(),
        SizedBox(width: 20),
        Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              "Hyeonseong",
              style: TextStyle(
                fontSize: 25,
                fontWeight: FontWeight.w700,
              ),
            ),
            Text(
              "프로그래머/개발자/학생",
              style: TextStyle(fontSize: 20),
            ),
            Text(
              "Flutter 프로그래밍",
              style: TextStyle(fontSize: 15),
            ),
          ],
        ),
      ],
    );
  }

  Widget _buildHeaderAvatar() {
    return SizedBox(
      width: 100,
      height: 100,
      child: CircleAvatar(
        backgroundImage: AssetImage("assets/avatar.png"),
      ),
    );
  }
}


profile_drawer 파일 (앱의 목록 탭)

import 'package:flutter/material.dart';

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

  @override
  Widget build(BuildContext context) {
    return Container(
      width: 200,
      height: double.infinity,
      color: Colors.blue,
    );
  }
}

앱의 목록 탭을 눌렀을 경우


profile_buttons(Fllow, Message의 버튼 담당)

버튼을 여러 페이지에 사용할 수도 있으므로 버튼은 메소드로 만들어서 사용

버튼의 Text 배치

배치를 할 때 우측 하단 flutter -Inspector로 어디에 값을 줘야할지 확인 해 주는게 좋다.

현재 Text는 Container를 전부 차지 하는게 확인 된다. Container에 바로 위치를 선언 해주면 된다

버튼 Text 배치

onTap실행시 콘솔 화면 , 아이콘을 버튼화 시키는 방법 : Inkwell

아이콘을 Inkwell로 감싸고 onTap을 주면 버튼이 된다. 

import 'package:flutter/material.dart';

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

  @override
  Widget build(BuildContext context) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.spaceAround,
      children: [
        _buildFollowButton(),
        _buildMessageButton(),
      ],
    );
  }

  Widget _buildFollowButton() {
    // 다른 페이지에서도 사용하기 때문에 보통 컴포넌트로 만든다.
    return InkWell(
      onTap: () {
        print("Follow클릭됨.");
      }, // 익명 함수 표기법. tap을 할 시 실행된다.
      child: Container(
        alignment: Alignment.center, // Alignment(0.0)
        width: 150,
        height: 45,
        child: Text(
          "Follow",
          style: TextStyle(color: Colors.white),
        ),
        decoration: BoxDecoration(
          color: Colors.blue,
          borderRadius: BorderRadius.circular(10),
        ),
      ),
    );
  }

  Widget _buildMessageButton() {
    return InkWell(
      onTap: () {
        print("Message클릭됨.");
      }, // 익명 함수 표기법. tap을 할 시 실행된다.
      child: Container(
        alignment: Alignment.center, // Alignment(0.0)
        width: 150,
        height: 45,
        child: Text(
          "Message",
          style: TextStyle(color: Colors.black),
        ),
        decoration: BoxDecoration(
            color: Colors.white,
            borderRadius: BorderRadius.circular(10),
            border: Border.all()),
      ),
    );
  }
}


profile_tab

Tab_Bar 만드는 순서

  1. StateFul 위젯으로 만들기
  2. TabContoller가 필요함.(TabBar 와 TabBarView를 연결 해주기 위함)
  3. Mixin을 해줘야함 (has 라고 이해하면 편함. ~를 가진다. (다형성을 생각할 필요가 없다.)
    • SingleTickerProviderStateMixin
  4. TabBarView는 높이가 필요함

initstate의 역할 : 최초의 클래스가 실행될 때 한번만 실행된다.

initstate만들어 주고, _tabController를 객체 생성 length는 현재 탭의 갯수를 뜻한다 2개,

vsync는 어떤 클래스를 실행 할지 결정한다. 자신 클래스를 실행 하므로 this, 하지만 this만 써주면 오류가 난다. → Type가 TickerProvider이기 때문이다.

아이콘을 클릭할 때마다 아래 색깔이 바뀐다.

import 'package:flutter/material.dart';

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

  @override
  State<ProfileTab> createState() => _ProfileTabState();
}

class _ProfileTabState extends State<ProfileTab>
    with SingleTickerProviderStateMixin {
  TabController? _tabController;

  @override
  void initState() {
    // 최초의 클래스가 실행 될 때 한번만 실행한다.
    // TODO: implement initState
    super.initState();
    // length는 아이콘의 갯수
    _tabController = new TabController(length: 2, vsync: this);
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        _buildTabBar(),
        Expanded(child: _buildTabBarView()),
      ],
    );
  }

  Widget _buildTabBar() {
    return TabBar(
      controller: _tabController,
      tabs: [
        Tab(icon: Icon(Icons.directions_car)),
        Tab(icon: Icon(Icons.directions_transit)),
      ],
    );
  }

  Widget _buildTabBarView() {
    return TabBarView(controller: _tabController, children: [
      Container(
        color: Colors.green,
      ),
      Container(
        color: Colors.red,
      ),
    ]);
  }
}