페이징 처리 (전체 코드)

김인범's avatar
Jan 18, 2025
페이징 처리 (전체 코드)

list_body

import 'package:flutter/material.dart'; import 'package:flutter_blog/ui/pages/post/detail_page/post_detail_page.dart'; import 'package:flutter_blog/ui/pages/post/list_page/post_list_vm.dart'; import 'package:flutter_blog/ui/pages/post/list_page/wiegets/post_list_item.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:pull_to_refresh/pull_to_refresh.dart'; class PostListBody extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { PostListModel? model = ref.watch(postListProvider); PostListVM vm = ref.read(postListProvider.notifier); // << refreshCtrl 때문에 사용. if (model == null) { return Center(child: CircularProgressIndicator()); } else { return SmartRefresher( controller: vm.refreshCtrl, // enablePullUp과 onRefresh 두개는 쌍을 이룬다. enablePullUp: true, onRefresh: () async => await vm.init(), // enablePullDown과 onLoading 두개가 쌍을 이룬다. 다음 페이지 가져오는 메서드 enablePullDown: true, onLoading: () async => await vm.nextList(), child: ListView.separated( itemCount: model.posts.length, itemBuilder: (context, index) { return InkWell( onTap: () { Navigator.push( context, MaterialPageRoute( builder: (_) => PostDetailPage(model.posts[index].id!))); }, child: PostListItem(model.posts[index]), ); }, separatorBuilder: (context, index) { return const Divider(); }, ), ); } } }

list_ViewModel

init 수정 사항
nextList 페이징, 페이지 로드
import 'package:flutter/material.dart'; import 'package:flutter_blog/data/repository/post_repository.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:pull_to_refresh/pull_to_refresh.dart'; import '../../../../data/model/post.dart'; import '../../../../main.dart'; class PostListModel { bool isFirst; bool isLast; int pageNumber; int size; int totalPage; List<Post> posts; PostListModel( {required this.isFirst, required this.isLast, required this.pageNumber, required this.size, required this.totalPage, required this.posts}); PostListModel copyWith( {bool? isFirst, bool? isLast, int? pageNumber, int? size, int? totalPage, List<Post>? posts}) { return PostListModel( isFirst: isFirst ?? this.isFirst, isLast: isLast ?? this.isLast, pageNumber: pageNumber ?? this.pageNumber, size: size ?? this.size, totalPage: totalPage ?? this.totalPage, posts: posts ?? this.posts); } PostListModel.fromMap(Map<String, dynamic> map) : isFirst = map["isFirst"], isLast = map["isLast"], pageNumber = map["pageNumber"], size = map["size"], totalPage = map["totalPage"], posts = (map["posts"] as List<dynamic>) .map((e) => Post.fromMap(e)) .toList(); // 묵시적 형변환 List<dynamic>으로 list타입이 된다. & map<String,dynamic>으로 받게된다. 이걸 Post.fromMap으로 하나씩 리스트에 넣는다. } class PostList_Post {} class PostList_User {} final postListProvider = NotifierProvider<PostListVM, PostListModel?>(() { return PostListVM(); }); class PostListVM extends Notifier<PostListModel?> { final refreshCtrl = RefreshController(); final mContext = navigatorKey.currentContext!; PostRepository postRepo = const PostRepository(); @override PostListModel? build() { init(); return null; } // 1. 페이지 초기화 Future<void> init() async { Map<String, dynamic> responseBody = await postRepo.findAll(); if (!responseBody["success"]) { ScaffoldMessenger.of(mContext!).showSnackBar( SnackBar( content: Text("게시글 목록보기 실패 : ${responseBody["errorMessage"]}")), ); return; } state = PostListModel.fromMap(responseBody["response"]); // init 메서드가 종료되면 UI 도는거 멈추게 해준다. refreshCtrl.refreshCompleted(); } void remove(int id) { PostListModel model = state!; model.posts = model.posts.where((p) => p.id != id).toList(); state = state!.copyWith(posts: model.posts); } void add(Post post) { PostListModel model = state!; model.posts = [post, ...model.posts]; state = state!.copyWith(posts: model.posts); } // 페이징, 페이지 로드 Future<void> nextList() async { PostListModel model = state!; if (model.isLast) { await Future.delayed(Duration(milliseconds: 500)); refreshCtrl.loadComplete(); return; } // 마지막 페이지가 아닐 시 Map<String, dynamic> responseBody = await postRepo.findAll(page: state!.pageNumber + 1); if (!responseBody["success"]) { ScaffoldMessenger.of(mContext!).showSnackBar( SnackBar(content: Text("게시글 로드 실패 : ${responseBody["errorMessage"]}")), ); return; } PostListModel nextModel = PostListModel.fromMap(responseBody["response"]); PostListModel prevModel = state!; // 이전 상태(prevModel) 다음에 nextModel을 추가한다. state = nextModel.copyWith(posts: [...prevModel.posts, ...nextModel.posts]); refreshCtrl.loadComplete(); } }
 
Share article

taker