본문 바로가기
프로그래밍/Flutter & Dart

Flutter & Firebase - 인스타그램 클론 (3) - 사용자 검색화면 만들기

by 어느덧중반 2020. 9. 2.
반응형



오늘의 목표

사용자를 검색하는 사용자 검색화면(Search Page)을 만들어보자.
검색어 입력시 입력한 검색어와 일치하거나 검색어를 포함하는 사용자 리스트를 보여주게 된다.

#1. 검색창 화면 구성

#2. 검색창(상단) 만들기

#3. 검색결과(하단) 만들기

#4. 참고내용

#5. 결과화면

 


#1. 검색창 화면 구성

 - 상단 : appBar로 된 검색창

 - 하단 : 검색 결과를 보여줄 ListView

@override
Widget build(BuildContext context) {
  return Scaffold(
    backgroundColor: Colors.black,
    appBar: searchPageHeader(),
    body: futureSearchResults == null ? displayNoSearchResultScreen() : displayUsersFoundScreen(),
  );
}

 

#2. 검색창(상단) 만들기

// 검색창 입력내용 controller
TextEditingController searchTextEditingController = TextEditingController();
// DB에서 검색된 사용자를 가져오는데 활용되는 변수
Future<QuerySnapshot> futureSearchResults;

// X 아이콘 클릭시 검색어 삭제
emptyTheTextFormField() {
  searchTextEditingController.clear();
}

// 검색어 입력후 submit하게되면 DB에서 검색어와 일치하거나 포함하는 결과 가져와서 future변수에 저장
controlSearching(str) {
  print(str);
  Future<QuerySnapshot> allUsers = userReference.where('profileName', isGreaterThanOrEqualTo: str).get();
  setState(() {
    futureSearchResults = allUsers;
  });
}

// 검색페이지 상단부분
AppBar searchPageHeader() {
  return AppBar(
    backgroundColor: Colors.black,
    title: TextFormField(
      controller: searchTextEditingController, // 검색창 controller
      decoration: InputDecoration(
        hintText: 'Search here....',
        hintStyle: TextStyle(
          color: Colors.grey,
        ),
        enabledBorder: UnderlineInputBorder(
          borderSide: BorderSide(color: Colors.grey,)
        ),
        focusedBorder: UnderlineInputBorder(
            borderSide: BorderSide(color: Colors.white,)
        ),
        filled: true,
        prefixIcon: Icon(Icons.person_pin, color: Colors.white, size: 30),
        suffixIcon: IconButton(icon: Icon(Icons.clear, color: Colors.white,), onPressed: emptyTheTextFormField)
      ),
      style: TextStyle(
        fontSize: 18,
        color: Colors.white
      ),
      onFieldSubmitted: controlSearching,
    )
  );
}

 

#3. 검색결과(하단) 만들기

 - 검색어 입력 전 초기상태 (displayNoSearchResultScreen)

 - 검색어로 검색 후 결과화면 (displayUsersFoundScreen)

 - 결과화면의 각 사용자 ListTile 부분은 UserResult로 표현

displayNoSearchResultScreen() {
  final Orientation orientation = MediaQuery.of(context).orientation;
  return Container(
    child: Center(
      child: ListView(
        shrinkWrap: true,
        children: <Widget>[
          Icon(Icons.group, color: Colors.grey, size: 150),
          Text(
            'Search Users',
            textAlign: TextAlign.center,
            style: TextStyle(
              color: Colors.white,
              fontWeight: FontWeight.w500,
              fontSize: 40
            ),
          ),
        ],
      )
    )
  );
}

displayUsersFoundScreen() {
  return FutureBuilder(
    future: futureSearchResults,
    builder: (context, snapshot) {
      if(!snapshot.hasData) {
        return circularProgress();
      }

      List<UserResult> searchUserResult = [];
      snapshot.data.documents.forEach((document) {
        User users = User.fromDocument(document);
        UserResult userResult = UserResult(users);
        searchUserResult.add(userResult);
      });

      return ListView(
        children: searchUserResult
      );
    }
  );
}


...

class UserResult extends StatelessWidget {
  final User eachUser;
  UserResult(this.eachUser);

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: EdgeInsets.all(3),
      child: Container(
        color: Colors.white54,
        child: Column(
          children: <Widget>[
            GestureDetector(
              onTap: () {
                print('tapped');
              },
              child: ListTile(
                leading: CircleAvatar(
                  backgroundColor: Colors.black,
                  backgroundImage: eachUser.url == null ? circularProgress() : CachedNetworkImageProvider(eachUser.url,),
                ),
                title: Text(eachUser.profileName, style: TextStyle(
                  color: Colors.black,
                  fontSize: 16,
                  fontWeight: FontWeight.bold,
                )),
                subtitle: Text(eachUser.username, style: TextStyle(
                  color: Colors.black,
                  fontSize: 13,
                )),
              ),
            )
          ],
        ),
      )
    );
  }
}

 

#4. 참고내용

 - 다른 탭으로 이동했다가 다시 해당 탭으로 오게되면 initState를 불러 초기화할 수가 있는데
  with AutomaticKeepAliveClientMixin<SearchPage> 를 추가하여 초기화가 안되도록 설정해주면 된다.

// with AutomaticKeepAliveClientMixin를 추가해주면 다른 Tap으로 다녀와도 이전 검색기록이 남아 있게 된다.
class _SearchPageState extends State<SearchPage> with AutomaticKeepAliveClientMixin<SearchPage>{

...

  // with AutomaticKeepAliveClientMixin를 추가해주고 아래 값을 true로 설정해주면 다른탭 다녀와도 initState안함
  @override
  bool get wantKeepAlive => true;

...

}

 

 

#5. 결과화면

 



반응형

댓글