Welcome to Day 8 of the "Hundred Days of Flutter" course! Today, we'll explore more advanced layout widgets that help us create scrollable and grid-based user interfaces. These widgets are essential for building modern mobile applications.
ListView is one of the most commonly used widgets in Flutter. It displays a scrollable list of widgets.
ListView(
children: [
ListTile(
leading: Icon(Icons.star),
title: Text('First Item'),
subtitle: Text('Subtitle'),
),
ListTile(
leading: Icon(Icons.star),
title: Text('Second Item'),
subtitle: Text('Subtitle'),
),
],
)
ListView.builder is more efficient for long lists as it only builds items that are visible:
ListView.builder(
itemCount: 100,
itemBuilder: (context, index) {
return ListTile(
leading: Icon(Icons.star),
title: Text('Item $index'),
subtitle: Text('Subtitle $index'),
);
},
)
ListView.separated adds separators between items:
ListView.separated(
itemCount: 10,
separatorBuilder: (context, index) => Divider(),
itemBuilder: (context, index) {
return ListTile(
title: Text('Item $index'),
);
},
)
GridView displays items in a grid layout.
GridView.count(
crossAxisCount: 2,
children: [
Container(color: Colors.red),
Container(color: Colors.blue),
Container(color: Colors.green),
Container(color: Colors.yellow),
],
)
GridView.builder is efficient for large grids:
GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
),
itemCount: 20,
itemBuilder: (context, index) {
return Container(
color: Colors.primaries[index % Colors.primaries.length],
child: Center(
child: Text('Item $index'),
),
);
},
)
GridView.custom(
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 150,
mainAxisSpacing: 10,
crossAxisSpacing: 10,
),
childrenDelegate: SliverChildBuilderDelegate(
(context, index) => Container(
color: Colors.primaries[index % Colors.primaries.length],
child: Center(
child: Text('Item $index'),
),
),
childCount: 20,
),
)
SingleChildScrollView allows scrolling of a single child widget that might be larger than the screen.
SingleChildScrollView(
child: Column(
children: [
Container(
height: 1000,
color: Colors.blue,
),
Container(
height: 1000,
color: Colors.red,
),
],
),
)
SingleChildScrollView(
padding: EdgeInsets.all(16),
child: Column(
children: [
Card(
child: Padding(
padding: EdgeInsets.all(16),
child: Text('First Card'),
),
),
SizedBox(height: 16),
Card(
child: Padding(
padding: EdgeInsets.all(16),
child: Text('Second Card'),
),
),
],
),
)
RefreshIndicator(
onRefresh: () async {
// Fetch new data
await Future.delayed(Duration(seconds: 1));
},
child: ListView.builder(
itemCount: 20,
itemBuilder: (context, index) {
return ListTile(
title: Text('Item $index'),
);
},
),
)
class InfiniteScrollListView extends StatefulWidget {
@override
_InfiniteScrollListViewState createState() => _InfiniteScrollListViewState();
}
class _InfiniteScrollListViewState extends State<InfiniteScrollListView> {
final List<String> items = [];
final ScrollController _scrollController = ScrollController();
bool _isLoading = false;
@override
void initState() {
super.initState();
_loadMoreItems();
_scrollController.addListener(() {
if (_scrollController.position.pixels >=
_scrollController.position.maxScrollExtent - 200) {
_loadMoreItems();
}
});
}
Future<void> _loadMoreItems() async {
if (_isLoading) return;
setState(() => _isLoading = true);
await Future.delayed(Duration(seconds: 1));
setState(() {
items.addAll(List.generate(10, (index) => 'Item ${items.length + index}'));
_isLoading = false;
});
}
@override
Widget build(BuildContext context) {
return ListView.builder(
controller: _scrollController,
itemCount: items.length + 1,
itemBuilder: (context, index) {
if (index == items.length) {
return Center(
child: CircularProgressIndicator(),
);
}
return ListTile(
title: Text(items[index]),
);
},
);
}
}
Let's test your understanding of today's concepts:
What is the main advantage of using ListView.builder over a regular ListView?
Which widget would you use to display items in a grid layout?
What is the purpose of SingleChildScrollView?
Create a photo gallery app that:
Here's a starting point:
class PhotoGallery extends StatefulWidget {
@override
_PhotoGalleryState createState() => _PhotoGalleryState();
}
class _PhotoGalleryState extends State<PhotoGallery> {
final List<String> photos = [];
final ScrollController _scrollController = ScrollController();
bool _isLoading = false;
bool _hasError = false;
@override
void initState() {
super.initState();
_loadPhotos();
_scrollController.addListener(_onScroll);
}
Future<void> _loadPhotos() async {
if (_isLoading) return;
setState(() => _isLoading = true);
try {
await Future.delayed(Duration(seconds: 1)); // Simulate network delay
setState(() {
photos.addAll(List.generate(10, (index) =>
'https://picsum.photos/200/200?random=${photos.length + index}'));
_isLoading = false;
});
} catch (e) {
setState(() {
_hasError = true;
_isLoading = false;
});
}
}
void _onScroll() {
if (_scrollController.position.pixels >=
_scrollController.position.maxScrollExtent - 200) {
_loadPhotos();
}
}
@override
Widget build(BuildContext context) {
return RefreshIndicator(
onRefresh: () async {
setState(() {
photos.clear();
_hasError = false;
});
await _loadPhotos();
},
child: _hasError
? Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Error loading photos'),
ElevatedButton(
onPressed: () {
setState(() => _hasError = false);
_loadPhotos();
},
child: Text('Retry'),
),
],
),
)
: GridView.builder(
controller: _scrollController,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
),
itemCount: photos.length + 1,
itemBuilder: (context, index) {
if (index == photos.length) {
return Center(
child: CircularProgressIndicator(),
);
}
return Image.network(
photos[index],
fit: BoxFit.cover,
);
},
),
);
}
}
Tomorrow, we'll explore StatefulWidgets and learn how to create interactive UI elements!
Master Flutter's layout widgets including Row, Column, Expanded, and Flexible to create responsive and well-structured user interfaces.
Start Previous DayLearn about StatefulWidgets, state management, and lifecycle methods in Flutter to create interactive and dynamic user interfaces.
Start Next Day