• 로그인
  • 장바구니에 상품이 없습니다.

home2 게시판 Flutter 게시판 리스트 뷰 질문

리스트 뷰 질문

4 글 보임 - 1 에서 4 까지 (총 4 중에서)
  • 글쓴이
  • #137582

    iampeel
    참가자
    ListView.builder로 리스트를 만드는 도중 스크롤 하는데 뭔가 시간차이가 나면서 덜덜거리면서 따라오는 느낌이 들더라구요.
    그래서 네이티브(swift)와 플러터로 각각 100개 목록을 만들어서 비교해봤는데 네이티브는 저런 느낌이 없는데 플러터는 있습니다.
    그래서 구글링을 해봤는데 비슷한 이슈가 있더라고요. 1~2년 전. 근데 그게 해결이 된 건지는 잘 모르겠는데.. 
    암튼 AI써서 이렇게 저렇게 다 만들어봤는데 덜덜거리는 느낌은 없앨 수 없었습니다.
    이게 플러터의 한계인지. 다른 방법이 있는지. 궁금합니다...
    
    
    
    
    
    
    
    
    • 이 게시글은 iampeel에 의해 2 월, 2 주 전에 수정됐습니다.
    #137592

    codingapple
    키 마스터
    100개 정도로는 성능이슈가 생기진 않을걸요 이미지가 너무 크거나 코드가 이상해서 계속 재렌더링 된다거나 그럴 수도요
    #137634

    iampeel
    참가자
    그냥 리스트빌더로 만든 코드인데 스크롤할때 내용이 약간 늦게 따라오는데 덜덜거리면서 옵니다.
    동영상입니다.
    https://drive.google.com/file/d/1ym645JPZ6RI2YsNVAnjnNjjBNfLMrtxR/view?usp=drive_link
    ------------------------------------------------------------------------------------------
    import 'package:flutter/material.dart';
    import 'board_titles.dart';
    void main() {
      WidgetsFlutterBinding.ensureInitialized();
      runApp(const MyApp());
    }
    class MyApp extends StatelessWidget {
      const MyApp({super.key});
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: '게시판 제목 목록1',
          theme: ThemeData(
            primarySwatch: Colors.blue,
            scaffoldBackgroundColor: Colors.grey[100],
          ),
          home: const BoardTitlesPage(),
        );
      }
    }
    class BoardTitlesPage extends StatefulWidget {
      const BoardTitlesPage({super.key});
      @override
      State<BoardTitlesPage> createState() => _BoardTitlesPageState();
    }
    class _BoardTitlesPageState extends State<BoardTitlesPage> {
      final List<String> _titles = BoardData.titles;
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(title: const Text('게시판 제목 목록2')),
          body: Column(
            children: [
              Container(
                width: double.infinity,
                color: Colors.blue[700],
                padding: const EdgeInsets.all(16.0),
                child: const Text(
                  '게시판 제목 목록3',
                  style: TextStyle(
                    fontSize: 18.0,
                    fontWeight: FontWeight.bold,
                    color: Colors.white,
                  ),
                ),
              ),
              Expanded(
                child: ListView.builder(
                  itemCount: _titles.length,
                  padding: const EdgeInsets.all(8.0),
                  itemBuilder: (context, index) {
                    return Padding(
                      padding: const EdgeInsets.symmetric(
                        vertical: 4.0,
                        horizontal: 8.0,
                      ),
                      child: Container(
                        height: 60.0,
                        decoration: BoxDecoration(
                          color: Colors.white,
                          borderRadius: BorderRadius.circular(8.0),
                          boxShadow: [
                            BoxShadow(
                              color: Colors.black.withAlpha(13),
                              blurRadius: 2.0,
                              offset: const Offset(0, 1),
                            ),
                          ],
                        ),
                        child: Padding(
                          padding: const EdgeInsets.symmetric(horizontal: 16.0),
                          child: Row(
                            children: [
                              CircleAvatar(
                                radius: 20,
                                backgroundColor: Colors.blue[400],
                                child: const Icon(
                                  Icons.article,
                                  color: Colors.white,
                                  size: 20,
                                ),
                              ),
                              const SizedBox(width: 16.0),
                              Expanded(
                                child: Text(
                                  _titles[index],
                                  style: const TextStyle(
                                    fontSize: 16.0,
                                    fontWeight: FontWeight.w500,
                                  ),
                                  overflow: TextOverflow.ellipsis,
                                  maxLines: 1,
                                ),
                              ),
                            ],
                          ),
                        ),
                      ),
                    );
                  },
                ),
              ),
            ],
          ),
        );
      }
    }
    
    ------------------------------------------------------------------------------------------
    게시판 데이터는 파일 따로 만들었습니다.
    board_titles.dart
    
    // 게시판 제목 데이터
    class BoardData {
    // 게시판 제목 50개의 리스트
    static final List<String> titles = [
    '1플러터 최신 버전 업데이트 소식',
    '2다트 3.0 문법 총정리',
    '3모바일 앱 UI/UX 디자인 트렌드',
    '4플러터 성능 최적화 팁 10가지',
    '5크로스 플랫폼 개발 비교: 플러터 vs 리액트 네이티브',
    '6플러터에서 애니메이션 구현하기',
    '7다트 비동기 프로그래밍 심화과정',
    '8위젯 구성의 모범 사례',
    '9플러터 상태관리 라이브러리 비교',
    '10플러터 웹 개발 가이드',
    '11다크 모드 구현하기',
    '12플러터 네이티브 기능 연동 방법',
    '13플러터 앱 출시 체크리스트',
    '14플러터 폼 검증과 사용자 입력 처리',
    '15플러터 차트 라이브러리 활용법',
    '16플러터 앱 테스트 자동화',
    '17플러터 앱 다국어 지원 구현',
    '18플러터 머티리얼 디자인 3.0 적용하기',
    '19오프라인 모드에서 데이터 처리하기',
    '20플러터 푸시 알림 구현 가이드',
    '21플러터 카메라 및 갤러리 기능 구현',
    '22플러터 앱 보안 강화 방법',
    '23플러터 앱 메모리 최적화 기법',
    '24플러터 사용자 인증 구현 가이드',
    '25플러터 앱 아키텍처 패턴 비교',
    '26플러터 애니메이션 성능 최적화',
    '27플러터 페이지네이션 구현 방법',
    '28플러터 위젯 테스트 작성법',
    '29플러터 앱 성능 프로파일링',
    '30플러터 앱 디자인 시스템 구축',
    '31플러터 코드 리팩토링 팁',
    '32플러터 앱 출시 후 관리 방법',
    '33플러터 앱 CI/CD 파이프라인 구축',
    '34플러터 애널리틱스 연동 방법',
    '35플러터 앱 접근성 향상 가이드',
    '36플러터 상태관리: BLoC 패턴 심화',
    '37플러터 앱 마이그레이션 가이드',
    '38플러터에서 JSON 처리 최적화',
    '39플러터 커스텀 위젯 설계 원칙',
    '40플러터 앱 배포 자동화',
    '41플러터 앱 오류 모니터링 시스템',
    '42플러터 데이터베이스 연동 방법',
    '43플러터 앱 성능 벤치마킹',
    '44플러터 코드 자동 생성 도구',
    '45플러터 지도 API 활용법',
    '46플러터 앱 레이아웃 디버깅',
    '47플러터 코드 품질 관리 방법',
    '48플러터 앱 접근성 테스트',
    '49플러터 앱 성능 측정 지표',
    '50플러터 사용자 피드백 수집 방법',
     ];
    }
    
    ------------------------------------------------------------------------------------------
    계속 고민하고. 구글링하고, ai한테 물어보다가 
    플러터 패키지에 다음과 같은게 있더라고요.
    섹션. 즉, 안에 리스트를 만들어서 쪼개서 넣더라고요. (제 해석이 맞는지 모르겠지만)
    암튼 그러면 지연되면서 오는것도 없고 덜덜 거리면서 오는 것도 없는데
    갑툭튀. 스크롤 하다가 주르륵 생기는 현상이 일어납니다. 근데,  아무리 봐도 나눠서 생성하는 거 같지 않거든요.
    나누긴 하지만 다 만든다음 sectionWidgets 여기에 넣는거 같거든요.
    
    1. 아래 코드에서 주르륵 생기는 거 없애는 방법이 뭘까요... 
    2. 리스트를 만드는데 리스트 빌더가 가장 좋다고 하는데 스크롤 하는데 왜 덜덜거리게 만들었을까??
    3. 방향을 잘못잡은건가?? 뭘 모르고 있는건가??
    이런 생각이 드는데 어떻게 해결해야 할지 모르겠습니다.
    
    아래 코드 동영상입니다.
    https://drive.google.com/file/d/1dkZvteiKZ7vRpipY5PFUsEOP6UHlz9Ac/view?usp=drive_link
    
    ------------------------------------------------------------------------------------------
    import 'package:flutter/material.dart';
    import 'package:sliver_tools/sliver_tools.dart';
    import 'board_titles.dart';
    void main() {
      WidgetsFlutterBinding.ensureInitialized();
      runApp(const MyApp());
    }
    class MyApp extends StatelessWidget {
      const MyApp({super.key});
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: '게시판 제목 목록1',
          theme: ThemeData(
            primarySwatch: Colors.blue,
            scaffoldBackgroundColor: Colors.grey[100],
          ),
          home: const BoardTitlesPage(),
        );
      }
    }
    class BoardTitlesPage extends StatefulWidget {
      const BoardTitlesPage({super.key});
      @override
      State<BoardTitlesPage> createState() => _BoardTitlesPageState();
    }
    class _BoardTitlesPageState extends State<BoardTitlesPage> {
      late final ScrollController _scrollController;
      final List<String> _titles = BoardData.titles;
      static const double _itemHeight = 60.0;
      static const TextStyle _textStyle = TextStyle(
        fontSize: 16.0,
        fontWeight: FontWeight.w500,
      );
      List<String> _getSectionNames() {
        final int sectionCount = (_titles.length / 10).ceil();
        return List.generate(sectionCount, (i) => '섹션 ${i + 1}');
      }
      @override
      void initState() {
        super.initState();
        _scrollController = ScrollController();
      }
      @override
      void dispose() {
        _scrollController.dispose();
        super.dispose();
      }
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(title: const Text('게시판 제목 목록2')),
          body: CustomScrollView(
            controller: _scrollController,
            physics: const ClampingScrollPhysics(),
            cacheExtent: MediaQuery.of(context).size.height * 3,
            slivers: [
              SliverPinnedHeader(
                child: Container(
                  color: Colors.blue[700],
                  padding: const EdgeInsets.all(16.0),
                  child: const Text(
                    '게시판 제목 목록3',
                    style: TextStyle(
                      fontSize: 18.0,
                      fontWeight: FontWeight.bold,
                      color: Colors.white,
                    ),
                  ),
                ),
              ),
              SliverAnimatedPaintExtent(
                duration: const Duration(milliseconds: 1),
                child: MultiSliver(
                  pushPinnedChildren: true,
                  children: _buildHiddenSections(),
                ),
              ),
            ],
          ),
        );
      }
      List<Widget> _buildHiddenSections() {
        final List<Widget> sectionWidgets = [];
        final List<String> sectionNames = _getSectionNames();
        for (int i = 0; i < sectionNames.length; i++) {
          final int startIndex = i * 10;
          final int endIndex = (i + 1) * 10;
          final List<String> sectionItems = _titles.sublist(
            startIndex,
            endIndex > _titles.length ? _titles.length : endIndex,
          );
          sectionWidgets.add(
            MultiSliver(
              children: [
                SliverCrossAxisConstrained(
                  maxCrossAxisExtent: 700,
                  child: SliverClip(
                    child: SliverFixedExtentList(
                      itemExtent: _itemHeight,
                      delegate: SliverChildBuilderDelegate(
                        (context, index) {
                          if (index >= sectionItems.length) return null;
                          return _buildListItem(
                            sectionItems[index],
                            startIndex + index,
                          );
                        },
                        childCount: sectionItems.length,
                      ),
                    ),
                  ),
                ),
              ],
            ),
          );
        }
        return sectionWidgets;
      }
      Widget _buildListItem(String text, int index) {
        return Padding(
          padding: const EdgeInsets.symmetric(
            horizontal: 16.0,
            vertical: 4.0,
          ),
          child: Container(
            decoration: BoxDecoration(
              color: Colors.white,
              borderRadius: BorderRadius.circular(8.0),
              boxShadow: [
                BoxShadow(
                  color: Colors.black.withAlpha(13),
                  blurRadius: 2.0,
                  offset: const Offset(0, 1),
                ),
              ],
            ),
            child: Padding(
              padding: const EdgeInsets.symmetric(horizontal: 16.0),
              child: Row(
                children: [
                  CircleAvatar(
                    radius: 20,
                    backgroundColor: Colors.blue[400],
                    child: const Icon(
                      Icons.article,
                      color: Colors.white,
                      size: 20,
                    ),
                  ),
                  const SizedBox(width: 16.0),
                  Expanded(
                    child: Text(
                      text,
                      style: _textStyle,
                      overflow: TextOverflow.ellipsis,
                      maxLines: 1,
                    ),
                  ),
                ],
              ),
            ),
          ),
        );
      }
    }
     
     
    • 이 답변은 iampeel에 의해 2 월, 2 주 전에 수정됐습니다.
    • 이 답변은 iampeel에 의해 2 월, 2 주 전에 수정됐습니다.
    #137641

    codingapple
    키 마스터
    영상으로 봐서는 덜덜거리는게 뭔지는 모르겠는데 다른 앱들도 스크롤시 약간 늦게 따라오는 경우가 많지 않나요
    아니면 iOS 버전 바뀌면 어쩌다 한번 성능이슈가 있는데 그런걸수도요
    ListView.builder 쓰면 스크롤할 때마다 리스트를 다이나믹하게 생성해주는데 
    ListView안의 위젯에 const붙여보거나 .builder안쓰고 생성해서 테스트해봅시다
    
    
4 글 보임 - 1 에서 4 까지 (총 4 중에서)
  • 답변은 로그인 후 가능합니다.

About

현재 월 700명 신규수강중입니다.

  (09:00~20:00) 빠른 상담은 카톡 플러스친구 코딩애플 (링크)
  admin@codingapple.com
  이용약관
ⓒ Codingapple, 강의 예제, 영상 복제 금지
top

© Codingapple, All rights reserved. 슈퍼로켓 에듀케이션 / 서울특별시 강동구 고덕로 19길 30 / 사업자등록번호 : 212-26-14752 온라인 교육학원업 / 통신판매업신고번호 : 제 2017-서울강동-0002 호 / 개인정보관리자 : 박종흠