Flutter - StatefulWidget 활용 - 실시간 검색 기능 구현하기

반응형

 

Overview

  • 실시간 검색기능으로 검색어와 일치하는 항목 출력

 

 

ListView 목록 구현 포스팅 관련

 

포스팅에 사용된 예시 프로젝트 파일 다운로드

 

GitHub - luvris2/flutter_memo_app

Contribute to luvris2/flutter_memo_app development by creating an account on GitHub.

github.com


StatefulWidget 위젯

설명

  • 변경 가능한 상태를 가진 위젯
  • 위젯이 빌드될 때 동기적으로 읽을 수 있음
  • 위젯의 수명동안 변경될 수 있음
  • 상태 변경 시, State.setState를 사용하여 State에 즉시 알리도록 코딩
  • StatefulWidget을 확장할 때마다 createState 호출

* StatelessWidget은 주어진 특정 구성 및 주변 상태에 항상 동일한 방식으로 빌드되는 위젯


구문

void main() {
  runApp(MyApp());
}

// 메인 앱에서 statefulWidget 클래스 상속
class MyApp extends StatefulWidget {
  const MyApp({Key? key}) : super(key: key);

  // 상태 변경을 위해 클래스 오버라이딩
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  // 앱을 동적으로 변경할 코드 작성
}
  • extends StatefulWidget : 앱의 동적 표시를 위해 변경 가능한 위젯 상속
  • createState() : 위젯에 대한 변경 가능한 상태로 설정
  • 변수나 클래스 앞에 언더바(_)가 있는 이유?
    • private 형태임을 암시하는 키워드, 즉 _MyAppState 클래스는 공개되지 않는 형태임을 암시

설계 및 코딩

요구사항

  • 텍스트 필드를 추가하여 검색어를 입력할 수 있도록 함
  • 메인 클래스를 StatefulWidget 클래스에 상속
  • createState를 이용하여 앱이 동적으로 작동하도록 변경 가능 상태로 설정 (검색을 위해) 
  • 리스트뷰에 표시되어 있는 항목의 문자열과 검색어가 맞는지 확인
  • 검색어가 일치할 경우 일치한 항목을 리스트뷰에 출력
  • 검색어가 일치하지 않을 경우 아무런 항목을 반환하지 않음
  • 검색어가 없으면 모든 항목 표시

코드 설계

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

// 검색어
String searchText = '';

// 리스트뷰에 표시할 내용
List<String> items = ['Item 1', 'Item 2', 'Item 3', 'Item 4'];
List<String> itemContents = [
  'Item 1 Contents',
  'Item 2 Contents',
  'Item 3 Contents',
  'Item 4 Contents'
];

// 검색을 위해 앱의 상태를 변경해야하므로 StatefulWidget 상속
class MyApp extends StatefulWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  MyAppState createState() => MyAppState();
}

// 메인 클래스의 상태 상속
class MyAppState extends State<MyApp> {
  // 리스트뷰 카드 클릭 이벤트 핸들러
  void cardClickEvent(BuildContext context, int index) {
    String content = itemContents[index];
    Navigator.push(
      context,
      MaterialPageRoute(
        // 정의한 ContentPage의 폼 호출
        builder: (context) => ContentPage(content: content),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'MemoApp', // 앱의 아이콘 이름
      home: Scaffold(
        appBar: AppBar(
          centerTitle: true,
          title: Text('Search Example'), // 앱 상단바 설정
        ),
        body: Column(
          children: <Widget>[
            Padding(
              padding: const EdgeInsets.all(20.0),
              child: TextField(
                decoration: InputDecoration(
                  hintText: '검색어를 입력해주세요.',
                  border: OutlineInputBorder(),
                ),
                onChanged: (value) {
                  setState(() {
                    searchText = value;
                  });
                },
              ),
            ),
            Expanded(
              child: ListView.builder(
                // items 변수에 저장되어 있는 모든 값 출력
                itemCount: items.length,
                itemBuilder: (BuildContext context, int index) {
                  // 검색 기능, 검색어가 있을 경우
                  if (searchText.isNotEmpty &&
                      !items[index]
                          .toLowerCase()
                          .contains(searchText.toLowerCase())) {
                    return SizedBox.shrink();
                  }
                  // 검색어가 없을 경우, 모든 항목 표시
                  else {
                    return Card(
                      elevation: 3,
                      shape: RoundedRectangleBorder(
                          borderRadius:
                              BorderRadius.all(Radius.elliptical(20, 20))),
                      child: ListTile(
                        title: Text(items[index]),
                        onTap: () => cardClickEvent(context, index),
                      ),
                    );
                  }
                },
              ),
            ),
          ],
        ),
      ),
    );
  }
}

// 선택한 항목의 내용을 보여주는 추가 페이지
class ContentPage extends StatelessWidget {
  final String content;

  const ContentPage({Key? key, required this.content}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Content'),
      ),
      body: Center(
        child: Text(content),
      ),
    );
  }
}

실행


참고

flutter - Widget - StatefulWidget abstract class

반응형