Don't think! Just do it!

종합 IT 기술 정체성 카오스 블로그! 이... 이곳은 어디지?

Flutter/Flutter Project

[소셜차트] 앱 제작기 #9. 무한 스크롤

방피터 2022. 11. 7. 15:09

유저가 작성한 인사이트 카드를 여러사람에게 맞춰 보여주는 건 쉽지 않은 일이겠지? ㅋㅋ 아마 Query가 마이 복잡해 질거야. 앱에서 firestore에 다이렉트로 못 할 수도 있고.. 이럴 경우에는 Backend에서 해야겠지. 그렇다고 손놓고 있으면 등신이고. 걍 일단 시간순으로 정렬해서 listview로 보여주자고..

insight card

Insight card는 단순하게 header, body, bottom 이렇게 3부분으로 나뉘는데 header에는 chart의 요약 정보가 들어갈거고 body에는 유저, 카드 내용이, 그리고 bottom에는 스크랩, 댓글 달기, 외부 공유를 할 수 있는 버튼이 있어. header와 bottom은 구현해야할 게 엄청 많으니까 일단 body만 읽어와서 뿌려줄건데 처음에는 귀엽게 10개를 고정으로 읽어보자 ㅋ

 

우선 컨트롤러의 onInit에서 순서대로 정렬한 insightcard 10개를 읽도록 해놓고. 👇

class ScreenHomeController extends GetxController {
  var userInsightCardColRef =
      firestore.collection("userInsightCard").withConverter(
            fromFirestore: (snapshot, options) =>
                InsightCardModel.fromJson(snapshot.data()!),
            toFirestore: (value, options) => value.toJson(),
          );

  var _insightCards = Rx<List<QueryDocumentSnapshot<InsightCardModel>>>([]);

  List<QueryDocumentSnapshot<InsightCardModel>> get insightCards =>
      _insightCards.value;

  @override
  void onInit() {
    // TODO: implement onInit
    super.onInit();
    userInsightCardColRef.orderBy("createdAt").limit(10).get().then(
      (value) {
        _insightCards.value = value.docs;//읽어왓!
      },
    );
  }

그리고 Obx로 감싼 ListView.builder에 item으로 InsightCard위젯을 넣어주면 끝.

class ScreenHome extends GetView<ScreenHomeController> {
  const ScreenHome({super.key, this.navKey});
  final NavKeys? navKey;
  static const routeName = '/ScreenHome';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: MainAppBar(appBar: AppBar(), title: "Social Chart"),
      body: Container(
        color: Colors.black12,
        child: Obx(
          () => ListView.builder(
            itemCount: controller.insightCards.length,
            itemBuilder: (context, index) {
              return InsightCard(
                navKey: navKey,
                cardInfo: controller.insightCards[index].data(),// 데이터로 카드 그렷!
              );
            },
          ),
        ),
      ),
    );
  }
}

그럼 아래처럼 보이게 되는데 ㅎㅎㅎ 못났네 ㅋㅋ 

10개를 읽어서 뿌렷!

여기에 이제 linkPreview도 보여주고, Text 위젯도 정렬하고 ㅎㅎ 개발하는 도중이지만 이쁜게 좋잖아?👇

좀 더 이쁘게!

사실 이와중에 AnyLinkPreivew가 맘에 안들어서 새로 만들었어. 로드한 이미지 사이즈에 따라서 배치를 변경하고 싶었는데 AnyLinkPreview는 안되더라고;;;

새로 만든 linkPreview

새로 만든 LinkPreview는 CachedNetworkImage 위젯의 imagebuilder를 통해서 구현했어.👇 이렇게 하면 로드할 이미지 사이즈도 알고 image cache도 하고! 개이득!!

...
...
CachedNetworkImage(
  imageUrl: imageUrl,
  width: 50,
  imageBuilder: (context, imageProvider) {
    Completer<ui.Image> completer = Completer<ui.Image>();
    imageProvider
        .resolve(const ImageConfiguration())
        .addListener(ImageStreamListener((ImageInfo info, bool _) {
      completer.complete(info.image);
    }));
    return FutureBuilder<ui.Image>(
      future: completer.future,
      builder: (context, snapshot) {
        if (snapshot.hasData) {
          ui.Image image = snapshot.data!;
          if (image.width > 800) {
...
...

 

이제 좀 봐줄만 하니까 pagination을 해보자. 계속 listview를 쓰려고 했었는데 음.. 뭔가 더 좋은 옵션들이 있는 거 같더라구.👇 이런거 말이지... Flutter Favorite 딱지에다가 좋아요도 2k가 넘고 ㅎㅎ 이런 건 써야지ㅎㅎ

https://pub.dev/packages/infinite_scroll_pagination/example

 

infinite_scroll_pagination | Flutter Package

Lazily load and display pages of items as the user scrolls down your screen.

pub.dev

컨트롤러는 아래와 같이 구성. firestore부분만 추가되었을 뿐 infinite scroll pagination에서 제공한 예제와 다를바 없어.

class ScreenHomeController extends GetxController {
  
  PagingController<DocumentSnapshot<Object?>?,
          QueryDocumentSnapshot<InsightCardModel>> pageController =
      PagingController(firstPageKey: null);

  final _pageSize = 10;

  var userInsightCardColRef =
      firestore.collection("userInsightCard").withConverter(
            fromFirestore: (snapshot, options) =>
                InsightCardModel.fromJson(snapshot.data()!),
            toFirestore: (value, options) => value.toJson(),
          );

  final _insightCards = Rx<List<QueryDocumentSnapshot<InsightCardModel>>>([]);

  List<QueryDocumentSnapshot<InsightCardModel>> get insightCards =>
      _insightCards.value;

  void fetchInsightCard(DocumentSnapshot<Object?>? pageKey) async {
    QuerySnapshot<InsightCardModel> loadedInsightCard;
    if (pageKey != null) {
      loadedInsightCard = await userInsightCardColRef
          .orderBy("createdAt", descending: true)
          .startAfterDocument(pageKey)
          .limit(_pageSize)
          .get();
    } else {
      loadedInsightCard = await userInsightCardColRef
          .orderBy("createdAt", descending: true)
          .limit(_pageSize)
          .get();
    }
    final isLastPage = loadedInsightCard.docs.length < _pageSize;
    if (isLastPage) {
      pageController.appendLastPage(loadedInsightCard.docs);
    } else {
      final nextPageKey = loadedInsightCard.docs.last;
      pageController.appendPage(loadedInsightCard.docs, nextPageKey);
    }
  }

  @override
  void onInit() async {
    // TODO: implement onInit
    super.onInit();
    pageController.addPageRequestListener((pageKey) {
      fetchInsightCard(pageKey);
    });
  }

그리고 View에서는 👇👇 아래와 같이 PagedListView를 사용했지. 마찬가지로 infinite scroll pagination에서 제공한 예제와 크게 다르지 않지.

class ScreenHome extends GetView<ScreenHomeController> {
  const ScreenHome({super.key, this.navKey});
  final NavKeys? navKey;
  static const routeName = '/ScreenHome';

  @override
  // TODO: implement tag
  String? get tag => navKey?.name;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: MainAppBar(appBar: AppBar(), title: "Social Chart"),
      body: PagedListView(
          pagingController: controller.pageController,
          builderDelegate: PagedChildBuilderDelegate<
                  QueryDocumentSnapshot<InsightCardModel>>(
              itemBuilder: ((context, item, index) {
            return InsightCard(navKey: navKey, cardInfo: item.data());
          }))),
    );
  }
}

테스트를 해보면 👇👇 잘 동작해! 넘나 스무스하게 잘 동작하는 군!

pagination 잘 동작함!

infinite_scroll_pagination에는 이거 말고도 기능이 엄청 많은 거 같은데 미리 다 공부하고 시작할 수는 없으니 더 자세하게 기능을 구현해가면서 공부해봐야지 ㅎㅎㅎ 우선 이렇게 무한 스크롤을 하나의 위젯으로 정리해두고 여기 저기에 사용하면 될 것 같아. 휴~ 직장을 다니던 사업을 하던 월요일은 힘드네 ㅎㅎ! 다들 화이팅!!

반응형