상세 컨텐츠

본문 제목

Sliver를 활용한 비대칭 레이아웃과 복잡한 데이터 렌더링 기법

공부/Flutter

by micalcomanie 2025. 1. 1. 17:14

본문

728x90
반응형
SMALL

Flutter의 Sliver는 비대칭 레이아웃을 손쉽게 구현하고 대규모 데이터 렌더링을 최적화할 수 있는 강력한 도구입니다.
이 글에서는 Sliver를 활용하여 비대칭 레이아웃을 구성하고, 복잡한 데이터를 효과적으로 처리하는 방법을 소개합니다.


1. 비대칭 레이아웃이란?

비대칭 레이아웃은 각 요소가 고정된 크기나 비율이 아닌, 다양한 크기와 배치를 가지는 레이아웃을 뜻합니다. 예: Pinterest 스타일의 카드형 UI.


2. 비대칭 레이아웃 구현하기

Flutter의 SliverGrid와 사용자 정의 SliverGridDelegate를 활용하여 비대칭 레이아웃을 구성할 수 있습니다.

2.1 사용자 정의 SliverGridDelegate 구현

SliverGridDelegate를 확장하여 다양한 크기를 가진 그리드를 정의합니다.

import 'package:flutter/material.dart';

class AsymmetricGridDelegate extends SliverGridDelegate {
  @override
  SliverGridLayout getLayout(SliverConstraints constraints) {
    return SliverGridRegularTileLayout(
      crossAxisCount: 2,
      mainAxisStride: 300.0,
      crossAxisStride: constraints.crossAxisExtent / 2,
      childMainAxisExtent: 200.0,
      childCrossAxisExtent: constraints.crossAxisExtent / 2,
    );
  }

  @override
  bool shouldRelayout(SliverGridDelegate oldDelegate) => false;
}

2.2 SliverGrid와 사용자 정의 Delegate 결합

SliverGrid(
  delegate: SliverChildBuilderDelegate(
    (context, index) {
      return Container(
        color: index.isEven ? Colors.amber : Colors.blue,
        child: Center(
          child: Text('Item $index'),
        ),
      );
    },
    childCount: 20,
  ),
  gridDelegate: AsymmetricGridDelegate(),
)

3. 복잡한 데이터 렌더링 기법

3.1 동적 데이터 렌더링

API에서 데이터를 가져와 SliverList 또는 SliverGrid로 표시합니다.

import 'package:flutter/material.dart';

class DynamicDataSliver extends StatefulWidget {
  @override
  _DynamicDataSliverState createState() => _DynamicDataSliverState();
}

class _DynamicDataSliverState extends State<DynamicDataSliver> {
  List<String> _items = [];

  @override
  void initState() {
    super.initState();
    _fetchData();
  }

  Future<void> _fetchData() async {
    // Mock API call
    await Future.delayed(Duration(seconds: 2));
    setState(() {
      _items = List.generate(30, (index) => 'Item $index');
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: CustomScrollView(
        slivers: [
          SliverList(
            delegate: SliverChildBuilderDelegate(
              (context, index) => ListTile(
                title: Text(_items[index]),
              ),
              childCount: _items.length,
            ),
          ),
        ],
      ),
    );
  }
}

3.2 무한 스크롤 구현

ScrollController를 사용하여 스크롤 끝에 도달할 때 데이터를 추가합니다.

class InfiniteScrollSliver extends StatefulWidget {
  @override
  _InfiniteScrollSliverState createState() => _InfiniteScrollSliverState();
}

class _InfiniteScrollSliverState extends State<InfiniteScrollSliver> {
  final ScrollController _scrollController = ScrollController();
  List<String> _items = List.generate(20, (index) => 'Item $index');

  @override
  void initState() {
    super.initState();
    _scrollController.addListener(_onScroll);
  }

  void _onScroll() {
    if (_scrollController.position.pixels >=
        _scrollController.position.maxScrollExtent - 100) {
      _loadMoreItems();
    }
  }

  void _loadMoreItems() {
    setState(() {
      final newItems = List.generate(20, (index) => 'Item ${_items.length + index}');
      _items.addAll(newItems);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: CustomScrollView(
        controller: _scrollController,
        slivers: [
          SliverList(
            delegate: SliverChildBuilderDelegate(
              (context, index) => ListTile(
                title: Text(_items[index]),
              ),
              childCount: _items.length,
            ),
          ),
        ],
      ),
    );
  }

  @override
  void dispose() {
    _scrollController.dispose();
    super.dispose();
  }
}

4. 실습: 비대칭 그리드와 동적 데이터 결합

import 'package:flutter/material.dart';

class AsymmetricDynamicGrid extends StatefulWidget {
  @override
  _AsymmetricDynamicGridState createState() => _AsymmetricDynamicGridState();
}

class _AsymmetricDynamicGridState extends State<AsymmetricDynamicGrid> {
  List<String> _items = [];
  bool _isLoading = true;

  @override
  void initState() {
    super.initState();
    _fetchData();
  }

  Future<void> _fetchData() async {
    await Future.delayed(Duration(seconds: 2)); // Simulate network delay
    setState(() {
      _items = List.generate(50, (index) => 'Item $index');
      _isLoading = false;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Asymmetric Dynamic Grid')),
      body: _isLoading
          ? Center(child: CircularProgressIndicator()) // Show a loading spinner
          : CustomScrollView(
        slivers: [
          SliverPadding(
            padding: const EdgeInsets.all(10.0),
            sliver: SliverGrid(
              delegate: SliverChildBuilderDelegate(
                    (context, index) => Container(
                  color: index.isEven ? Colors.teal : Colors.orange,
                  child: Center(
                    child: Text(
                      _items[index],
                      style: TextStyle(color: Colors.white),
                    ),
                  ),
                ),
                childCount: _items.length,
              ),
              gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: 2,
                mainAxisSpacing: 10,
                crossAxisSpacing: 10,
              ),
            ),
          ),
        ],
      ),
    );
  }
}

void main() => runApp(MaterialApp(home: AsymmetricDynamicGrid()));

5. 결론

Sliver를 활용한 비대칭 레이아웃과 복잡한 데이터 렌더링은 사용자 경험을 크게 향상시킬 수 있습니다. 이 글에서 다룬 기법을 바탕으로 독창적이고 성능 좋은 UI를 제작해 보세요. 다음 글에서는 Sliver와 애니메이션을 결합한 고급 패턴을 다룰 예정입니다.


참고 자료

728x90
반응형
LIST

관련글 더보기