Flutter Project (리펙토링)
사용 프레임워크 및 라이브러리
- Flutter
- dart
- Android-studio
- Ios / Android
- App Store 등록
- google analytics
flutter 구조
-
main.dart => pages / components
-
main은 라우터 부분을 컨트롤 한다.
-
main.dart에서 라우터를 관리하고 모든걸 관리한다.
그리고 전체적인 구성을 sidebar와 pages들로 UI를 나누어 구성한다. -
페이지는 총 3페이지로 구성되어있으며, url-parser / url-encoder / base64로 구성되어있다.
그리고 계속 들어가는 부분인 sidebar도 컴포넌트로 구성되어있다. -
main 위젯에서 전체적인 부분은 MaterialApp으로 감싸주고 routes / theme / home 3가지로 나눈 다음
Home에서 컴포넌트들을 import하여 페이지를 구성해준다.Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, initialRoute: '/', routes: { '/url/parser': (context) => UrlParser(), '/url/encoder/decoder': (context) => UrlEncoder(), '/base64/encoder/decoder': (context) => Base64Encoder() }, theme: ThemeData( fontFamily: 'Ubuntu', textTheme: const TextTheme( bodyText1: TextStyle(fontSize: 72.0, fontWeight: FontWeight.bold), button: TextStyle(fontSize: 12) ) ), home: Container( color: const Color(0xFFF6F6F6), child: Row( children: [ const SideBar(), const Expanded( child: MyIp(), ), ], ), ) ); }
CSS
-
전체적인 전역 css를 관리해주는 파일을 하나 만들어서
font.dart 파일을 만들고import 'package:flutter/material.dart'; // set color const kPrimaryColor = Color(0xFF4F46E5); const kDefaultFontColor = Color(0xFF6B7280); // set font var kDefaultFontStyle = const TextStyle( fontFamily: 'Ubuntu', fontSize: 16, fontWeight: FontWeight.normal, decoration: TextDecoration.none, color: kDefaultFontColor ); var kSubFontStyle = const TextStyle( fontFamily: 'Ubuntu', fontSize: 12, fontWeight: FontWeight.normal, decoration: TextDecoration.none, color: kDefaultFontColor ); var kDefaultButtonFontStyle = const TextStyle( fontFamily: 'Noto Sans', fontSize: 12, fontWeight: FontWeight.w400, decoration: TextDecoration.none, color: kDefaultFontColor );
이런식으로 font.dart에서 만들어준 css부분을 페이지 style부분에서 import하여 사용한다.
-
style: kDefaultTitleFontStyle,
style: kSubFontStyle.copyWith(fontWeight: FontWeight.w400, color: Color(0xFF4F46E5))
이런식으로 가져와서 사용해준다. -
위젯마다 문법이 다르기 때문에 위젯 문법에 맞게 css를 인라인으로 사용한다.
focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: BorderSide(width: 2, color: Color(0xFF4F46E5))), ), 이런식으로 위젯마다 문법에 맞게 css를 인라인으로 사용한다.
Method / Library
-
TextEditingController()
final _idTextEditController = TextEditingController(); controller: _idTextEditController, _idTextEditController.text = value;
=> textfield에 컨트롤러를 추가하여 안에있던 속성들을 컨트롤 해준다.
-
clipboard.paste()
FlutterClipboard.paste().then((value) { setState(() { _idTextEditController.text = value; });
=>컨트롤러에 해당하는 부분의 텍스트에 값을 붙여넣는 라이브러리 함수이다.
-
child: UrlResult(urlData:_idTextEditController.text)
class UrlResult extends StatefulWidget { const UrlResult({ Key? key, required this.urlData }) : super(key: key); final urlData; 페이지 안에서는 widget.urlData로 사용한다.
=> 부모컴포넌트에서 자식컴포넌트를 호출하여 활용한다.
-
Uri uri = Uri.parse(widget.urlData)
uri.scheme, uri.userInfo, uri.host, uri.port.toString() = 혼자 int타입이기 때문에 string으로 바꿔준다. uri.path uri.port등등
=>유저들이 붙여넣는 url들을 라이브러리를 이용하여 데이터를 parsing해준다.
-
encoded = Uri.encodeComponent(_idTextEditController.text)
=> url을 encode해줄때 사용한다. encoded라는 변수에 컨트롤러의 텍스트값을 인코딩한다. -
decoded = Uri.decodeComponent(_idTextEditController.text)
=> url을 decode해줄때 사용한다. decoded라는 변수에 컨트롤러의 텍스트값을 디코딩한다. -
Clipboard.setData(ClipboardData(text: encoded))
=>인코딩 한 값을 담은 변수 encoded를 clipboard에 Copy한다.
-
Toast알림
import 'package:flutter_toastr/flutter_toastr.dart'; _showToast(String msg, {int? duration, int? position}) { FlutterToastr.show( msg, context, duration: FlutterToastr.lengthShort, position: FlutterToastr.bottom ); } _showToast("Copy Successful.",)
=>toast라이브러리를 사용하여 알림창을 띄운다.
-
ModalRoute.of(context)?.settings.name == '/' ? kPrimaryColor : kDefaultFontColor
style: ModalRoute.of(context)?.settings.name == '/url/encoder/decoder' ?
=>라우터로 세팅이된 이름을 구분하여 css를 주는 방식이다.
!)꿀팁으로 웬만한 css는 이걸로 처리해주면 좋다.
-
Navigator.pushNamed(context, "/")
Navigator.pushNamed(context, '/url/encoder/decoder')
=>onTap이나 onPressed함수에 활용되어 라우터가 context 이름에 따라 이동된다.
Widget
-
StatelessWidget / StatefulWidget
StatelessWidget : 상태가 없는 위젯. 변화가 거의 없는 위젯은 이것으로 선언한다
StatefulWidget : state라는 데이터 변화를 감지하고, state가 변할시 위젯을 rebuild 하는 위젯. setState라는 함수를 통해 state변화를 감지하여야 한다
class UrlResult extends StatefulWidget {
//필수
const UrlResult({
Key? key,
required this.urlData
}) : super(key: key);
final urlData;
//부모 컴포넌트로부터 data를 받아왔을 경우에 위에 처럼 선언해준다.
@override
UrlResultState createState() => UrlResultState();
//필수
}
class UrlResultState extends State<UrlResult> {
//필수
//이제 여기서 본문에서 사용할 변수와 함수들을 선언해준다.
bool protocolView = false;
_showToast(String msg, {int? duration, int? position}) {
FlutterToastr.show(
msg, context,
duration: duration,
position: FlutterToastr.bottom
);
}
@override
Widget build(BuildContext context) {
Uri uri = Uri.parse(widget.urlData);
return
Container(
);}
-
initState / dispose
initState: StatefulWidget 생성시 초기에 딱 한번 호출. 이니셜라이징 할 곳은 이곳에 모아두자
dispose : StatefulWidget에서 state object가 필요없을시 불리는 함수. uninit 할곳은 이곳에 모아두자.@override void initState() { super.initState(); _findInternetConnection(); _getMyIp(); }
-
GestureDetector / MouseRegion
GestureDetector : 많은 위젯들은 GestureDetector를 통해 다른위젯에 콜백을 전달합니다. 예를들어 IconButton, RaisedButton, FloatingActionButton 위젯은
onPressed() 라는, 유저가 위젯을 탭 했을때 호출되는 콜백을 가지고 있습니다. tap 제스쳐 외에 drag, scale등의 제스처를 감지할 수 있습니다.MouseRegion : 마우스에 대한 속성들을 가지고 있는 위젯이다. 호버와 탭 커서등 여러가지 마우스에 속성을 부여할수있다.
onHover: (s){ setState(() { protocolView = true; }); },
-
Material / Scaffold(appBar ,Body ,BottomNavigationBar)
Material : Navigator라는 위젯을 제공합니다. Navigator는 위젯의 스택을 관리하며 각 스택은 "routes"라는 string 변수로 구분됩니다.
Scaffold : 전체적인 시멘틱 태크와 같이 Scaffold위젯 안에서 사용된다.
3부분으로 헤드 바디 바텀으로 나뉘어 위젯의 위치를 분류하여 꾸밀수있다.
여러 다른 위젯을 named arguments로 받는것을 보실수 있다.
-
Stack / SingleChildScrollView / PageView
Stack : children에 나열한 여러 위젯을 순서대로 겹치게해준다. 사진 위에 글자를 표현하거나 화면 위로 로딩 표시를 하는 상황에 사용한다.
SingleChildScrollView: 하나의 자식을 포함하는 스크롤을 가능하게 하는 위젯이다. 자식으로는 Column보다는 ListBody위젯을 사용해주면 더 간편해진다.
PageView : 여러 페이지를 좌우로 슬라이드 하여 넘길수 있도록 하는 위젯이다.
children 프로퍼티에 각 화면을 표현할 위젯을 여러개 준비하여 지정하면 Tap과 연동되어 활용된다. -
Container / SizedBox
Container: 아무것도 없는 기본 위젯으로 div와 같은 형태를 띈다.
다양한 프로퍼티를 가지고 있기때문에 사용하기에 따라서 다양한 응용이 가능하다.
또다른 위젯을 가질수 잇으며, margin,padding등 여러가지 CSS를 적용할수있다.SizedBox ; 위젯을 특정한 크기로 만들고 싶은경우 사용한다. 하지만 Container가 더 많이 쓰이며, Container보단 가볍다.
-
Column / Row(MainAxisSize / MainAxisAlignment / CrossAxisAlignment)
전체적으로 가로 / 세로로 나열할수있게 만들어주는 기본 방향 위젯이다.
이 위젯 안에서는 중요한 부분을 정의할수있는데 MainAxisSize / MainAxisAlignment / CrossAxisAlignment등을 적용할수있다. 이것들을 flex와 같이 사용할수있기 때문에 편리하다. -
Expanded / Card / Flexible
Expanded : 자식위젯의 크기를 최대한으로 확장시켜주는 위젯으로 여러 위젯에 동시에 적용하면 flex프로퍼티에 정수 값을 지정하여 비율을 정할수 있으며 기본 값은 1이다.
Card : 카드 형태의 모양을 제공하는 위젯으로 기본적으로 크기가 0이므로 자식 위젯의 크기에 따라 크기가 결정된다.
Card( shape : RoundedRectangleBorder( borderRadius: BorderRadius.circular(16.0), ), elevation: [실수값], // 그림자 깊이 child: [위젯], ),
Flexible: Expanded와 비슷하지만 최대로 확장하는것이 아닌 flexible한 유연하게 크기를 조절할수가있다. Text부분이 overflow가 되거나 하는 부분들을 flexible로 감싸주면 효과가 적용된다.
Flexible( child: Container( height: 25, child: Text(uri.scheme, overflow: TextOverflow.ellipsis, style: kDefaultFontStyle.copyWith(fontWeight: FontWeight.w300), ), ), ),
-
Text / Icon / Image / Progress / CircleAvatar / Button
Text : 글자를 표시하는 위젯이다.
Text( ' hello', style: TextStyle( fontSize: 40, fontStyle, FontStyle.italic, color: Colors.red ), ),
Icon : 아이콘은 그냥 아이콘 단독으로 사용가능하다.그리고 이미지를 부여할수도있다.
icon: parseHover ? SvgPicture.asset(
"assets/icons/edit.svg",
color: kDefaultFontColor,
)
Image : Image.asset("asset/sample.jpg")
Progress : 로딩중이거나 오래걸리는 작업을 할때 진행중임을 보여주는 위젯이다.
CircularProgressIndicator()
LinearProgressIndicator()
CircleAvatar : 프로필 화면 등에 많이 사용하는 원형 위젯으로 child 프로퍼티에 정의한 위젯을 원형으로 만들어준다.
CircleAvatar(
child: Icon(Icon.person),
),
CircleAvatar(
backgroundImage: NetworkImage([이미지 URL]),
),
Button :
1)RaisedButton : 입체감을 가지는 일반적인 버튼으로 onPress에 실행될 코드를 적용시켜야 비활성화가 풀린다.
2)FlatButton : 평평한 형태의 버튼이다.
3)IconButton : 아이콘을 표시하는 버튼이다. 아이콘 크기 색상등을 지정할수있다.
4)FloatingActionButton : 입체감있는 둥근 버튼으로 해당부분에 action을 줄수가있다.
ElevatedButton.icon : 아이콘과 텍스트를 같이 사용할수있으며 웬만한 기능들이 다 들어있는 최고의 버튼이다.
child: ElevatedButton.icon(
icon: parseHover ? SvgPicture.asset(
"assets/icons/edit.svg",
color: kDefaultFontColor,
) :
SvgPicture.asset(
"assets/icons/edit-over.svg",
color: kDefaultFontColor,
),
onPressed: _show,
label: Text(
"Parse",
style: parseHover ?
kSubFontStyle.copyWith(fontWeight: FontWeight.w400, color: Color(0xFF4F46E5))
: kSubFontStyle.copyWith(fontWeight: FontWeight.w400),
),
style: ElevatedButton.styleFrom(
primary: Colors.white,
fixedSize: const Size(80, 25),
side: parseHover ? BorderSide(width: 1.0, color: Color(0xFF4F46E5))
: BorderSide(width: 1.0, color: Colors.transparent)
),
),
LifeCycle
Project Component
-
유저가 브라우저를 열면 html파일을 제일 먼저 보는데
여기 구조에서도 public의 index.html파일이 있고
그안에 body태그안에 라우터를 통하여 컴포넌트와 페이지들이 들어간다.
html파일이 먼저 보여지고 그다음 App.vue가 켜진다. -
main>index>App>Home>…components
-
main은 app 과router를 실행시켜준다.
-
라우터폴더 안에 index.js는 말 그대로 라우터를 관리한다.
-
App에서는 페이지 관련된 각각의 views 페이지로된 컴포넌트들을 관리하고 바인딩해준다.
-
Home에서는 일단 바인딩해주는 템플릿태그 안에서 Home페이지에 뿌려줄 컴포넌트들을 import해서
써주고 버튼같은 부분들도 구현하여 이벤트를 걸어준다. -
script태그 안에 export default부터는
name:Home //사용할 이름을 써주고 Components:{ DataTitle, //여기도 사용할 컴포넌트를 써주고 }, data(){ Return{ //여기에는 관리할 state 들을 초기화 시켜둔다. } }, Methods:{ Async fetchCovidData(){ const res = await fetch(``) }, async created(){ const data = await this.fetchCovidData() this.dataDate = data.Date this.stats = data.Global this.countries = data.Countries this.loading = false }
: 여기에선 여러가지 fetch함수나 메소드등등 다양하게 쓴다.
Views Pages / Components
-
중요
this.$emit('get-country', country)
(‘함수이름’,넘겨줄인자)
Emit은 이벤트버스 이벤트 통신방법이다.
부모로 값을 넘겨주어 부모 이벤트를 실행시킬때
주로 사용한다.
-
일단 header는 App.vue에서 컴포넌트를 따로 만들어 전역으로 사용될수있게 분리하여 App.vue에서 Import해주었다.
-
1)Home.vue
Header
DataTitle
DataBoxes
CountrySelect Home.vue 안에
순서대로 컴포넌트가 col형태로 분리되어 있으며,
일단 템플릿태그안에 UI 바인딩되는 부분을 메인태그로 감싸서 여러 컴포넌트로 채워놨고
그 밑에 버튼을 두었다.
그리고 그 메인 태그에 v-if조건을 두어 로딩이되었을경우 보여지게끔 해두었고 로딩전에는
밑에 따로 메인 태그에 v-else로 fetching data.. 하면서 로딩화면을 띄우게 해두었다.
그리고 메소드로 가서 fetch함수를 이용하여 데이터를 불러오고 리턴해주어 다른 컴포넌트들에 props로 뿌려준다. -
2)DataTitle.vue
: 여기에서는 국가이름과 데이터를 가져온 시간을 알려주는데 {{timestamp}}를 사용한다.
그러려면 momet를 사용하는데 일단 moment를 import 해주고 computed를 통해서 timestamp를 써준다.Computed:{ timestamp: function(){ return moment(this.dataDate) .format(‘MMM Do YYYY, h:mm:ss a’) } }
이런식으로 사용해준다.
-
3)DataBoxes
2개의 데이터박스를 만들어서 확진자와 사망자를 분리하고 stats이라는 props데이터를 가져오는데
가져온 데이터를 뿌려줄때 숫자에 컴마를 추가하고싶은 경우에는numberWithCommas(x){ return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',') } 이 메소드를 사용해준다. 예)12345,133,34235,24234,3200 {{numberWithCommas(stats.NewConfirmed)}} ㄴ>템플릿엔 이렇게 적어준다.
-
4).CountrySelect.vue
셀렉트박스를 컨트롤해준다.
일단 home에서 국가에대한 데이터를 props로 받아와서
템플릿태그안에 select 태그에 v-model=“selected” 를 사용하여 셀렉트 박스를 만든후
option태그를 사용하고 그 안에 v-for를 이용하여 국가에대한 데이터를 받아와서
다 뿌려준다.
그리고 value=“country.ID”를 이용하여 ID에 해당하는 셀렉트된 국가를 보여준다.{{country.Country}}
그리고 data()부분에서는 selected: 0으로 초기화해두고
메소드부분에서 onchange이벤트를 이용하여 국가가 변경됐을때 호출되면서 셀렉한 국가를 fnid() 함수를 이용하여 찾아낸 후 그 아이템에 ID를 부여하고 셀렉된 국가를
this.$emit('get-country', country)
Emit 이벤트 버스를 이용하여 ID부여된 국가를 부모에게 준다.
그러면 Home에서 해당하는 Props를받고 함수를 실행시켜 데이터를 변경해주고 다시 셀렉된 국가에 대한 정보를 뿌려준다. -
5.) clear버튼
버튼의 함수에서는 다시 fetch함수를 불러와서 클리어할 부분의 state값을 다시 set해준다.
그리고 v-if조건을 주어 국가를 지정했을때 버튼이 보이게끔 해두었다.
Author And Source
이 문제에 관하여(Flutter Project (리펙토링)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@jihs2113/Flutter-Project-리펙토링저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)