Develop/Flutter

[Flutter]12 자식위젯이 부모위젯의 state 변경하기

dawonny 2022. 2. 20. 04:32
728x90
반응형

ref : 코딩 애플[Flutter로 만드는 iOS, Android 앱] 강의


부모에 total 이라는 state 를 만들어보자

total == 앱에 저장된 친구수

이 total 값은 appBar 에도 확인할 수 있게 해놓았다.

그리고 다이얼로그안에 있는 완료버튼을 눌렀을 때 저 값이 +1씩 되도록 만들어보려고한다.

저 다이얼로그가 새로운 친구 등록 화면임

 

부모위젯에 있는 state 인 total을 자식위젯에서 변경하고 싶으면 

1. 일단 부모에 수정함수를 작성

  addOne이라는 수정함수를 부모위젯에 작성했다.

  state 를 수정하려면 setState에 넣어야했었다.

2. 자식위젯으로 보내기

왼쪽에는 작명

오른쪽에는 실제로 보내고 싶은 변수명(함수명)을 쓰면된다.

 

3. 그리고 자식위젯에 등록을 한다.

 

4. 실제로 사용한다.

   완료 버튼이 onPressed 일 때 함수가 실행되도록 짰다.

아래처럼 잘 실행되는 것을 볼 수 있다.


input 데이터 다루기(유저가 입력한 데이터 다루기)

 

사용자가 입력한 데이터를 변수같은거에 저장하고 싶다면 controller: 라는 문법을 써야함

  var inputData = TextEditingController();

다이얼로그 자식위젯 안에다가 inputData 라는 변수를 만들어줬다.

이 때 TextEditingController() 을 썼다.

            TextField( controller: inputData,),

그 다음에 TextField 안에다가 controller 뒤에 변수이름을 써주면 저 변수에 데이터가 저장된다.


controller 보다 직관적인 방법이 있는데

onChanged : () {}  라는 방법이다.

onPressed 와 유사하게, 입력한 값이 변하게 되면 코드를 실행해달라는 의미

text 는 사용자가 입력한 값이고

입력한 값이 inputData2 라는 변수에 저장되게 될거다.


TextField가 많을 경우엔 어떻게 해야할까?

변수를 여러개 만들게 아니라 

[ ] List

{ } Map

같은 거 활용하자


 

실습과제
다이얼로그에 TextField에다가 이름 입력하고 완료 누르면
4번째 친구로써 추가가 되어야한다.

name 이라는 리스트 state 에 새로운 친구의 이름을 추가하면 재렌더링이 될 것같다.

입력받은 변수를 addOne 함수와 써먹으면 될 것같다.


import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
      home : MyApp()
   )
  );
}



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

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  var total = 3;
  var like = [0,0,0];
  var name = ['해리', '론', '헤르미온느'];

  addOne(inputData){
    setState(() {
      name.add(inputData.toString());
    });
  }
  @override
  Widget build(BuildContext context) {

    return Scaffold(
          floatingActionButton: FloatingActionButton(
            onPressed: (){
              showDialog(context: context, builder: (context){
                return DialogUI( addOne : addOne);
              });
            },
          ),
            appBar: AppBar(title: Text(total.toString()),),
            bottomNavigationBar: BottomAppBar(),
            body: ListView.builder(
                itemCount: name.length,
                itemBuilder: (c, i){
                  return ListTile(
                    leading: Icon(Icons.account_box),
                    title: Text(name[i])
                  );
                },
            )
            );


  }
}

class DialogUI extends StatelessWidget {
  DialogUI({Key? key, this.addOne}) : super(key: key);
  final addOne;
  var inputData = TextEditingController();
  @override
  Widget build(BuildContext context) {
    return Dialog(
      child: SizedBox(
        width: 300,
        height: 300,
        child: Column(
          children: [
            TextField( controller: inputData,),
            TextButton( child: Text('완료'),
              onPressed: (){ addOne(inputData); },),
            TextButton( child: Text('취소'),
              onPressed: (){ Navigator.pop(context);},),

          ],
        ),
      )
    );
  }
}

일단 addOne 함수에 inputData 를 받으면 name 이라는 리스트 state 에 inputData 를 추가하도록 해봤고

ListView.builder안에서 itemCount 는 3에서 name.length 로 수정해줬다.

뭔가 이상하다 

추가는 되는데 추가되는 내용이 이상하다.

뭐가문제지


보니까 addName에 들어가는 inputData의 타입을 .toString() 으로 해주지않고

.text 로 해주었더니 잘 들어가진다.

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
      home : MyApp()
   )
  );
}



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

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  var total = 3;
  var like = [0,0,0];
  var name = ['해리', '론', '헤르미온느'];

  addName(a){
    setState(() {
      name.add(a);
    });
  }
  addOne(){
    setState(() {
      total ++;
    });
  }
  @override
  Widget build(BuildContext context) {

    return Scaffold(
          floatingActionButton: FloatingActionButton(
            onPressed: (){
              showDialog(context: context, builder: (context){
                return DialogUI( addOne : addOne, addName : addName );
              });
            },
          ),
            appBar: AppBar(title: Text('저는 친구가 ${name.length} 명 입니다.'),),
            bottomNavigationBar: BottomAppBar(),
            body: ListView.builder(
                itemCount: name.length,
                itemBuilder: (c, i){
                  return ListTile(
                    leading: Icon(Icons.account_box),
                    title: Text(name[i].toString())
                  );
                },
            )
            );


  }
}

class DialogUI extends StatelessWidget {
  DialogUI({Key? key, this.addOne, this.addName}) : super(key: key);
  final addOne;
  final addName;
  var inputData = TextEditingController();
  @override
  Widget build(BuildContext context) {
    return Dialog(
      child: SizedBox(
        width: 300,
        height: 300,
        child: Column(
          children: [
            TextField( controller: inputData,),
            TextButton( child: Text('완료'),
              onPressed: (){
              addOne();
              addName(inputData.text);
              Navigator.pop(context);
              },),
            TextButton( child: Text('취소'),
              onPressed: (){ Navigator.pop(context);},),

          ],
        ),
      )
    );
  }
}

완료버튼을 눌렀을때 추가로 창이 닫히게도 구현해봤는데

+ 추가로...

빈칸이면 완료눌러도 추가가 안되도록 하는 기능을 추가해봐야겠다.

+ 그리고 이름 옆에 삭제 버튼

+ 이름순 정렬버튼

+ 폰 번호 추가

등등 스스로 시도해보자..!


일단 빈칸일때에 완료누르면 추가가 안되도록 하는 기능은 조건문 사용해서 만들었다.

addName(a){
    if (a == ''){
      return;
    }
    else{
      setState(() {
        name.add(a);
      });
    }

완료버튼을 눌렀을 때에 

나중엔 '이름을 입력해주세요!' 같은 안내가 나오면 더 좋을 것 같다.


버튼위에 + 모양을 넣어보았다.

child 안에 Text 를 넣고 style 중에 TextStyle 을 찾아 fontSize 를 30정도로 주었다.

그리고 삭제기능도 구현해보고 싶어서 delete 버튼을 만들어봤다.

기존에 있던 ListTile에서 trailing 을 써봤더니 내가 원하는 대로 각 라인의 맨 뒤에 자리 잡았다.

TextButton 으로 만들어 주었다.

그리고 TextButton 의 onPressed 안에 delete 함수를 넣어서 i번째 인덱스의 이름은 삭제되도록 했다.

delete 함수는 위에서 따로 만들었다.

delete 버튼 눌러봤는데 잘 지워지고 앱바에 있는 사람 수도 잘 줄어든다.

 

728x90
반응형