Flutter Project (리펙토링)

김지환·2022년 3월 31일
0
post-thumbnail

사용 프레임워크 및 라이브러리

  • 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 Pages / Components

  • main>>MyIP>components(pages)

  • main은 app 과router를 실행시켜준다.

  • ) MyIP
    유저의 IP를 컨택해서 구글맵을 연동하여 위치를 잡아주는 컴포넌트이다.

    import 'package:flutter_svg/flutter_svg.dart';
    import 'package:shared_preferences/shared_preferences.dart';
    import 'package:internet_connection_checker/internet_connection_checker.dart';
    import 'package:flutter_map/flutter_map.dart';
    import 'package:latlong2/latlong.dart';

      @override
        Widget build(BuildContext context) {
          return Column(
            children: [
              Visibility(
                child:_myIpContent(_ip),
                visible: _isOnlineState,
              ),
              Visibility(
                child: _offlineState(),
                visible: !_isOnlineState,
              )
            ],
          );
        }
        

    메인 context필드에서 리턴해주고 child부분에서 myIpContent와 offlineState 위젯을 보여준다.

       bool result = await InternetConnectionChecker().hasConnection;
        if (result) _findMyIp();
        var url = Uri.parse('https://geolocation-db.com/json');
        //인터넷 연결을 체크한 후 url을 파싱한다.
        var response = await http.get(url);
        //파싱된 url을 받아서 200체크를 해준 후 json code를 받아서 IPv4를 뿌려준다.
        if (response.statusCode == 200) {
          var responseData = jsonDecode(response.body);
          _setMyIp(responseData["IPv4"]);
    
          setState(() {
            _ip = responseData["IPv4"];
          });
        }
      }

    인터넷을 체크하고 IP를 불러와 UI에 뿌려준다.

     child: FlutterMap(
              options: MapOptions(
                center: LatLng(37.3686272, 127.1136256),
                zoom: 16.0,
              ),
              layers: [
                TileLayerOptions(
                  urlTemplate: "https://api.mapbox.com/styles/v1/yoosangjun/cl1n9mhvh000514
                  pjz6cgkf26/tiles/256/{z}/{x}/{y}@2x?access_token=pk.eyJ1IjoieW9vc2FuZ2p1biIsImEiOiJjbDFuN2Y0MjAwcnBrM2JzOW........",
                ),
              ],
            )

    구글맵은 이런식으로 맵을 연동하고 좌표를 찍어서 UI에 뿌려준다.

  • 1) url_parser.dart
    Paste
    Toast알림
    Parse
    3가지 기능이있다.
    일단 전반적인 UI는 Material로 구분하였고 그 밑으로 paste clear textfield parse버튼 그리고 파싱 데이터를 다루는 url_result 컴포넌트로 나눠져있다.
    버튼 UI는 MouseRegion위젯을 사용하여 hover기능을 추가하였고, 전체적인 버튼 css는 elevatedButton.icon위젯을 사용하여 텍스트와 아이콘을 같이 사용할수있다.

    					MouseRegion(
                          onHover: (s) {
                            setState(() {
                              pasteHover = true;
                            });
                          },
                          onExit: (s) {
                            setState(() {
                              pasteHover = false;
                            });
                          },
                          child: Container(
                            child: ElevatedButton.icon(
                              icon: pasteHover ? SvgPicture.asset(
                                "assets/icons/paste-over.svg",
                              ) :
                              SvgPicture.asset(
                                "assets/icons/paste.svg",
                              ),
                              onPressed: _pasteData,
                              label: Text(
                                "Paste",
                                style: pasteHover ?
                                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: pasteHover ? BorderSide(width: 1.0, color: Color(0xFF4F46E5)) : BorderSide(width: 1.0, color: Color(0xFFE5E5E5))
                              ),
                            ),
                          ),
                        ),

    첫번째로, paste기능은 FlutterClipboard.paste().then((value) 라이브러리 함수를 사용해주었다.
    두번쨰로, Toast기능은 이런식으로 custom하여 사용가능하다.
    _showToast("Copy Successful.");

      _showToast(String msg, {int? duration, int? position}) {
      FlutterToastr.show(
          msg, context,
          duration: FlutterToastr.lengthShort,
          position: FlutterToastr.bottom
      );}

    세번째로, parsing data 부분인데 이건 컴포넌트화 해주었다. child: UrlResult(urlData:_idTextEditController.text),
    상세한 내용은 밑에서 첨부하겠다.

  • 2) url_result.dart
    : 위에서 url을 입력하면 그 url을 parsing하여 결과를 부분적으로 뿌려준다.
    그리고 결과마다 복사를 할수있게 해주었다.

    첫번째로, 부모 컴포넌트인 url_parser에서 textfield에 입력된 url을 받아온다.
    똑같이 required this.urlData로 받아와서
    final urlData;로 선언을 해준다.
    그다음 UI부분에만 widget.urlData로 사용해주면된다.
    그리고 받아온 url을 라이브러리 함수를 사용하여 파싱해준다.

      Uri uri = Uri.parse(widget.urlData);

    그리고 결과부분들을 이런식으로 뿌려준다. text의 overflow가 적용되려면 container부분을 felxible로 감싸줘야한다.
    그리고 복사 아이콘을 클릭했을 경우 Toast함수가 작동하도록 해주었다.
    파싱한 결과로는 uri.scheme / uri.host / uri.port.toString() / uri.userInfo / path / query / fragment등을 사용했다.

                  Flexible(
                    child: Container(
                      height: 25,
                      child: Text(uri.scheme,
                        overflow: TextOverflow.ellipsis,
                        style: kDefaultFontStyle.copyWith(fontWeight: FontWeight.w300),
                      ),
                    ),
                  ),
                  protocolView ?
                  IconButton(
                    hoverColor: Colors.transparent,
                    padding: EdgeInsets.fromLTRB(0,0,0,3),
                    constraints: BoxConstraints(),
                    icon: SvgPicture.asset(
                      "assets/icons/copy.svg",
                      color: kDefaultFontColor,
                    ),
                    iconSize: 24,
                    onPressed: ()=>{
                      Clipboard.setData(ClipboardData(text: uri.scheme)),
                      _showToast("Copy Successful.",
                        duration: FlutterToastr.lengthShort,
                        position: FlutterToastr.bottom,
                      ),
                    },
                  )
                  
  • 3) url_encoder.dart
    encode
    decode
    copy
    :이 컴포넌트도 url_parser 컴포넌트와 마찬가지로 버튼의 UI는 똑같지만, 다른점은 url을 encoder / decoder해주는 부분이다.
    textfield 2개를 사용하였고, 위에 하나는 url을 입력하는 부분이고 두번째 textfield는 encode / decode한 url을 보여주는 부분이다.

    final _idTextEditController = TextEditingController();
    //텍스트필드에 controll을 주어 id값 처럼 활용해준다.
    		Container(
                      margin: EdgeInsets.fromLTRB(0,5,0,0),
                      width: 500.0, height: 80.0,
                      child:
                      TextField(
                        autofocus: true,
                        controller: _idTextEditController,
                        maxLines: 10,
                        style: kDefaultFontStyle.copyWith(fontSize: 12.0),
                        decoration: InputDecoration(
                            hintText: "Enter a message",
                            fillColor: Colors.white,
                            filled: true,
                            enabledBorder: OutlineInputBorder(
                              borderSide: const BorderSide(width: 2, color: Color(0xFFE5E5E5)),
                              borderRadius: BorderRadius.circular(5),
                            ),
                            focusedBorder: OutlineInputBorder(
                              borderSide: const BorderSide(width: 2, color: Color(0xFF4F46E5)),
                              borderRadius: BorderRadius.circular(5),
                            )),
                        onChanged: (text){
                          setState(() {
    
                          });
                        },
                      ),
                    ),

    첫번째로, encode는 라이브러리가 아닌 내부 encodeComponent()함수를 사용했으며, encoded한 후 클립으로 복사하고 텍스트필드에 뿌려주는 함수이다.

       _urlEncode(){
          setState(() {
            encoded = Uri.encodeComponent(_idTextEditController.text);
          });
          Clipboard.setData(ClipboardData(text: encoded));
          FlutterClipboard.paste().then((value) {
            setState(() {
              _twoTextEditController.text = value;
            });
          });
        }

    두번쨰로, decode도 마찬가지이며, 유저가 encoded된 url을 입력하지 않은 경우 오류를 잡기위해서 try/catch를 적용해주었다.

    _urlDecode(){
        try{
          setState(() {
            decoded = Uri.decodeComponent(_idTextEditController.text);
          });
          Clipboard.setData(ClipboardData(text: decoded));
          FlutterClipboard.paste().then((value) {
            setState(() {
              _twoTextEditController.text = value;
            });
          });
        } catch(e){
          _showToast("Enter a encoded URL.",);
        }
      }

