Profile Preview
Loading preview
Starting the Flutter app
Public profile view with stats, posts grid, and follow buttons
import 'package:flutter/material.dart';
class ProfilePreview extends StatefulWidget {
const ProfilePreview({super.key});
@override
State<ProfilePreview> createState() => _ProfilePreviewState();
}
class _ProfilePreviewState extends State<ProfilePreview> {
var _following = false;
@override
Widget build(BuildContext context) {
final isDark = Theme.of(context).brightness == Brightness.dark;
return Scaffold(
body: CustomScrollView(
slivers: [
SliverAppBar(
expandedHeight: 280,
pinned: true,
backgroundColor: isDark ? Colors.grey[900] : Colors.blue[700],
flexibleSpace: FlexibleSpaceBar(
background: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Colors.blue[400]!, Colors.purple[400]!],
),
),
child: SafeArea(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Center(
child: FittedBox(
fit: BoxFit.scaleDown,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(height: 40),
Stack(
children: [
const CircleAvatar(
radius: 55,
backgroundColor: Colors.white24,
child: Icon(
Icons.person,
size: 55,
color: Colors.white,
),
),
Positioned(
bottom: 0,
right: 0,
child: Container(
padding: const EdgeInsets.all(6),
decoration: BoxDecoration(
color: Colors.green,
shape: BoxShape.circle,
border: Border.all(
color: Colors.white,
width: 2,
),
),
),
),
],
),
const SizedBox(height: 16),
const Text(
'Sarah Johnson',
style: TextStyle(
color: Colors.white,
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
Text(
'@sarahjohnson',
style: TextStyle(
color: Colors.white.withValues(alpha: 0.8),
fontSize: 14,
),
),
],
),
),
),
),
),
),
),
actions: [
IconButton(
tooltip: 'Share',
icon: const Icon(Icons.share, color: Colors.white),
onPressed: () => _showDemo(context, 'Share (demo)'),
),
IconButton(
tooltip: 'More options',
icon: const Icon(Icons.more_vert, color: Colors.white),
onPressed: () => _showDemo(context, 'More options (demo)'),
),
],
),
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: isDark ? Colors.grey[800] : Colors.grey[100],
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Flutter Developer & UI Designer',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 8),
Wrap(
spacing: 16,
runSpacing: 4,
children: [
Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.location_on,
size: 16,
color: isDark
? Colors.white54
: Colors.grey[600],
),
const SizedBox(width: 4),
Flexible(
child: Text(
'San Francisco, CA',
style: TextStyle(
color: isDark
? Colors.white54
: Colors.grey[600],
),
),
),
],
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.link,
size: 16,
color: isDark
? Colors.white54
: Colors.grey[600],
),
const SizedBox(width: 4),
const Flexible(
child: Text(
'sarah.dev',
style: TextStyle(color: Colors.blue),
),
),
],
),
],
),
],
),
),
const SizedBox(height: 16),
Row(
children: [
_buildStat('Posts', '142'),
_buildDivider(),
_buildStat('Followers', '12.5K'),
_buildDivider(),
_buildStat('Following', '892'),
],
),
const SizedBox(height: 16),
Row(
children: [
Expanded(
child: ElevatedButton(
onPressed: () =>
setState(() => _following = !_following),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(
vertical: 12,
horizontal: 8,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: Text(_following ? 'Following' : 'Follow'),
),
),
const SizedBox(width: 12),
Expanded(
child: OutlinedButton(
onPressed: () => _showDemo(context, 'Message (demo)'),
style: OutlinedButton.styleFrom(
padding: const EdgeInsets.symmetric(
vertical: 12,
horizontal: 8,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: const Text('Message'),
),
),
],
),
const SizedBox(height: 24),
_buildTabBar(isDark),
const SizedBox(height: 16),
_buildContentGrid(),
],
),
),
),
],
),
);
}
Widget _buildStat(String label, String value) {
return Expanded(
child: Column(
children: [
FittedBox(
fit: BoxFit.scaleDown,
child: Text(
value,
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
),
const SizedBox(height: 4),
FittedBox(
fit: BoxFit.scaleDown,
child: Text(
label,
style: TextStyle(color: Colors.grey[600], fontSize: 13),
),
),
],
),
);
}
Widget _buildDivider() {
return Container(height: 40, width: 1, color: Colors.grey[300]);
}
Widget _buildTabBar(bool isDark) {
return Row(
children: [
_buildTabItem('Posts', true),
_buildTabItem('Media', false),
_buildTabItem('Likes', false),
],
);
}
Widget _buildTabItem(String label, bool isSelected) {
return Expanded(
child: Container(
padding: const EdgeInsets.symmetric(vertical: 12),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: isSelected ? Colors.blue : Colors.transparent,
width: 2,
),
),
),
child: Text(
label,
textAlign: TextAlign.center,
style: TextStyle(
fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal,
color: isSelected ? Colors.blue : Colors.grey,
),
),
),
);
}
Widget _buildContentGrid() {
return GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
crossAxisSpacing: 4,
mainAxisSpacing: 4,
),
itemCount: 9,
itemBuilder: (context, index) {
return Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Colors.blue[(index % 3 + 1) * 200]!,
Colors.purple[(index % 3 + 1) * 200]!,
],
),
),
child: Center(
child: Icon(
Icons.image,
color: Colors.white.withValues(alpha: 0.5),
),
),
);
},
);
}
}
void _showDemo(BuildContext context, String message) {
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(SnackBar(content: Text(message)));
}
Add it to your project
Copies the Dart source into your app, so you own it. Flutter SDK only, no extra packages.
First time? Run dart pub global activate fluttertemplates_dev, or read the CLI guide.
How it works
CustomScrollView with a 280px SliverAppBar whose gradient background hosts the avatar, name and handle, inside a FittedBox that shrinks them to fit large text. The rest is one SliverToBoxAdapter. The location line is a Wrap, and stats share the row through Expanded. _buildTabItem only imitates tabs, so Posts stays selected. Follow toggles _following. _buildContentGrid shows nine gradient tiles in a shrinkWrap grid that builds every item at once.
Adapting it
- For long feeds, move the grid out to a
SliverGridso tiles load lazily. - Replace
_buildTabItemwithTabBar, or track the selected tab in state. - Take a user model, and theme the fixed greys in
_buildStatand_buildDivider.