Flutter의 Sliver는 비대칭 레이아웃을 손쉽게 구현하고 대규모 데이터 렌더링을 최적화할 수 있는 강력한 도구입니다.
이 글에서는 Sliver를 활용하여 비대칭 레이아웃을 구성하고, 복잡한 데이터를 효과적으로 처리하는 방법을 소개합니다.
비대칭 레이아웃은 각 요소가 고정된 크기나 비율이 아닌, 다양한 크기와 배치를 가지는 레이아웃을 뜻합니다. 예: Pinterest 스타일의 카드형 UI.
Flutter의 SliverGrid와 사용자 정의 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;
}
SliverGrid(
delegate: SliverChildBuilderDelegate(
(context, index) {
return Container(
color: index.isEven ? Colors.amber : Colors.blue,
child: Center(
child: Text('Item $index'),
),
);
},
childCount: 20,
),
gridDelegate: AsymmetricGridDelegate(),
)
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,
),
),
],
),
);
}
}
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();
}
}
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()));
Sliver를 활용한 비대칭 레이아웃과 복잡한 데이터 렌더링은 사용자 경험을 크게 향상시킬 수 있습니다. 이 글에서 다룬 기법을 바탕으로 독창적이고 성능 좋은 UI를 제작해 보세요. 다음 글에서는 Sliver와 애니메이션을 결합한 고급 패턴을 다룰 예정입니다.
Flutter와 Firebase 통합 가이드 (0) | 2025.01.01 |
---|---|
Sliver와 애니메이션을 결합한 고급 패턴 (0) | 2025.01.01 |
Sliver와 애니메이션을 심화적으로 활용하는 고급 사례 (0) | 2025.01.01 |
Sliver의 애니메이션 효과와 동적 데이터 적용 (1) | 2025.01.01 |
Flutter Sliver와 CustomScrollView 심화 가이드 (0) | 2025.01.01 |