본문 바로가기
개발/Flutter

[Flutter] 프로필 UI 만들기

by 가시죠 2021. 12. 30.
반응형

사용된 컴포넌트

ThemeData : https://api.flutter.dev/flutter/material/ThemeData-class.html

TabBar : https://api.flutter.dev/flutter/material/TabBar-class.html

TabBarView : https://api.flutter.dev/flutter/material/TabBarView-class.html

AppBar : https://api.flutter.dev/flutter/material/AppBar-class.html

InkWell : https://api.flutter.dev/flutter/material/InkWell-class.html

GridView : https://api.flutter.dev/flutter/widgets/GridView-class.html

Drawer : https://api.flutter.dev/flutter/material/Drawer-class.html

Align : https://api.flutter.dev/flutter/material/Drawer-class.html

Image : https://api.flutter.dev/flutter/dart-ui/Image-class.html

 

프로젝트 구조

프로젝트 구조

 

소스코드

pubspec.yaml - 변경후에는 Pub get 버튼을 클릭하여 적용해야 한다.

name: flutter_profile
description: profile Flutter Project
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 1.0.0+1
environment:
  sdk: ">=2.12.0 <3.0.0"
dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^1.0.2
  fluttertoast: ^8.0.8
dev_dependencies:
  flutter_test:
    sdk: flutter
flutter:
  uses-material-design: true
  assets:
    - assets/

 

main.dart

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';
import 'package:flutter_profile/theme.dart';

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

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

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

class ProfilePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      endDrawer: ProfileDrawer(), //오른쪽에서 왼쪽으로 슬라이드 하는 Drawer
      appBar: _buildProfileAppBar(), //앱바
      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"),
      centerTitle: true,
    );
  }
}

 

theme.dart

import 'package:flutter/material.dart';

ThemeData theme() {
  return ThemeData(
    primaryColor: Colors.white, // 전체적인 색상, default는 blue
    accentColor: Colors.blueGrey, // 액센트 색상
    appBarTheme: AppBarTheme(
      iconTheme: IconThemeData(color: Colors.blue),
    ),
  );
}

 

profile_buttons.dart

import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';

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

  // followButton
  _buildFollowButton() {
    return InkWell(
      onTap: () {
        print("FollowButton Clicked");
        Fluttertoast.showToast(msg: "Follow!", webPosition: "center", webBgColor: "FF0083FF");
      },
      child: Container(
        alignment: Alignment.center,
        width: 150,
        height: 45,
        child: Text(
          "Follow",
          style: TextStyle(color: Colors.white),
        ),
        decoration: BoxDecoration(
          color: Colors.blue,
          borderRadius: BorderRadius.circular(10), // 모서리 둥글게
          border: Border.all()
        ),
      ),
    );
  }

  _buildMessageButton() {
    return InkWell(
      onTap: () {
        print("MessageButton Clicked");
        Fluttertoast.showToast(msg: "Message!", webPosition: "center", webBgColor: "FF0083FF");
      },
      child: Container(
        alignment: Alignment.center,
        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_count_info.dart

import 'package:flutter/material.dart';

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

      ],
    );
  }

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

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

 

profile_drawer.dart

import 'package:flutter/material.dart';

/// 넓이가 200인 파란색 Container
class ProfileDrawer extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      width: 200,
      height: double.infinity, /// 최대 범위로 지정
      color: Colors.blue,
    );
  }
}

 

profile_header.dart

import 'package:flutter/material.dart';

class ProfileHeader extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        SizedBox(width: 20,),
        _buildHeaderAvator(),
       SizedBox(width: 20,),
       _buildHeaderProfile(),
      ],
    );
  }

  Widget _buildHeaderProfile() {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start, // 왼쪽 정렬
      children: [
        Text(
          "GetinThere",
          style: TextStyle(
            fontSize: 25,
            fontWeight: FontWeight.w700,
          ),
        ),
        Text(
          "프로그래머/작가/강사",
          style: TextStyle(
            fontSize: 20,
          ),
        ),
        Text(
          "프로그래머",
          style: TextStyle(
            fontSize: 15,
          ),
        ),
      ],
    );
  }

  _buildHeaderAvator() {
    return SizedBox(
      width: 100,
      height: 100,
      child: CircleAvatar( // CircleAvator를 이용하면 둥글게 이미지를 만들 수 있다.
        backgroundImage: AssetImage("assets/avator.png"),
      ),
    );
  }
}

 

profile_tab.dart

import 'package:flutter/material.dart';

class ProfileTab extends StatefulWidget {
  // 자동으로 생성
  const ProfileTab({Key? key}) : super(key: key);

  @override
  _ProfileTabState createState() => _ProfileTabState();
}

class _ProfileTabState extends State<ProfileTab> with SingleTickerProviderStateMixin {
  /**
   * 다중 상속을 위해 with 키워드 사용
   * SingleTickerProviderStateMixin
   * 한개의 애니메이션을 가진 위젯을 정의할 때 사용
   * 두개 이상의 애니메이션을 가진 위젯을 경우 TickerProviderStateMixin을 사용.
   */
  TabController? _tabController;

  @override
  void initState() {
    /**
     * StatefulWidget에만 존재하는 초기화를 위한 함수 (최초 한번만 실행됨)
     * vsync: this -> 해당 위젯의 싱크를 SingleTickerProviderStateMixin에 맞춤.
     */
    super.initState();
    _tabController = new TabController(length: 2, vsync: this);
  }

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

  // 탭바를 리턴, controller를 _tabController로 지정
  Widget _buildTabBar() {
    return TabBar(
      controller: _tabController,
      tabs: [
        Tab(icon: Icon(Icons.directions_car),),
        Tab(icon: Icon(Icons.directions_transit))
      ],
    );
  }

  _buildTabBarView() {
    return TabBarView(
      controller: _tabController,
      children: [
        // GridView를 동적으로 생성하기 위해 GridView.builder 사용
        GridView.builder(
            gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
              crossAxisSpacing: 10,
              crossAxisCount: 3,
              mainAxisSpacing: 10
            ),
            itemCount: 42,
            itemBuilder: (context, index) {
              // picsum 사이트에서 제공하는 이미지 가져오기 및 사이즈 지정
              return Image.network("https:picsum.photos/id/${index+1}/200/200");
            }
        ),
        Container(color: Colors.red,)
      ],
    );
  }
}

 

 


참조 : https://github.com/flutter-coder/flutter-ui-book1

 

반응형

댓글