flutter_animated_skeleton 0.1.0
flutter_animated_skeleton: ^0.1.0 copied to clipboard
Impeller-optimized skeleton/shimmer loader with presets and themes.
import 'package:flutter/material.dart';
import 'package:flutter_animated_skeleton/flutter_animated_skeleton.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Animated Skeleton Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
darkTheme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.deepPurple,
brightness: Brightness.dark,
),
useMaterial3: true,
),
home: const DemoHomePage(),
);
}
}
class DemoHomePage extends StatefulWidget {
const DemoHomePage({super.key});
@override
State<DemoHomePage> createState() => _DemoHomePageState();
}
class _DemoHomePageState extends State<DemoHomePage> {
bool _showSkeletons = true;
bool _shimmerEnabled = true;
bool _isDarkTheme = false;
int _currentIndex = 0;
@override
Widget build(BuildContext context) {
return SkeletonTheme(
data: _isDarkTheme ? SkeletonThemeData.dark() : SkeletonThemeData.light(),
child: Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text('Flutter Animated Skeleton'),
actions: [
IconButton(
icon: Icon(_isDarkTheme ? Icons.light_mode : Icons.dark_mode),
onPressed: () {
setState(() {
_isDarkTheme = !_isDarkTheme;
});
},
),
],
),
body: Column(
children: [
// Controls
Container(
padding: const EdgeInsets.all(16),
child: Column(
children: [
Row(
children: [
const Text('Show Skeletons: '),
Switch(
value: _showSkeletons,
onChanged: (value) {
setState(() {
_showSkeletons = value;
});
},
),
],
),
Row(
children: [
const Text('Shimmer Effect: '),
Switch(
value: _shimmerEnabled,
onChanged: (value) {
setState(() {
_shimmerEnabled = value;
});
},
),
],
),
],
),
),
const Divider(),
// Content
Expanded(child: _buildCurrentScreen()),
],
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
onTap: (index) {
setState(() {
_currentIndex = index;
});
},
items: const [
BottomNavigationBarItem(icon: Icon(Icons.list), label: 'List'),
BottomNavigationBarItem(icon: Icon(Icons.grid_view), label: 'Grid'),
BottomNavigationBarItem(icon: Icon(Icons.person), label: 'Profile'),
],
),
),
);
}
Widget _buildCurrentScreen() {
switch (_currentIndex) {
case 0:
return _showSkeletons
? _buildSkeletonListContent()
: _buildRealListContent();
case 1:
return _showSkeletons
? _buildSkeletonGridContent()
: _buildRealGridContent();
case 2:
return _showSkeletons
? _buildSkeletonProfileContent()
: _buildRealProfileContent();
default:
return _showSkeletons
? _buildSkeletonListContent()
: _buildRealListContent();
}
}
Widget _buildSkeletonListContent() {
return SkeletonLoader(
enabled: true,
shimmer: _shimmerEnabled,
child: ListView.separated(
itemCount: 10,
separatorBuilder: (context, index) => const Divider(height: 1),
itemBuilder: (context, index) {
return SkeletonListTile(
hasLeading: index.isEven,
titleLines: index % 3 + 1,
subtitleLines: index.isOdd ? 1 : 0,
hasTrailing: index % 3 == 0,
);
},
),
);
}
Widget _buildSkeletonGridContent() {
return SkeletonLoader(
enabled: true,
shimmer: _shimmerEnabled,
child: SkeletonGrid.count(
crossAxisCount: 2,
itemCount: 20,
mainAxisSpacing: 8,
crossAxisSpacing: 8,
itemBuilder: (context, index) {
return const SkeletonCard(
hasImage: true,
titleLines: 2,
subtitleLines: 1,
actionCount: 0,
margin: EdgeInsets.zero,
);
},
),
);
}
Widget _buildSkeletonProfileContent() {
return SkeletonLoader(
enabled: true,
shimmer: _shimmerEnabled,
child: SingleChildScrollView(
child: Column(
children: [
const SkeletonProfileHeader(),
const SizedBox(height: 24),
// Profile stats section
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: List.generate(
3,
(index) => Column(
children: const [
LineSkeleton(width: 40, height: 20),
SizedBox(height: 8),
LineSkeleton(width: 60, height: 14),
],
),
),
),
),
const SizedBox(height: 24),
// Profile content cards
...List.generate(
5,
(index) => SkeletonCard(
hasImage: index.isEven,
titleLines: 1,
subtitleLines: 2,
actionCount: 1,
),
),
],
),
),
);
}
Widget _buildRealListContent() {
return ListView.separated(
itemCount: 10,
separatorBuilder: (context, index) => const Divider(height: 1),
itemBuilder: (context, index) {
return ListTile(
leading: index.isEven
? CircleAvatar(child: Text('${index + 1}'))
: null,
title: Text('List Item ${index + 1}'),
subtitle: index.isOdd ? Text('Subtitle for item ${index + 1}') : null,
trailing: index % 3 == 0 ? const Icon(Icons.more_vert) : null,
);
},
);
}
Widget _buildRealGridContent() {
return GridView.count(
crossAxisCount: 2,
mainAxisSpacing: 8,
crossAxisSpacing: 8,
children: List.generate(20, (index) {
return Card(
margin: EdgeInsets.zero,
child: Column(
children: [
Expanded(
child: Container(
color: Colors.deepPurple.withValues(alpha: 0.2),
child: const Center(child: Icon(Icons.image, size: 48)),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Item ${index + 1}',
style: const TextStyle(fontWeight: FontWeight.bold),
),
const SizedBox(height: 4),
Text('Description for item ${index + 1}'),
],
),
),
],
),
);
}),
);
}
Widget _buildRealProfileContent() {
return SingleChildScrollView(
child: Column(
children: [
// Real profile header
Container(
padding: const EdgeInsets.all(16),
child: const Row(
children: [
CircleAvatar(
radius: 28,
backgroundColor: Colors.deepPurple,
child: Text(
'JD',
style: TextStyle(color: Colors.white, fontSize: 20),
),
),
SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'John Doe',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 4),
Text(
'@johndoe',
style: TextStyle(fontSize: 16, color: Colors.grey),
),
],
),
),
],
),
),
const SizedBox(height: 16),
// Profile stats
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: const [
Column(
children: [
Text(
'1.2K',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 4),
Text('Posts', style: TextStyle(color: Colors.grey)),
],
),
Column(
children: [
Text(
'845',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 4),
Text('Followers', style: TextStyle(color: Colors.grey)),
],
),
Column(
children: [
Text(
'234',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 4),
Text('Following', style: TextStyle(color: Colors.grey)),
],
),
],
),
),
const SizedBox(height: 24),
// Profile posts
...List.generate(
5,
(index) => Card(
margin: const EdgeInsets.all(8),
child: Column(
children: [
if (index.isEven)
Container(
height: 200,
width: double.infinity,
color: Colors.deepPurple.withValues(alpha: 0.2),
child: const Center(child: Icon(Icons.image, size: 64)),
),
Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Post ${index + 1}',
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
Text(
'This is the content of post ${index + 1}. It contains some interesting information about various topics.',
),
const SizedBox(height: 12),
const Row(
children: [
Icon(Icons.favorite_border, size: 20),
SizedBox(width: 8),
Text('Like'),
SizedBox(width: 24),
Icon(Icons.comment_outlined, size: 20),
SizedBox(width: 8),
Text('Comment'),
],
),
],
),
),
],
),
),
),
],
),
);
}
}