코딩 이야기/미니미 프로젝트

[Flutter 프로젝트] flutter로 선택장애, 결정장애 해결 안드로이드 어플만들기 2편

dajoonee(다쥬니) 2020. 12. 9. 14:00
728x90
반응형
 

[flutter 프로젝트] flutter로 선택장애, 결정장애 해결 안드로이드 어플만들기 1편

네이버블로그에서 포스팅했지만 배달시킨 음식이 도착기까지 아직 많은 시간이 남아서 티스토리에도 올립니당😀 지금 업로드 되는 gif 파일들속 영상들에는 오늘 추가한 모든 기능들이 들어있

dajoonee.tistory.com

👆👆👆👆 이 전 UI를 보시려면 전 포스팅을 보고 오세요~~ 👆👆👆👆

하루나 이틀뒤에 다시 오겠다는 지키지못할 약속을 해놓고.. 드디오 왔습니다 🤣

UI를 갈아엎(?)은 정도는 아니지만 포인트 컬러는 다 바꿨어요 ㅎ헣

 

 

갑자기 버튼이 생겼어요.

친 언니가 글쎄,

누가 랜덤뽑기하냐고 사다리게임 정도는 있어야 하는거 아니냐고..

그래서 기능 두 가지를 더 추가하기로 했지요 하하하

사다리타기와 로또번호

포토샵으로 아이콘들도 직접 그렸어요.

로또 아이콘은 왜 포켓볼 공처럼 보일까요...

dart 파일들도 이렇게 늘어남ㅋㅋㅋ

아직 기능 1. 『 랜덤뽑기 』 만해서 이 정도에요..

앞으로 더 늘어날 예정입니다 ㅎㅎ


코드를 파헤쳐 봅시다.


main.dart

MaterialApp을 빌드하고

home의 인자값으로 메인페이지를 띄워줍니다.

나머지 페이지들은 (뽑기 메인페이지, 고르는중 페이지, 뽑기 결과 송출 페이지)

이름이 있는 라우트를 지정해주었어요.

 

golladuck_home.dart

메인 페이지는 Stateless 위젯입니다.

빌드된 이후 메인 페이지에 있는 위젯들은 변화할 필요가 없기 때문이죠.

단순하게 페이지 오픈을 위한 버튼들로만 구성이 되어 있으니까요.

테스트로 snackBar를 구현하기 위해 바로 Scaffold 리턴하지 않고 Builder로 감싸서 리턴하게 했었으나..

SnackBar가 안나타남..

지금 보니깐 Scaffold 자체를 builder로 감싸는게 아니라

body의 인자값인 SafeArea를 감쌌어야했네요....;;;

바로 고쳐봤어요ㅎ ㅎㅎ하핳

테스트용으로 쓰려고 했던 snackBar라서 굳이 SafeArea를 Builder로 감싸주진 않았습니다.

flutter.dev 홈페이지에서 SnackBar를 찾아보시면

이러한 문구를 보실 수 있습니다.

SnackBar에 대해 더 자세한 내용은 다음 포스팅에서 다뤄보도록 할게요 :)

여러 위젯들이 들어가기에 Column 위젯안에 버튼들을 배치해 두었어요.

버튼은 같은 디자인이지만 내용물이 다르죠.

그래서 리팩토링(Repactoring)을 통해 가독성을 높였습니다.

버튼 이름과, 페이지번호, context를 매개변수로 가지고 있는 customBtnWidget 메소드를 만들어주었습니다.

버튼 이름은 말 그대로 버튼 안에 들어갈 내용이며,

페이지 번호는 어떠한 페이지를 띄울것인지 판단하도록

또한, 해당 페이지에 맞는 icon을 넣어주기 위해 int형으로 받도록 하였습니다.

버튼을 눌렀을 때 해당 페이지를 열어주어야 하니

openPage라는 메소드를 만들어주었구요.

아직은 랜덤뽑기 페이지만 만들어놓아서 메소드의 기능을 모두 코딩해놓지는 않았습니다..ㅎㅎ;

 

picking_addItems_page.dart

랜덤뽑기 메인 페이지 입니다.

 

이 페이지의 코드들은 좀 난잡..(?)할 수 있어요.

많은 삽질을 하고 머리를 쥐어뜯으며 코딩했던 페이지라

코드 정리가 잘 안되어있습니다 ㅠ

먼저 결과 송출 페이지로 이동하는 버튼은 FloatingActionButton을 이용했습니다.

버튼내에 Text를 넣기 위해 크기는 extended를 사용했습니다.

직접 일러스트레이터로 그린 오리 실루엣을 아이콘으로 활용하기 위해

아이콘으로 추가할 수 있게 만들어주는 사이트를 이용했어요.

 

FlutterIcon - Flutter custom icons generator

This site will not work if cookies are completely disabled. {"assets_hash":"e63afe94764170521b88e195c1026df9","page_data":{},"locale":"en-US","layout":"fontello.layout"}

www.fluttericon.com

🙄 위 사이트에서 내가 제작한 아이콘을 flutter에서 활용할 수 있는 파일로 만들어줍니다.

이 페이지에 구현한 기능들을 설명해보자면

 

목록 전체 삭제 기능

 

이렇게 두 개 이상의 값이 입력되면 입력 필드 옆에

지금까지 추가한 값을 한꺼번에 지울수 있는 버튼이 나타납니다.

 

 

 

선택한 아이템 삭제하기, 아이템을 모두 지울경우 모두삭제 버튼 없애기

 

 

목록이 비어있을경우 스낵바로 에러 메세지 보여주기

 

 

 

 

기능 설명하려다 보니 아직 미완성이네요..

일단, picking_addItem_page.dart의 전체 코드를 올립니다.

아직 해결할게 많....🥶

 

class Picking_AddItemsPage extends StatefulWidget {
  @override
  _Picking_AddItemsPageState createState() => _Picking_AddItemsPageState();
}

class _Picking_AddItemsPageState extends State<Picking_AddItemsPage> {
  ItemList _itemList;
  int itemNumber = 0;
  bool removeAllBtn = false;
  List<Item> myItem = [];
  TextEditingController _txtEditCont;
  FocusNode _mFocusNode;

  @override
  void initState() {
    super.initState();
    _txtEditCont = TextEditingController();
    _mFocusNode = FocusNode();
    _itemList = ItemList();
  }


  @override
  void dispose() {
    super.dispose();
    _mFocusNode.dispose();
  }

