Created
March 1, 2025 14:20
-
-
Save rubywai/dfd13c6b3ef03111960b8e976f4fb59e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; | |
import 'package:flutter/services.dart'; | |
void main() { | |
// Set preferred orientations and system UI | |
WidgetsFlutterBinding.ensureInitialized(); | |
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); | |
SystemChrome.setSystemUIOverlayStyle( | |
const SystemUiOverlayStyle( | |
statusBarColor: Colors.transparent, | |
statusBarIconBrightness: Brightness.light, | |
), | |
); | |
runApp(const MyApp()); | |
} | |
class MyApp extends StatelessWidget { | |
const MyApp({super.key}); | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
title: 'TikTok Style Image Viewer', | |
theme: ThemeData.dark(), | |
debugShowCheckedModeBanner: false, | |
home: const TikTokStyleFeed(), | |
); | |
} | |
} | |
class TikTokStyleFeed extends StatefulWidget { | |
const TikTokStyleFeed({super.key}); | |
@override | |
State<TikTokStyleFeed> createState() => _TikTokStyleFeedState(); | |
} | |
class _TikTokStyleFeedState extends State<TikTokStyleFeed> { | |
final PageController _pageController = PageController(); | |
@override | |
void dispose() { | |
_pageController.dispose(); | |
super.dispose(); | |
} | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
extendBodyBehindAppBar: true, | |
appBar: AppBar( | |
backgroundColor: Colors.transparent, | |
elevation: 0, | |
title: const Text( | |
'For You', | |
style: TextStyle( | |
fontWeight: FontWeight.bold, | |
fontSize: 18, | |
), | |
), | |
centerTitle: true, | |
actions: [ | |
IconButton( | |
icon: const Icon(Icons.search), | |
onPressed: () { | |
// Implement search functionality | |
}, | |
), | |
], | |
), | |
body: PageView.builder( | |
scrollDirection: Axis.vertical, | |
controller: _pageController, | |
itemCount: 100, | |
onPageChanged: (int page) { | |
setState(() {}); | |
}, | |
itemBuilder: (context, index) { | |
return TikTokStylePost( | |
imageUrl: 'https://picsum.photos/200/300?random=$index', | |
index: index, | |
); | |
}, | |
), | |
); | |
} | |
} | |
class TikTokStylePost extends StatelessWidget { | |
final String imageUrl; | |
final int index; | |
const TikTokStylePost({ | |
super.key, | |
required this.imageUrl, | |
required this.index, | |
}); | |
@override | |
Widget build(BuildContext context) { | |
return Stack( | |
fit: StackFit.expand, | |
children: [ | |
// Background image | |
Image.network( | |
key: UniqueKey(), | |
imageUrl, | |
fit: BoxFit.cover, | |
loadingBuilder: (context, child, loadingProgress) { | |
if (loadingProgress == null) { | |
return child; | |
} | |
return Container( | |
color: Colors.blue, | |
child: Center( | |
child: CircularProgressIndicator( | |
value: loadingProgress.expectedTotalBytes != null | |
? loadingProgress.cumulativeBytesLoaded / | |
(loadingProgress.expectedTotalBytes ?? 1) | |
: null, | |
color: Colors.white, | |
), | |
), | |
); | |
}, | |
errorBuilder: (context, error, stackTrace) { | |
return Container( | |
color: Colors.black, | |
child: const Center( | |
child: Column( | |
mainAxisAlignment: MainAxisAlignment.center, | |
children: [ | |
Icon(Icons.error_outline, color: Colors.red, size: 48), | |
SizedBox(height: 16), | |
Text( | |
'Could not load image', | |
style: TextStyle(color: Colors.white), | |
), | |
], | |
), | |
), | |
); | |
}, | |
), | |
// Dark gradient overlay for better text visibility | |
Container( | |
decoration: BoxDecoration( | |
gradient: LinearGradient( | |
begin: Alignment.topCenter, | |
end: Alignment.bottomCenter, | |
colors: [ | |
Colors.black.withValues(alpha: 0.3), | |
Colors.black.withValues(alpha: 0.0), | |
Colors.black.withValues(alpha: 0.0), | |
Colors.black.withValues(alpha: 0.7), | |
], | |
stops: const [0.0, 0.2, 0.8, 1.0], | |
), | |
), | |
), | |
// User info and caption at bottom | |
Positioned( | |
left: 16, | |
right: 96, | |
bottom: 80, | |
child: Column( | |
crossAxisAlignment: CrossAxisAlignment.start, | |
children: [ | |
Row( | |
children: [ | |
const Text( | |
'photographer', | |
style: TextStyle( | |
color: Colors.white, | |
fontWeight: FontWeight.bold, | |
fontSize: 16, | |
), | |
), | |
const SizedBox(width: 8), // FIXED TYPO | |
Container( | |
padding: | |
const EdgeInsets.symmetric(horizontal: 8, vertical: 2), | |
decoration: BoxDecoration( | |
border: Border.all(color: Colors.white), | |
borderRadius: BorderRadius.circular(10), | |
), | |
child: const Text( | |
'Follow', | |
style: TextStyle(color: Colors.white, fontSize: 12), | |
), | |
), | |
], | |
), | |
const SizedBox(height: 8), // FIXED TYPO | |
Text( | |
'Beautiful landscape photo #${index + 1}', | |
style: const TextStyle(color: Colors.white), | |
), | |
const SizedBox(height: 8), // FIXED TYPO | |
Row( | |
children: const [ | |
Icon(Icons.music_note, color: Colors.white, size: 16), | |
SizedBox(width: 4), | |
Text( | |
'original sound - photography', | |
style: TextStyle(color: Colors.white), | |
), | |
], | |
), | |
], | |
), | |
), | |
// Right-side interaction buttons | |
Positioned( | |
right: 16, | |
bottom: 100, | |
child: Column( | |
children: [ | |
_buildInteractionButton(Icons.favorite, '${(index + 1) * 234}K'), | |
_buildInteractionButton( | |
Icons.chat_bubble, '${(index + 1) * 12}K'), | |
_buildInteractionButton(Icons.bookmark, 'Save'), | |
_buildInteractionButton(Icons.share, 'Share'), | |
], | |
), | |
), | |
// Watermark | |
Positioned( | |
bottom: 20, | |
right: 20, | |
child: Text( | |
'© RubyLearner', | |
style: TextStyle( | |
color: Colors.white.withValues(alpha: 0.6), | |
fontSize: 12, | |
), | |
), | |
), | |
], | |
); | |
} | |
Widget _buildInteractionButton(IconData icon, String label) { | |
return Padding( | |
padding: const EdgeInsets.only(bottom: 16), | |
child: Column( | |
children: [ | |
Icon(icon, color: Colors.white, size: 32), | |
const SizedBox(height: 4), | |
Text( | |
label, | |
style: const TextStyle(color: Colors.white, fontSize: 12), | |
), | |
], | |
), | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment