1. 텍스트 폼 필드 설정
  TextFormField(
            minLines: 1, // 최소 줄 수
            maxLines: null, // 무제한으로 아래로 확장
            keyboardType: TextInputType.multiline, // 여러 줄 입력 가능하게 설정
          ),
          TextFormField(),그냥 텍스트폼필드와, 텍스트 폼필드를 설정할 수 있다. 

위의 필드는 글이 늘어나면 아래로 확장되고
아무 설정을 하지 않으면 옆으로 늘어난다. 
2. 텍스트 폼에 체크 박스 넣기
   Checkbox(
              value: false,
              onChanged: (value) {
                print(value);
              },
            ),체크박스를 누를 때 마다 값을 출력하도록 했다.
체크를 하더라도 체크 상태가 유지되지 않고, 계속해서 
true 가 출력된다.
원래라면 눌렀을때 true → false → true 라는 식으로 계속해서 변경되는 것이 옳다.이를 위해 
StatelessWidget 을 ful 로 변경을 해야할까..?(참고로 
TextEditingController 는 체크박스에서는 사용 할 수 없다.)3. Expanded 와 Flexible

빨간 박스만큼 Expanded 하고 있다.

Flexible 을 loose 로 설정해 주어야 유연하게 공간을 차지한다. (스크롤도 가능함)  TextFormField(
                  controller: _username,
                ),
                TextFormField(
                  controller: _password,
                ),
              ],
            ),
          ),
          TextButton(
              onPressed: () {
                print(_username.text); //I/flutter (12998): sarr
              },
              child: Text("글쓰기")),
        ],
      ),username 은 값을 가져올 수 있다.
view 는 viewModel 한테 의존하기 때문에 이 값을 vm 에 전달 해야한다. 

요청되면 이걸 리턴함
  Future<void> notifySave() async {
    Map<String, dynamic> one = await PostRepository().save(); // 한 건 받음
    _Post newPost = _Post.fromMap(one);
    PostListModel model = state!; // 이게 호출될때는 리스트->글쓰기 로 간거라 null 일 수가 없음
    
    model.posts.add(newPost);
    state = PostListModel(model.posts);
  }껍데기를 바꿨기 때문에 이게 넣어진다(주소가 달라짐)
  Future<void> notifySave() async {
    Map<String, dynamic> one = await PostRepository().save(); // 한 건 받음
    _Post newPost = _Post.fromMap(one);
    PostListModel model = state!; // 이게 호출될때는 리스트->글쓰기 로 간거라 null 일 수가 없음
    List<_Post> newPosts = [newPost, ...model.posts]; //최근에 쓴 글이 앞에 와야해
    state = PostListModel(newPosts);
  }
혹은 깊은 복사를 해서 넣어야 한다. 
그러나 state 에 model 을 바로 넣을 수는 없다. (기존의 값을 건들이게 되기 떄문에)
리뷰
  Future<void> notifySave(String title, String contnent) async {
  
  // 통신코드
    Map<String, dynamic> one =
        await PostRepository().save(title, contnent); // 한 건 받음
    _Post newPost = _Post.fromMap(one);
    PostListModel model = state!; // 이게 호출될때는 리스트->글쓰기 로 간거라 null 일 수가 없음
    //model.posts.add(newPost);
// 상태 갱신 코드
    List<_Post> newPosts = [newPost, ...model.posts]; //최근에 쓴 글이 앞에 와야해
    state = PostListModel(newPosts);
  }- repository
  Future<Map<String, dynamic>> save(String title, String content) async {
    Response response = await dio.post(
      "/api/post",
      data: {"title": title, "content": content},
    );
    Map<String, dynamic> responseBody = response.data["body"];
    // list 의 map 타입
    return responseBody;
  }- write_body
TextButton(
        onPressed: () {
          ref
              .read(postListProvider.notifier)
              .notifySave(_title.text, _content.text);
        },
        child: Text("글쓰기")),
  ],글쓰기 버튼을 누른 뒤 직접 뒤로가기 버튼을 눌러 보면 작성된 게시물이 업로드 되어 있다. 
글 쓰기 버튼을 누르고 나면 어디선가 
Navigator.pop(context) 를 해야 하는것..
이 페이지에서는 write 페이지가 날아간다고 해서 model 이 사라지진 않기 때문에 상관 없지만 어떤 페이지에서는 리턴이 오래 걸릴 때 이미 List로 갔지만, 작성 시도한 게시글은 보이지 않고, 몇 초 뒤에 나타나게 될 수 있다.따라서 작성등록 → List로 이동은 하나의 트랜잭션으로 묶여 있어야 한다.
notifySave 는 이미 async~await 를 갖고 있다. 그렇다면 save 의 마지막에 pop 을 시키면 될까…?
매개변수로는 title과 content 만 갖고 있으면 된다…

창고 만들 떄 만들어
작동 원리는 main  에 
navigatorKey: navigatorKey, 를 넣어놨다 . 이 키는 utils 에서 전역 키로 만들어져 있다.(임의로 등록해놓음)
final navigatorKey = GlobalKey<NavigatorState>();제일 상단에 떠있는 페이지를 추적할 수 있도록 해주는 것이다.

이제 ViewModel(=VM) 에 만들어 놓은 컨텍스트를 보면
final mContext = navigatorKey.currentState!.context;절대 null 이 아닐 것이란것을 가정한다. Save 가 호출되는 곳은
write 페이지이기 때문에 context 가 존재하지 않을 수 없는것이다.
따라서 전역변수로 추적되는 context 를 매개변수로 받아 
화면 이동이 이루어지게 되는 것이다.
- 서버 통신 - jar 파일 만들기 / 설정 https://inblog.ai/hj/서버-통신하기1-설정-31514
- 서버 통신 - 상세 페이지 보기 https://inblog.ai/hj/서버-통신하기2-상세-페이지-보기-31515
- 서버 통신 - 글쓰기 https://inblog.ai/hj/서버-통신하기3-글쓰기-31518
- 서버 통신 - 게시글 삭제 https://inblog.ai/hj/서버-통신하기4-게시글-삭제-31519
Share article