  @override
  Widget build(BuildContext context) {
    double mWidth = MediaQuery.of(context).size.width;
    double mHeight = MediaQuery.of(context).size.height;

    return Scaffold(
      resizeToAvoidBottomInset: false,
      floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
      floatingActionButton: Builder(builder: (BuildContext cnt) {
        return FloatingActionButton.extended(
          label: Text(
            ' 고른덕덕덕',
            style: TextStyle(
                fontFamily: 'BinggraeII',
                letterSpacing: 2,
                fontWeight: FontWeight.bold,
                fontSize: 20),
          ),
          icon: Icon(MyFlutterApp.duck),
          backgroundColor: Colors.indigo.shade300,
          onPressed: () {
            //TODO: Add Navigator.pop
            if (_itemList.getItems().isEmpty)
              showSnackBar('추가된 항목이 없습니다.', cnt);
            else if (_itemList.getItems().length < 2)
              showSnackBar('두 개 이상의 항목이 필요합니다.', cnt);
            else {
              Navigator.pushNamed(context, '/pl');
              popRoute();
            }
          },
        );
      }),
      body: Builder(builder: (context) {
        return GestureDetector(
          behavior: HitTestBehavior.translucent,
          //TODO: YOU MUST STUDY THAT HOW I SOLVED THIS PROBLEM!
          onTap: () {
            print('탭캐배ㅐㄴㅇ');
            FocusScope.of(context).unfocus();
          },
          child: SafeArea(
            // child: Padding(
            // padding: EdgeInsets.all(mHeight * 0.01),
            child: Container(
              padding: EdgeInsets.fromLTRB(mWidth * 0.04, 0, mWidth * 0.02, 0),
              child: Column(
                children: <Widget>[                  Expanded(                    child: Row(                      mainAxisAlignment: MainAxisAlignment.spaceEvenly,                      crossAxisAlignment: CrossAxisAlignment.center,                      children: <Widget>[                        Visibility(                          visible: removeAllBtn,                          child: Container(                            padding: EdgeInsets.all(mWidth * 0.005),                            // flex: 1,                            child: IconButton(                                icon: Icon(Icons.refresh),                                color: Colors.red.shade300,                                iconSize: mWidth * 0.1,                                onPressed: () {                                  setState(() {                                    _itemList.clearItems();                                    removeAllBtn = false;                                    // myItem =[];
                                  });
                                }),
                          ),
                        ),
                        
                     Flexible(
                          // flex: 6,
                          child: Theme(
                            data: ThemeData(
                              primaryColor: Colors.red.shade300,
                            ),
                            child: Container(
                              child: TextField(
                                style: TextStyle(color: Colors.indigo,),
                                controller: _txtEditCont,
                                focusNode: _mFocusNode,
                                decoration: InputDecoration(
                                  isDense: true,
                                  labelStyle: TextStyle(
                                      fontStyle: FontStyle.italic,
                                      color: Colors.indigo,
                                      fontSize: 15.0),
                                  labelText: '추가할 항목을 입력하라덕',
                                  border: outlineInputBorder(
                                      color: Colors.red.shade300),
                                  enabledBorder: outlineInputBorder(
                                      color: Colors.red.shade300),
                                ),
                              ),
                            ),
                          ),
                        ),
                        Container(
                          // flex: 1,
                          padding: EdgeInsets.all(mWidth * 0.005),
                          child: IconButton(
                              icon: Icon(Icons.add_circle),
                              color: Colors.red.shade300,
                              iconSize: mWidth * 0.1,
                              onPressed: () {
                                setState(() {
                                  //TODO : clear() 안먹힘;
                                  if (_txtEditCont.text != '') {
                                    _itemList.makeItem(_txtEditCont.text, test);

                                    if (_itemList.getItems().length >= 2)
                                      removeAllBtn = true;
                                  } else {
                                    showSnackBar(
                                        '입력값이 없습니다. 항목을 입력하세요.', context);
                                  }
                                  _txtEditCont.clear();
                                  _mFocusNode.requestFocus();
                                  FocusScope.of(context).unfocus();

                                });
                              }),
                        )
                      ],
                    ),
                    flex: 1,
                  ),
                  Expanded(
                      flex: 9,
                      child: SingleChildScrollView(
                        child: Column(
                          children: _itemList.getItems(),
                        ),
                      ))
                ],
              ),
            ),
            // ),
          ),
        );
      }),
    );
  }
 Future popRoute() async {
    return Future.delayed(Duration(milliseconds: 2000), () {
      Navigator.pop(context, '/pl');
      Navigator.pushNamed(context, '/pr',
          arguments: DataArg(_itemList.getList()));
    });
  }

  void test(Function removeMethod) {
    setState(() {
      removeMethod;
      if (_itemList.getItems().length <= 0) {
        removeAllBtn = false;
      }
    });
  }

  OutlineInputBorder outlineInputBorder({Color color}) {
    return OutlineInputBorder(
      // gapPadding: 2.0,
        borderSide: BorderSide(
          color: color,
        ),
        borderRadius: BorderRadius.all(Radius.circular(10.0)));
  }

  void showSnackBar(String message, BuildContext cnt) {
    Scaffold.of(cnt).showSnackBar(SnackBar(
      content: Container(
        alignment: Alignment.center,
        height: MediaQuery.of(cnt).size.height * 0.035,
        child: Text(
          message,
          style: TextStyle(
            fontSize: MediaQuery.of(cnt).size.height * 0.02,
            color: Colors.red,
          ),
        ),
      ),
      backgroundColor: Colors.white,
    ));
  }
}

예상치못한 에러가 있어서 오늘 포스팅은 여기까지..

to be continue...(언제오려나..;;)

 

 

반응형