세번째로, Copy는 onpress부분에서 url을 작성했을경우, 클립보드에 복사하여 toast알림이 호출되게 처리를 해주었다.

					onPressed: () => {
                            if(_twoTextEditController.text.length > 0){
                              Clipboard.setData(ClipboardData(text: _twoTextEditController.text)),
                              _showToast("Copy Successful.",

                              )
                            }},
  • 4) base64_encoder.dart
    base64 encode
    base64 decode
    :위에 url_encoder 컴포넌트와 거의 유사하다.
    비록 다른점은 일반적인 url을 encode하는게 아니라 base64를 encode하는것이다.

    첫번째로, base64 encode이다. encoded = base64Url.encode(utf8.encode(_idTextEditController.text));

    두번째로, base64 decode이다. decoded = utf8.decode(base64Url.decode(_idTextEditController.text));
    마찬가지로, 오류를 위해서 try/catch를 적용해준다.

  • 5.) sidebar.dart
    ModalRoute.of(context)?.settings.name
    :메인 Pages들과 함께 계속 사용해주는 컴포넌트 위젯으로 다른 페이지 컴포넌트에는 라우터 네이게이터를 사용해준다.

    첫번째로, Home부분은 GestureDetector위젯을 사용하여 묶어주었으며, onTap하였을때 Navigator.pushNamed(context, "/"); 네이게이터를 통하여 이동하게 해주었다.
    그리고 css적인 부분들은 ModalRoute.of(context)?.settings.name를 통하여 그 context Tap에 맞게 적용시켜주었다.

       GestureDetector(
                  onTap: (){
                    Navigator.pushNamed(context, "/");
                  },
                  child: Container(
                    width: double.infinity,
                    padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 14),
                    decoration: const BoxDecoration(
                        color: Color(0xFFE5E5E5),
                        borderRadius: BorderRadius.all(Radius.circular(8.0))
                    ),
                    // homebox
                    child: Column(
                      children: [
                        Row(
                          children: [
                            SvgPicture.asset(
                              'assets/icons/home.svg',
                                color: ModalRoute.of(context)?.settings.name == '/' ? kPrimaryColor : kDefaultFontColor,
                                width: ModalRoute.of(context)?.settings.name == '/' ? 19 : 16,
                            ),
                            const SizedBox(width: 5,),
                            Text(
                              'MyIP',
                              style: ModalRoute.of(context)?.settings.name == '/' ?
                              kDefaultFontStyle.copyWith(fontWeight: FontWeight.w400, letterSpacing: -0.5, color: kPrimaryColor) :
                              kDefaultFontStyle.copyWith(fontWeight: FontWeight.w400, letterSpacing: -0.5),
                            )
                          ],
                        ),
                        Container(
                          alignment: Alignment.centerLeft,
                          child: Text(
                            _ip,
                            style: ModalRoute.of(context)?.settings.name == '/' ?
                            kDefaultFontStyle.copyWith(fontWeight: FontWeight.w400, letterSpacing: -0.5, color: kPrimaryColor) :
                            kDefaultFontStyle.copyWith(fontWeight: FontWeight.w400, letterSpacing: -0.5),
                          ),
                        )
                      ],
                    ),
                  ),
                ),

    두번째로, 나머지 pages들은 Expanded위젯을 사용하여 묶어주었으며, ListView로 묶어서 처리해주었다.
    onPress부분은 Navigator를 사용해주었으며, css부분은 ModalRoute.of(context)?.settings.name == '/url/parser' ?를 적용시켜 변화를 주었다.

       Expanded(
              child: Container(
                width: double.infinity,
                padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 10),
                alignment: Alignment.topCenter,
                child: ListView(
                  children: [
                    Container(
                      child: TextButton(
                        style: TextButton.styleFrom(
                          textStyle: const TextStyle()
                        ),
                        onPressed: () {
                          Navigator.pushNamed(context, '/url/parser');
                        },
                        child: Align(
                          alignment: Alignment.centerLeft,
                          child: Text(
                            'URL Parser',
                            style: ModalRoute.of(context)?.settings.name == '/url/parser' ?
                            kDefaultFontStyle.copyWith(fontWeight: FontWeight.w400, letterSpacing: -0.5, color: kPrimaryColor) :
                            kDefaultFontStyle.copyWith(fontWeight: FontWeight.w400, letterSpacing: -0.5),
                          ),
                        ),
                      ),
                    ),
                    

App Store

google Analytics

profile
Web Developer

0개의 댓글